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( &self, _symbol: &S, logger: &SlogLogger, _force: bool, ) -> Result> { info!(logger, "run_symbol"); sleep(Duration::from_millis(0)).await; Ok(self.run) } } fn test( count: usize, body: fn(setup: Setup) -> (), ) -> Vec> { 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"); }