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::path::PathBuf;
static CREDENTIALS_DIRECTORY: &str = "CREDENTIALS_DIRECTORY";
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[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")]
Empty { name: String, path: PathBuf },
#[error("Credential {name} in env variable is empty")]
NoEnv { name: String, e: VarError },
}
impl PartialEq for Error {
fn eq(&self, other: &Self) -> bool {
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::Read {
@ -28,22 +29,26 @@ impl PartialEq for Error {
name: nb, path: 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>;
pub trait CredentialDirectoryPath {
fn path(&self) -> std::result::Result<String, VarError>;
pub trait ReadEnv {
fn read_env(&self, name: &str) -> std::result::Result<String, VarError>;
}
#[derive(Default, Debug)]
pub struct EnvPath;
impl CredentialDirectoryPath for EnvPath {
fn path(&self) -> std::result::Result<String, VarError> {
std::env::var("CREDENTIALS_DIRECTORY")
impl ReadEnv for EnvPath {
fn read_env(&self, name: &str) -> std::result::Result<String, VarError> {
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
fn read_credential(&self, name: &str) -> Result<Option<String>>;
fn file_or_env(&self, file_name: &str, env_name: &str) -> Result<String>;
}
#[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>> {
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 {
tracing::warn!(
"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))
}
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>;
#[cfg(test)]
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::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 {
fn path(&self) -> Result<String, VarError> {
impl<'k, 'v> ReadEnv for MemPath<'k, 'v> {
fn read_env(&self, name: &str) -> Result<String, VarError> {
Ok(self
.0
.as_ref()
.get(name)
.ok_or_else(|| VarError::NotPresent)?
.to_str()
.unwrap()
.to_string())
}
}
#[test]
fn empty_var() {
let m = MemPath(None);
let m = MemPath(HashMap::new());
let res = ReadCredDir(m).read_credential("creds.txt");
assert_eq!(res, Ok(None));
}
#[test]
fn empty_dir() {
test_ext::with_dir(|dir| {
with_dir(|dir| {
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");
assert_eq!(
res,
@ -138,9 +158,12 @@ mod tests {
#[test]
fn empty_file() {
test_ext::with_dir(|dir| {
with_dir(|dir| {
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();
let res = ReadCredDir(m).read_credential("creds.txt");
@ -156,13 +179,50 @@ mod tests {
#[test]
fn file_with_content() {
test_ext::with_dir(|dir| {
with_dir(|dir| {
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();
let res = ReadCredDir(m).read_credential("creds.txt");
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()));
}
}