myc

Self-custodial remote signer for Radroots apps
git clone https://radroots.dev/git/myc.git
Log | Files | Refs | README | LICENSE

logging_run.rs (3753B)


      1 use radroots_identity::RadrootsIdentity;
      2 use radroots_log::{LogFileLayout, LoggingOptions};
      3 use std::path::Path;
      4 use std::process::{Child, Command, Stdio};
      5 use std::thread;
      6 use std::time::{Duration, Instant};
      7 
      8 fn write_test_identity(path: &Path, secret_key: &str) {
      9     let identity = RadrootsIdentity::from_secret_key_str(secret_key).expect("identity from secret");
     10     myc::identity_files::store_encrypted_identity(path, &identity).expect("write identity");
     11 }
     12 
     13 fn wait_for_log_contents(
     14     path: &Path,
     15     timeout: Duration,
     16     expected_substrings: &[&str],
     17 ) -> Result<String, String> {
     18     let deadline = Instant::now() + timeout;
     19     while Instant::now() < deadline {
     20         match std::fs::read_to_string(path) {
     21             Ok(contents)
     22                 if !contents.trim().is_empty()
     23                     && expected_substrings
     24                         .iter()
     25                         .all(|substring| contents.contains(substring)) =>
     26             {
     27                 return Ok(contents);
     28             }
     29             Ok(_) => {}
     30             Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
     31             Err(error) => return Err(format!("failed to read log file: {error}")),
     32         }
     33         thread::sleep(Duration::from_millis(100));
     34     }
     35     Err(format!(
     36         "timed out waiting for non-empty log file at {}",
     37         path.display()
     38     ))
     39 }
     40 
     41 fn kill_child(child: &mut Child) {
     42     let _ = child.kill();
     43     let _ = child.wait();
     44 }
     45 
     46 #[test]
     47 fn myc_run_writes_non_empty_dated_log_file() {
     48     let temp = tempfile::tempdir().expect("tempdir");
     49     let state_dir = temp.path().join("state");
     50     let logs_dir = temp.path().join("logs");
     51     let signer_path = temp.path().join("signer.json");
     52     let user_path = temp.path().join("user.json");
     53     let env_path = temp.path().join("myc.env");
     54 
     55     write_test_identity(
     56         signer_path.as_path(),
     57         "1111111111111111111111111111111111111111111111111111111111111111",
     58     );
     59     write_test_identity(
     60         user_path.as_path(),
     61         "2222222222222222222222222222222222222222222222222222222222222222",
     62     );
     63 
     64     std::fs::write(
     65         &env_path,
     66         format!(
     67             "MYC_SERVICE_INSTANCE_NAME=myc-test\n\
     68 MYC_LOGGING_FILTER=info,myc=info\n\
     69 MYC_LOGGING_OUTPUT_DIR={}\n\
     70 MYC_LOGGING_STDOUT=false\n\
     71 MYC_PATHS_STATE_DIR={}\n\
     72 MYC_IDENTITY_SIGNER_PATH={}\n\
     73 MYC_IDENTITY_USER_PATH={}\n\
     74 MYC_DISCOVERY_ENABLED=false\n\
     75 MYC_TRANSPORT_ENABLED=false\n\
     76 MYC_TRANSPORT_CONNECT_TIMEOUT_SECS=10\n",
     77             logs_dir.display(),
     78             state_dir.display(),
     79             signer_path.display(),
     80             user_path.display(),
     81         ),
     82     )
     83     .expect("write env");
     84 
     85     let expected_log_path = LoggingOptions {
     86         dir: Some(logs_dir.clone()),
     87         file_name: "log".to_owned(),
     88         stdout: false,
     89         default_level: Some("info,myc=info".to_owned()),
     90         file_layout: LogFileLayout::DatedFileName,
     91     }
     92     .resolved_current_log_file_path()
     93     .expect("resolved current log path");
     94 
     95     let mut child = Command::new(env!("CARGO_BIN_EXE_myc"))
     96         .arg("--env-file")
     97         .arg(&env_path)
     98         .arg("run")
     99         .stdout(Stdio::null())
    100         .stderr(Stdio::piped())
    101         .spawn()
    102         .expect("spawn myc");
    103 
    104     let contents = match wait_for_log_contents(
    105         expected_log_path.as_path(),
    106         Duration::from_secs(5),
    107         &["logging initialized", "myc runtime bootstrapped"],
    108     ) {
    109         Ok(contents) => contents,
    110         Err(error) => {
    111             kill_child(&mut child);
    112             panic!("{error}");
    113         }
    114     };
    115 
    116     kill_child(&mut child);
    117 
    118     assert!(expected_log_path.exists());
    119     assert!(contents.contains("logging initialized"));
    120     assert!(contents.contains("myc runtime bootstrapped"));
    121 }