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

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. use async_trait::async_trait;
  2. use regex::Regex;
  3. use schematics::async_utils::{run, sleep};
  4. use schematics::loggers::{Entry, StoringLogger};
  5. use schematics::resources::{AcmeUser, Cert, Csr, GitCheckout};
  6. use schematics::symbols::Symbol;
  7. use schematics::Setup;
  8. use schematics::SymbolRunner;
  9. use slog::{info, Logger as SlogLogger};
  10. use std::error::Error;
  11. use std::fmt::Debug;
  12. use std::path::Path;
  13. use std::time::Duration;
  14. #[derive(Clone, Debug)]
  15. struct TestSymbolRunner {
  16. run: bool,
  17. }
  18. impl TestSymbolRunner {
  19. fn new(run: bool) -> Self {
  20. Self { run }
  21. }
  22. }
  23. #[async_trait(?Send)]
  24. impl SymbolRunner for TestSymbolRunner {
  25. async fn run_symbol<S: Symbol + Debug>(
  26. &self,
  27. _symbol: &S,
  28. logger: &SlogLogger,
  29. _force: bool,
  30. ) -> Result<bool, Box<dyn Error>> {
  31. info!(logger, "run_symbol");
  32. sleep(Duration::from_millis(0)).await;
  33. Ok(self.run)
  34. }
  35. }
  36. fn test(
  37. count: usize,
  38. body: fn(setup: Setup<TestSymbolRunner, StoringLogger>) -> (),
  39. ) -> Vec<Entry<String>> {
  40. let runner = TestSymbolRunner::new(false);
  41. let logger = StoringLogger::new();
  42. {
  43. let setup = Setup::new(runner, logger.clone());
  44. body(setup);
  45. }
  46. assert_eq!(logger.release(), vec![Entry(3, ".".repeat(count))]);
  47. let runner = TestSymbolRunner::new(true);
  48. let logger = StoringLogger::new();
  49. {
  50. let setup = Setup::new(runner, logger.clone());
  51. body(setup);
  52. }
  53. logger.release()
  54. }
  55. #[test]
  56. fn can_create_an_acme_user() {
  57. let mut result = test(1, |setup| {
  58. assert_eq!(&*(run(setup.add(AcmeUser)).unwrap().0).0, "acme");
  59. });
  60. let entry = result
  61. .pop()
  62. .expect("log is empty but should contain one entry");
  63. assert_eq!(result.len(), 0, "log has more than one entry");
  64. assert_eq!(entry.0, 3, "log entry has wrong level");
  65. let re =
  66. Regex::new(r"^resource: AcmeUser\n \w+ \d{1,2} \d{2}:\d{2}:\d{2}.\d{3} INFO run_symbol\n$")
  67. .unwrap();
  68. assert!(
  69. re.is_match(&entry.1),
  70. "log output {} does not match {}",
  71. entry.1,
  72. re
  73. );
  74. }
  75. #[test]
  76. fn runs_only_once() {
  77. let mut result = test(2, |setup| {
  78. run(async {
  79. assert_eq!(
  80. (setup.add(Csr("somehost")).await.unwrap().0)
  81. .as_ref()
  82. .to_str()
  83. .unwrap(),
  84. "/etc/ssl/local_certs/somehost.csr",
  85. );
  86. assert_eq!(
  87. (setup.add(Csr("somehost")).await.unwrap().0)
  88. .as_ref()
  89. .to_str()
  90. .unwrap(),
  91. "/etc/ssl/local_certs/somehost.csr",
  92. );
  93. });
  94. });
  95. let entry = result
  96. .pop()
  97. .expect("log is empty but should contain entries");
  98. assert_eq!(entry.0, 3, "log entry has wrong level");
  99. assert_eq!(entry.1, ".", "log entry has wrong content");
  100. let entry = result
  101. .pop()
  102. .expect("log is empty but should contain entries");
  103. assert_eq!(entry.0, 3, "log entry has wrong level");
  104. assert_eq!(entry.1.matches("run_symbol").count(), 7); // Key and CSR + 5 dirs
  105. assert_eq!(result.len(), 0, "log has more than one entry");
  106. }
  107. #[test]
  108. fn can_create_an_acme_cert() {
  109. let mut result = test(1, |setup| {
  110. assert_eq!(
  111. (run(setup.add(Cert("somehost"))).unwrap().0)
  112. .as_ref()
  113. .to_str()
  114. .unwrap(),
  115. "/etc/ssl/local_certs/somehost.crt",
  116. );
  117. });
  118. let entry = result
  119. .pop()
  120. .expect("log is empty but should contain one entry");
  121. assert_eq!(entry.0, 3, "log entry has wrong level");
  122. assert_eq!(entry.1.matches("run_symbol").count(), 19);
  123. assert_eq!(result.len(), 0, "log has more than one entry");
  124. }
  125. #[test]
  126. fn can_create_a_git_checkout() {
  127. let mut result = test(1, |setup| {
  128. run(setup.add(GitCheckout::new(
  129. "/tmp/somepath".as_ref() as &Path,
  130. "/tmp/some_src_repo",
  131. "master",
  132. )))
  133. .unwrap();
  134. });
  135. let entry = result
  136. .pop()
  137. .expect("log is empty but should contain one entry");
  138. assert_eq!(entry.0, 3, "log entry has wrong level");
  139. assert_eq!(entry.1.matches("run_symbol").count(), 3);
  140. assert_eq!(result.len(), 0, "log has more than one entry");
  141. }