Add more tests

This commit is contained in:
Adrian Woźniak 2023-04-12 13:02:33 +02:00
parent 16ba3d8f33
commit 21bfe667ea

View File

@ -1,6 +1,8 @@
use std::env::VarError; use std::env::VarError;
use std::path::PathBuf; use std::path::PathBuf;
static CREDENTIALS_DIRECTORY: &str = "CREDENTIALS_DIRECTORY";
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("Failed to read credential {name} in directory {path:?}: {io}")] #[error("Failed to read credential {name} in directory {path:?}: {io}")]
@ -11,14 +13,13 @@ pub enum Error {
}, },
#[error("Credential {name} in directory {path:?} is empty")] #[error("Credential {name} in directory {path:?} is empty")]
Empty { name: String, path: PathBuf }, Empty { name: String, path: PathBuf },
#[error("Credential {name} in env variable is empty")]
NoEnv { name: String, e: VarError },
} }
impl PartialEq for Error { impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(Self::Read { .. }, Self::Empty { .. }) | (Self::Empty { .. }, Self::Read { .. }) => {
false
}
(Self::Empty { name: na, path: pa }, Self::Empty { name: nb, path: pb }) (Self::Empty { name: na, path: pa }, Self::Empty { name: nb, path: pb })
| ( | (
Self::Read { Self::Read {
@ -28,22 +29,26 @@ impl PartialEq for Error {
name: nb, path: pb, .. name: nb, path: pb, ..
}, },
) => na == nb && pa == pb, ) => na == nb && pa == pb,
(Self::NoEnv { name: na, e: ea }, Self::NoEnv { name: nb, e: eb }) => {
na == nb && ea == eb
}
_ => false,
} }
} }
} }
type Result<T> = std::result::Result<T, Error>; type Result<T> = std::result::Result<T, Error>;
pub trait CredentialDirectoryPath { pub trait ReadEnv {
fn path(&self) -> std::result::Result<String, VarError>; fn read_env(&self, name: &str) -> std::result::Result<String, VarError>;
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct EnvPath; pub struct EnvPath;
impl CredentialDirectoryPath for EnvPath { impl ReadEnv for EnvPath {
fn path(&self) -> std::result::Result<String, VarError> { fn read_env(&self, name: &str) -> std::result::Result<String, VarError> {
std::env::var("CREDENTIALS_DIRECTORY") std::env::var(name)
} }
} }
@ -54,14 +59,16 @@ pub trait ReadCredential {
/// ///
/// This function will return an error if reading the secret from the file fails /// This function will return an error if reading the secret from the file fails
fn read_credential(&self, name: &str) -> Result<Option<String>>; fn read_credential(&self, name: &str) -> Result<Option<String>>;
fn file_or_env(&self, file_name: &str, env_name: &str) -> Result<String>;
} }
#[derive(Debug)] #[derive(Debug)]
pub struct ReadCredDir<R: CredentialDirectoryPath = EnvPath>(R); pub struct ReadCredDir<R: ReadEnv = EnvPath>(R);
impl<R: CredentialDirectoryPath> ReadCredential for ReadCredDir<R> { impl<R: ReadEnv> ReadCredential for ReadCredDir<R> {
fn read_credential(&self, name: &str) -> Result<Option<String>> { fn read_credential(&self, name: &str) -> Result<Option<String>> {
let credentials_dir = self.0.path(); let credentials_dir = self.0.read_env(CREDENTIALS_DIRECTORY);
let Ok(creds_dir) = credentials_dir.map(PathBuf::from) else { let Ok(creds_dir) = credentials_dir.map(PathBuf::from) else {
tracing::warn!( tracing::warn!(
"CREDENTIALS_DIRECTORY is not set while looking for {} - not running under systemd?", "CREDENTIALS_DIRECTORY is not set while looking for {} - not running under systemd?",
@ -87,43 +94,56 @@ impl<R: CredentialDirectoryPath> ReadCredential for ReadCredDir<R> {
Ok(Some(pass)) Ok(Some(pass))
} }
fn file_or_env(&self, file_name: &str, env_name: &str) -> Result<String> {
match self.read_credential(file_name) {
Ok(Some(v)) => Ok(v),
Ok(None) => self.0.read_env(env_name).map_err(|e| Error::NoEnv {
name: env_name.into(),
e,
}),
Err(e) => Err(e),
}
}
} }
pub type ReadCredentialDir = ReadCredDir<EnvPath>; pub type ReadCredentialDir = ReadCredDir<EnvPath>;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{CredentialDirectoryPath, Error, ReadCredDir, ReadCredential}; use crate::{EnvPath, Error, ReadCredDir, ReadCredential, ReadEnv, CREDENTIALS_DIRECTORY};
use std::collections::HashMap;
use std::env::VarError; use std::env::VarError;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::PathBuf; use test_ext::with_dir;
struct MemPath(Option<PathBuf>); struct MemPath<'k, 'v>(HashMap<&'k str, &'v str>);
impl CredentialDirectoryPath for MemPath { impl<'k, 'v> ReadEnv for MemPath<'k, 'v> {
fn path(&self) -> Result<String, VarError> { fn read_env(&self, name: &str) -> Result<String, VarError> {
Ok(self Ok(self
.0 .0
.as_ref() .get(name)
.ok_or_else(|| VarError::NotPresent)? .ok_or_else(|| VarError::NotPresent)?
.to_str()
.unwrap()
.to_string()) .to_string())
} }
} }
#[test] #[test]
fn empty_var() { fn empty_var() {
let m = MemPath(None); let m = MemPath(HashMap::new());
let res = ReadCredDir(m).read_credential("creds.txt"); let res = ReadCredDir(m).read_credential("creds.txt");
assert_eq!(res, Ok(None)); assert_eq!(res, Ok(None));
} }
#[test] #[test]
fn empty_dir() { fn empty_dir() {
test_ext::with_dir(|dir| { with_dir(|dir| {
let p = dir.into_path(); let p = dir.into_path();
let m = MemPath(Some(p.clone())); let m = MemPath(HashMap::from([(
CREDENTIALS_DIRECTORY,
p.to_str().unwrap(),
)]));
let res = ReadCredDir(m).read_credential("creds.txt"); let res = ReadCredDir(m).read_credential("creds.txt");
assert_eq!( assert_eq!(
res, res,
@ -138,9 +158,12 @@ mod tests {
#[test] #[test]
fn empty_file() { fn empty_file() {
test_ext::with_dir(|dir| { with_dir(|dir| {
let p = dir.into_path(); let p = dir.into_path();
let m = MemPath(Some(p.clone())); let m = MemPath(HashMap::from([(
CREDENTIALS_DIRECTORY,
p.to_str().unwrap(),
)]));
std::fs::write(p.join("creds.txt"), "").unwrap(); std::fs::write(p.join("creds.txt"), "").unwrap();
let res = ReadCredDir(m).read_credential("creds.txt"); let res = ReadCredDir(m).read_credential("creds.txt");
@ -156,13 +179,50 @@ mod tests {
#[test] #[test]
fn file_with_content() { fn file_with_content() {
test_ext::with_dir(|dir| { with_dir(|dir| {
let p = dir.into_path(); let p = dir.into_path();
let m = MemPath(Some(p.clone())); let m = MemPath(HashMap::from([(
CREDENTIALS_DIRECTORY,
p.to_str().unwrap(),
)]));
std::fs::write(p.join("creds.txt"), "ah87shd8ashd87ashd87").unwrap(); std::fs::write(p.join("creds.txt"), "ah87shd8ashd87ashd87").unwrap();
let res = ReadCredDir(m).read_credential("creds.txt"); let res = ReadCredDir(m).read_credential("creds.txt");
assert_eq!(res, Ok(Some("ah87shd8ashd87ashd87".into()))); assert_eq!(res, Ok(Some("ah87shd8ashd87ashd87".into())));
}); });
} }
#[test]
fn no_file_and_env() {
let res = ReadCredDir(EnvPath).file_or_env("creds.txt", "NO_FILE_AND_ENV");
assert_eq!(
res,
Err(Error::NoEnv {
name: "NO_FILE_AND_ENV".to_string(),
e: VarError::NotPresent
})
);
}
#[test]
fn no_file_and_empty_env() {
std::env::set_var("NO_FILE_AND_EMPTY_ENV", "");
let res = ReadCredDir(EnvPath).file_or_env("creds.txt", "NO_FILE_AND_ENV");
assert_eq!(
res,
Err(Error::NoEnv {
name: "NO_FILE_AND_ENV".to_string(),
e: VarError::NotPresent
})
);
}
#[test]
fn read_from_env() {
std::env::set_var("READ_FROM_ENV", "read_from_env");
let res = ReadCredDir(EnvPath).file_or_env("creds.txt", "READ_FROM_ENV");
assert_eq!(res, Ok("read_from_env".into()));
}
} }