Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion basis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ tokio.workspace = true
[dev-dependencies]
futures.workspace = true
mentra = { workspace = true, features = ["test-utils"] }
tokio = { workspace = true, features = ["time"] }
tokio = { workspace = true, features = ["time", "test-util"] }
tokio-tungstenite.workspace = true
tempfile.workspace = true
10 changes: 10 additions & 0 deletions basis/src/a2a/watch/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,13 @@ fn a_change_undone_before_the_next_poll_is_forgotten() {
"a later disappearance starts the count again"
);
}

#[test]
fn a_file_that_stays_missing_for_two_polls_is_read_as_a_deletion() {
let seen = present(10);
let mut settling = Settling::default();

assert!(!settling.observe(vec![Stamp::Missing], &seen));
assert!(settling.observe(vec![Stamp::Missing], &seen));
assert!(!settling.observe(vec![Stamp::Missing], &[Stamp::Missing]));
}
20 changes: 12 additions & 8 deletions basis/tests/a2a/watching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,20 +127,24 @@ async fn a_save_that_renames_the_file_away_and_writes_a_new_one_never_drops_an_a
.await
.expect("opens");
let interval = Duration::from_millis(100);
// Opening fetches an agent card over real IO. Pause only afterwards so
// a slow scheduler cannot stretch the missing-file window past a poll.
tokio::time::pause();
std::fs::rename(&file, dir.path().join(".basis/a2a.json~")).unwrap();
let _watch = workspace.watch_a2a(interval).expect("in a runtime");

for _ in 0..5 {
std::fs::rename(&file, dir.path().join(".basis/a2a.json~")).unwrap();
tokio::time::sleep(interval / 5).await;
assert!(!workspace.a2a_agents().is_empty(), "dropped mid-save");
write(&file, &a2a_file(&[("nous", &server.base)]));
tokio::time::sleep(interval / 2).await;
assert!(!workspace.a2a_agents().is_empty(), "dropped mid-save");
}
// The watch's immediate first tick sees the missing file; the next tick
// cannot run before we finish the save. Repeating short real-time saves
// could put successive polls in different missing windows, with no poll
// ever observing the file in between.
tokio::time::sleep(interval / 5).await;
assert_eq!(workspace.a2a_agents(), ["nous"], "dropped mid-save");
write(
&file,
&a2a_file(&[("nous", &server.base), ("scout", &server.base)]),
);
// Card refreshes use real IO, so restore the clock before the reload.
tokio::time::resume();
settles_on(&workspace, &["nous", "scout"]).await;
}

Expand Down
Loading