Files
galaxy/crates/galaxy_logging/src/native_tests.rs
T

252 lines
8.8 KiB
Rust

use std::io::Write as _;
use super::*;
#[test]
fn session_log_path_uses_the_galaxy_session_logs_directory() {
let path = session_log_path(Path::new("/Users/tester"), "session123", "20260822_091530");
assert_eq!(
path,
PathBuf::from("/Users/tester/.galaxy/session-logs/session123_20260822_091530.log")
);
}
#[test]
fn tee_writer_copies_each_record_to_both_destinations() {
let mut writer = TeeWriter {
primary: Vec::new(),
session: Vec::new(),
};
writer.write_all(b"first\n").unwrap();
writer.write_all(b"second\n").unwrap();
writer.flush().unwrap();
assert_eq!(writer.primary, b"first\nsecond\n");
assert_eq!(writer.session, b"first\nsecond\n");
}
fn touch(dir: &Path, name: &str) -> PathBuf {
let path = dir.join(name);
File::create(&path).unwrap();
path
}
#[test]
fn collects_active_in_session_and_old_logs_in_expected_order() {
let tmp = tempfile::tempdir().unwrap();
let active = touch(tmp.path(), "warp.log");
let in_session_0 = touch(tmp.path(), "warp.log.in_session.0");
let in_session_1 = touch(tmp.path(), "warp.log.in_session.1");
let in_session_2 = touch(tmp.path(), "warp.log.in_session.2");
let old_0 = touch(tmp.path(), "warp.log.old.0");
let old_1 = touch(tmp.path(), "warp.log.old.1");
let paths = collect_log_paths_in(tmp.path(), "warp.log").unwrap();
assert_eq!(
paths,
vec![
active,
in_session_0,
in_session_1,
in_session_2,
old_0,
old_1
]
);
}
#[test]
fn includes_in_session_logs_even_when_no_active_or_old_logs_exist() {
let tmp = tempfile::tempdir().unwrap();
let in_session_0 = touch(tmp.path(), "warp.log.in_session.0");
let paths = collect_log_paths_in(tmp.path(), "warp.log").unwrap();
assert_eq!(paths, vec![in_session_0]);
}
#[test]
fn ignores_unrelated_files_and_malformed_suffixes() {
let tmp = tempfile::tempdir().unwrap();
let active = touch(tmp.path(), "warp.log");
touch(tmp.path(), "warp.log.in_session.abc"); // not a number
touch(tmp.path(), "warp.log.in_session."); // empty suffix
touch(tmp.path(), "warp.log.old.xyz"); // not a number
touch(tmp.path(), "other.log"); // unrelated
touch(tmp.path(), "warp.log.old.temp"); // matches old. prefix but non-numeric
let paths = collect_log_paths_in(tmp.path(), "warp.log").unwrap();
assert_eq!(paths, vec![active]);
}
#[test]
fn errors_when_directory_is_empty() {
let tmp = tempfile::tempdir().unwrap();
let err = collect_log_paths_in(tmp.path(), "warp.log").unwrap_err();
assert!(err.to_string().contains("No warp logs were found"));
}
#[test]
fn respects_channel_specific_logfile_name() {
// Beta/preview channels use a different base name; make sure scanning
// is gated on that name and doesn't pick up the wrong channel's files.
let tmp = tempfile::tempdir().unwrap();
let active = touch(tmp.path(), "warp_preview.log");
let in_session_0 = touch(tmp.path(), "warp_preview.log.in_session.0");
touch(tmp.path(), "warp.log"); // different channel — must be ignored
touch(tmp.path(), "warp.log.in_session.0");
let paths = collect_log_paths_in(tmp.path(), "warp_preview.log").unwrap();
assert_eq!(paths, vec![active, in_session_0]);
}
#[test]
fn interleaves_old_logs_with_their_nested_in_session_chunks() {
// Previous sessions' mid-rotation chunks are nested under their
// parent .old.K slot; collection should output each .old.K
// immediately followed by its .old.K.in_session.M chunks before
// moving on to .old.{K+1}.
let tmp = tempfile::tempdir().unwrap();
let active = touch(tmp.path(), "warp.log");
let cur_in_session_0 = touch(tmp.path(), "warp.log.in_session.0");
let old_0 = touch(tmp.path(), "warp.log.old.0");
let old_0_chunk_0 = touch(tmp.path(), "warp.log.old.0.in_session.0");
let old_0_chunk_1 = touch(tmp.path(), "warp.log.old.0.in_session.1");
let old_1 = touch(tmp.path(), "warp.log.old.1");
let old_1_chunk_0 = touch(tmp.path(), "warp.log.old.1.in_session.0");
let paths = collect_log_paths_in(tmp.path(), "warp.log").unwrap();
assert_eq!(
paths,
vec![
active,
cur_in_session_0,
old_0,
old_0_chunk_0,
old_0_chunk_1,
old_1,
old_1_chunk_0,
]
);
}
#[test]
fn nested_chunks_surface_even_when_parent_old_slot_is_missing() {
// If a .old.K slot is missing from disk but its nested chunks
// remain (e.g. truncated by manual cleanup), the chunks should
// still be bundled rather than silently dropped.
let tmp = tempfile::tempdir().unwrap();
let active = touch(tmp.path(), "warp.log");
let orphan_chunk = touch(tmp.path(), "warp.log.old.3.in_session.0");
let paths = collect_log_paths_in(tmp.path(), "warp.log").unwrap();
assert_eq!(paths, vec![active, orphan_chunk]);
}
#[test]
fn migrate_previous_session_renames_in_session_to_old_0_in_session() {
// Mid-session chunks from the previous session belong with the
// .old.0 slot that holds that session's final-state log.
let tmp = tempfile::tempdir().unwrap();
touch(tmp.path(), "warp.log.in_session.0");
touch(tmp.path(), "warp.log.in_session.1");
touch(tmp.path(), "warp.log.in_session.2");
migrate_previous_session_in_session_chunks(tmp.path(), "warp.log");
assert!(tmp.path().join("warp.log.old.0.in_session.0").is_file());
assert!(tmp.path().join("warp.log.old.0.in_session.1").is_file());
assert!(tmp.path().join("warp.log.old.0.in_session.2").is_file());
// Bare .in_session.* slots are free for the new session.
assert!(!tmp.path().join("warp.log.in_session.0").exists());
assert!(!tmp.path().join("warp.log.in_session.1").exists());
assert!(!tmp.path().join("warp.log.in_session.2").exists());
}
#[test]
fn migrate_previous_session_is_a_noop_when_no_in_session_chunks_exist() {
let tmp = tempfile::tempdir().unwrap();
let unrelated = touch(tmp.path(), "warp.log");
migrate_previous_session_in_session_chunks(tmp.path(), "warp.log");
// Active log untouched; no spurious .old.0.in_session.* files.
assert!(unrelated.is_file());
let any_nested = fs::read_dir(tmp.path())
.unwrap()
.filter_map(Result::ok)
.any(|e| {
e.file_name()
.to_string_lossy()
.starts_with("warp.log.old.0.in_session.")
});
assert!(!any_nested);
}
#[test]
fn migrate_previous_session_ignores_malformed_in_session_filenames() {
let tmp = tempfile::tempdir().unwrap();
let real = touch(tmp.path(), "warp.log.in_session.0");
let bogus_a = touch(tmp.path(), "warp.log.in_session.abc");
let bogus_b = touch(tmp.path(), "warp.log.in_session.");
let unrelated = touch(tmp.path(), "warp.log.in_session.0.weird"); // not a usize
migrate_previous_session_in_session_chunks(tmp.path(), "warp.log");
assert!(!real.exists()); // moved
assert!(tmp.path().join("warp.log.old.0.in_session.0").is_file());
// Malformed entries are left where they are.
assert!(bogus_a.is_file());
assert!(bogus_b.is_file());
assert!(unrelated.is_file());
}
#[test]
fn shift_nested_chunks_renames_old_n_in_session_to_old_n_plus_1() {
let tmp = tempfile::tempdir().unwrap();
touch(tmp.path(), "warp.log.old.0.in_session.0");
touch(tmp.path(), "warp.log.old.0.in_session.1");
shift_old_session_in_session_chunks(tmp.path(), "warp.log", 0);
assert!(tmp.path().join("warp.log.old.1.in_session.0").is_file());
assert!(tmp.path().join("warp.log.old.1.in_session.1").is_file());
assert!(!tmp.path().join("warp.log.old.0.in_session.0").exists());
assert!(!tmp.path().join("warp.log.old.0.in_session.1").exists());
}
#[test]
fn shift_nested_chunks_only_touches_the_requested_slot() {
// Shifting slot 0 must not disturb slot 1's nested chunks.
let tmp = tempfile::tempdir().unwrap();
touch(tmp.path(), "warp.log.old.0.in_session.0");
let slot1_chunk = touch(tmp.path(), "warp.log.old.1.in_session.0");
shift_old_session_in_session_chunks(tmp.path(), "warp.log", 0);
assert!(tmp.path().join("warp.log.old.1.in_session.0").is_file());
assert_eq!(tmp.path().join("warp.log.old.1.in_session.0"), slot1_chunk);
}
#[test]
fn remove_nested_chunks_deletes_every_chunk_of_the_target_slot() {
let tmp = tempfile::tempdir().unwrap();
touch(tmp.path(), "warp.log.old.4.in_session.0");
touch(tmp.path(), "warp.log.old.4.in_session.1");
let survivor = touch(tmp.path(), "warp.log.old.3.in_session.0");
remove_old_session_in_session_chunks(tmp.path(), "warp.log", 4);
assert!(!tmp.path().join("warp.log.old.4.in_session.0").exists());
assert!(!tmp.path().join("warp.log.old.4.in_session.1").exists());
// Other slots' chunks are untouched.
assert!(survivor.is_file());
}