Odin tests: a later failed attempt, and a removal on its own

Two rules had no test of their own (Soul's mutants survived):
- only_oldest_attempt_tried: every fixture's file held the oldest failed
  attempt. The new test leaves the file holding a later one.
- remove_not_dirty: the heartbeat always dirtied the store, so a removal
  that did not mark it survived. The new test removes a record with nothing
  else changed and checks the write drops it (and that removing what is not
  held writes nothing).

Soul S-8 and the attempted-list gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-30 18:38:30 +02:00
commit e2c0c8f626

View file

@ -2197,6 +2197,56 @@ mod tests {
Ok(())
}
/// Two writes fail before replacing the file, and the file then comes to
/// hold the later attempt's records (the state a failed directory sync of
/// that attempt would leave). Any failed attempt may be what the file
/// holds, not only the first: the next write is made over it.
#[test]
fn the_file_may_hold_a_later_failed_attempt_not_only_the_first() -> Result<()> {
let temp = TempDir::new()?;
let path = temp.path().join("topology.cc");
let file = SingleFileMessagePackBackingStore::new(&path);
let store = MemoryOdinTopologyStore::load(&path)?;
std::fs::write(&path, b"not a cultcache store")?;
store.put(peer_document("doc-1")?);
assert!(store.flush().is_err());
store.put(peer_document("doc-2")?);
assert!(store.flush().is_err());
std::fs::remove_file(&path)?;
ensure!(file.compare_exchange_snapshot(
&[],
&[peer_document("doc-1")?, peer_document("doc-2")?]
)?);
store.put(peer_document("doc-3")?);
assert!(store.flush()?, "the later attempt is Odin's own");
assert_eq!(
file.pull_all_read_only_snapshot()?,
vec![peer_document("doc-1")?, peer_document("doc-2")?, peer_document("doc-3")?]
);
Ok(())
}
/// Removing a record is a change of its own: the next flush writes the
/// file without it, with nothing else changed.
#[test]
fn a_removed_record_is_written_out() -> Result<()> {
let temp = TempDir::new()?;
let path = temp.path().join("topology.cc");
let file = SingleFileMessagePackBackingStore::new(&path);
let store = MemoryOdinTopologyStore::load(&path)?;
store.put(peer_document("doc-1")?);
store.put(peer_document("doc-2")?);
assert!(store.flush()?);
store.remove("ghostlight.doc", "doc-1");
assert!(store.flush()?, "the removal is written");
assert_eq!(file.pull_all_read_only_snapshot()?, vec![peer_document("doc-2")?]);
store.remove("ghostlight.doc", "doc-1");
assert!(!store.flush()?, "removing what is not held changes nothing");
Ok(())
}
fn file_sequences(path: &Path) -> Result<(u64, Vec<u64>)> {
let entries = SingleFileMessagePackBackingStore::new(path).pull_all_read_only_snapshot()?;
let mark = unique_envelope(&entries, TOPOLOGY_PUBLISHER_WATERMARK_TYPE, "ghostlight")?