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 }