use crate::coordinate::Coordinate;
use crate::event::EventKind;
use crate::store::segment::CompactionOutcome;
use crate::store::{CompactionConfig, CompactionStrategy, Store, StoreConfig};
const KIND: EventKind = EventKind::custom(0xC, 0x01);
fn store_with_two_sealed_segments() -> (tempfile::TempDir, Store) {
let dir = tempfile::TempDir::new().expect("temp dir");
let config = StoreConfig::new(dir.path())
.with_segment_max_bytes(512)
.with_sync_every_n_events(1);
let store = Store::open(config).expect("open store");
let coord = |entity: &str| Coordinate::new(entity, "scope:compact-guard").expect("coord");
let big = serde_json::json!({ "blob": "x".repeat(2000) });
let _ = store
.append(&coord("entity:a"), KIND, &serde_json::json!({ "n": 1 }))
.expect("append a1");
let _ = store
.append(&coord("entity:a"), KIND, &big)
.expect("append filler 1");
let _ = store
.append(&coord("entity:b"), KIND, &serde_json::json!({ "n": 2 }))
.expect("append b1");
let _ = store
.append(&coord("entity:b"), KIND, &big)
.expect("append filler 2");
let _ = store
.append(&coord("entity:c"), KIND, &serde_json::json!({ "n": 3 }))
.expect("append anchor");
(dir, store)
}
fn deterministic_sealed_count() -> usize {
let (_dir, store) = store_with_two_sealed_segments();
let (_result, report) = store
.compact(&CompactionConfig {
strategy: CompactionStrategy::Merge,
min_segments: 1,
})
.expect("probe compact runs");
assert!(
matches!(report.outcome, CompactionOutcome::Performed),
"min_segments 1 must PERFORM whenever the fixture seals >= 1 segment"
);
report.sealed_segment_count
}
#[test]
fn compact_performs_at_the_threshold_and_tallies_the_reclaimed_sealed_segments() {
let n = deterministic_sealed_count();
assert!(
n >= 2,
"the fixture seals multiple segments (each oversized filler rotates)"
);
let (_dir, store) = store_with_two_sealed_segments();
let (result, report) = store
.compact(&CompactionConfig {
strategy: CompactionStrategy::Merge,
min_segments: n,
})
.expect("compact runs");
assert!(
matches!(result.outcome, CompactionOutcome::Performed),
"n sealed >= min_segments n must PERFORM; the `<`->`<=`/`==` guard mutants skip it"
);
assert_eq!(
report.sealed_segment_count, n,
"the sealed filter `*id < active` counts every sub-active segment"
);
assert_eq!(
report.segments_removed, n,
"the removal loop removes every sealed source; `segments_removed += 1` -> `-=`/`*=` miscounts"
);
assert!(
report.bytes_reclaimed > 0,
"the reclaim total sums the removed sealed sizes; `bytes_reclaimed += meta.len()` \
-> `*=` sticks it at zero (and `-=` underflows)"
);
assert_eq!(
report.segments_removed, result.segments_removed,
"the report mirrors the engine result's removed count"
);
assert_eq!(
report.bytes_reclaimed, result.bytes_reclaimed,
"the report mirrors the engine result's reclaimed bytes"
);
assert_eq!(report.outcome, CompactionOutcome::Performed);
}
#[test]
fn compact_skips_below_the_threshold_without_touching_the_reclaim_columns() {
let n = deterministic_sealed_count();
assert!(n >= 2, "the fixture seals multiple segments");
let (_dir, store) = store_with_two_sealed_segments();
let (result, report) = store
.compact(&CompactionConfig {
strategy: CompactionStrategy::Merge,
min_segments: n + 1,
})
.expect("compact runs");
assert!(
matches!(result.outcome, CompactionOutcome::Skipped),
"n sealed < min_segments n+1 must SKIP; the `<`->`>` guard mutant performs it"
);
assert_eq!(
report.sealed_segment_count, n,
"the skip report still counts the sealed sources it declined to merge"
);
assert_eq!(report.segments_removed, 0, "a skip removes no segments");
assert_eq!(report.bytes_reclaimed, 0, "a skip reclaims no bytes");
assert_eq!(report.outcome, CompactionOutcome::Skipped);
assert!(
report.merged_segment_id.is_none(),
"a skip never nominates a merged output segment"
);
}