A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

150 lines
3.9 KiB

use async_trait::async_trait;
use regex::Regex;
use schematics::async_utils::{run, sleep};
use schematics::loggers::{Entry, StoringLogger};
use schematics::resources::{AcmeUser, Cert, Csr, GitCheckout};
use schematics::symbols::Symbol;
use schematics::Setup;
use schematics::SymbolRunner;
use slog::{info, Logger as SlogLogger};
use std::error::Error;
use std::fmt::Debug;
use std::path::Path;
use std::time::Duration;
#[derive(Clone, Debug)]
struct TestSymbolRunner {
run: bool,
}
impl TestSymbolRunner {
fn new(run: bool) -> Self {
Self { run }
}
}
#[async_trait(?Send)]
impl SymbolRunner for TestSymbolRunner {
async fn run_symbol<S: Symbol + Debug>(
&self,
_symbol: &S,
logger: &SlogLogger,
_force: bool,
) -> Result<bool, Box<dyn Error>> {
info!(logger, "run_symbol");
sleep(Duration::from_millis(0)).await;
Ok(self.run)
}
}
fn test(
count: usize,
body: fn(setup: Setup<TestSymbolRunner, StoringLogger>) -> (),
) -> Vec<Entry<String>> {
let runner = TestSymbolRunner::new(false);
let logger = StoringLogger::new();
{
let setup = Setup::new(runner, logger.clone());
body(setup);
}
assert_eq!(logger.release(), vec![Entry(3, ".".repeat(count))]);
let runner = TestSymbolRunner::new(true);
let logger = StoringLogger::new();
{
let setup = Setup::new(runner, logger.clone());
body(setup);
}
logger.release()
}
#[test]
fn can_create_an_acme_user() {
let mut result = test(1, |setup| {
assert_eq!(&*(run(setup.add(AcmeUser)).unwrap().0).0, "acme");
});
let entry = result
.pop()
.expect("log is empty but should contain one entry");
assert_eq!(result.len(), 0, "log has more than one entry");
assert_eq!(entry.0, 3, "log entry has wrong level");
let re =
Regex::new(r"^resource: AcmeUser\n \w+ \d{1,2} \d{2}:\d{2}:\d{2}.\d{3} INFO run_symbol\n$")
.unwrap();
assert!(
re.is_match(&entry.1),
"log output {} does not match {}",
entry.1,
re
);
}
#[test]
fn runs_only_once() {
let mut result = test(2, |setup| {
run(async {
assert_eq!(
(setup.add(Csr("somehost")).await.unwrap().0)
.as_ref()
.to_str()
.unwrap(),
"/etc/ssl/local_certs/somehost.csr",
);
assert_eq!(
(setup.add(Csr("somehost")).await.unwrap().0)
.as_ref()
.to_str()
.unwrap(),
"/etc/ssl/local_certs/somehost.csr",
);
});
});
let entry = result
.pop()
.expect("log is empty but should contain entries");
assert_eq!(entry.0, 3, "log entry has wrong level");
assert_eq!(entry.1, ".", "log entry has wrong content");
let entry = result
.pop()
.expect("log is empty but should contain entries");
assert_eq!(entry.0, 3, "log entry has wrong level");
assert_eq!(entry.1.matches("run_symbol").count(), 7); // Key and CSR + 5 dirs
assert_eq!(result.len(), 0, "log has more than one entry");
}
#[test]
fn can_create_an_acme_cert() {
let mut result = test(1, |setup| {
assert_eq!(
(run(setup.add(Cert("somehost"))).unwrap().0)
.as_ref()
.to_str()
.unwrap(),
"/etc/ssl/local_certs/somehost.crt",
);
});
let entry = result
.pop()
.expect("log is empty but should contain one entry");
assert_eq!(entry.0, 3, "log entry has wrong level");
assert_eq!(entry.1.matches("run_symbol").count(), 19);
assert_eq!(result.len(), 0, "log has more than one entry");
}
#[test]
fn can_create_a_git_checkout() {
let mut result = test(1, |setup| {
run(setup.add(GitCheckout::new(
"/tmp/somepath".as_ref() as &Path,
"/tmp/some_src_repo",
"master",
)))
.unwrap();
});
let entry = result
.pop()
.expect("log is empty but should contain one entry");
assert_eq!(entry.0, 3, "log entry has wrong level");
assert_eq!(entry.1.matches("run_symbol").count(), 3);
assert_eq!(result.len(), 0, "log has more than one entry");
}