252 lines
8.8 KiB
Rust
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());
|
|
}
|