#![cfg(not(feature = "operator"))]
use fathomdb::{Engine, InitialState, PreparedWrite, SourceId};
fn temp_db() -> (tempfile::TempDir, std::path::PathBuf) {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("sdk-only-erasure.sqlite");
(dir, path)
}
fn anonymous_node(body: &str, source_id: &str) -> PreparedWrite {
PreparedWrite::Node {
kind: "doc".to_string(),
body: body.to_string(),
source_id: SourceId::new(source_id).expect("valid source id"),
logical_id: None,
state: InitialState::Active,
reason: None,
valid_from: None,
valid_until: None,
}
}
#[test]
fn sdk_only_consumer_erases_without_cli() {
let (_dir, path) = temp_db();
let opened = Engine::open(path.to_str().expect("utf-8 path")).expect("open");
let engine = opened.engine;
engine
.write(&[
anonymous_node("erasable alpha payload", "tenant-a"),
anonymous_node("erasable beta payload", "tenant-a"),
anonymous_node("retained gamma payload", "tenant-b"),
])
.expect("write");
let report = engine.erase_source("tenant-a").expect("erase_source");
assert_eq!(report.source_ref, "tenant-a", "the report must name the source it erased");
assert_eq!(report.nodes_excised, 2, "both `tenant-a` rows must be erased, and ONLY those");
let second = engine.erase_source("tenant-b").expect("erase_source");
assert_eq!(second.nodes_excised, 1, "the first erasure must not have touched the other source");
let again = engine.erase_source("tenant-a").expect("erase_source is idempotent");
assert_eq!(again.nodes_excised, 0);
engine.close().expect("close");
}
#[test]
fn sdk_only_erasure_is_durable_across_reopen() {
let (_dir, path) = temp_db();
let path_str = path.to_str().expect("utf-8 path").to_string();
let engine = Engine::open(&path_str).expect("open").engine;
engine
.write(&[
anonymous_node("durable erasable payload", "tenant-a"),
anonymous_node("durable retained payload", "tenant-b"),
])
.expect("write");
engine.erase_source("tenant-a").expect("erase_source");
engine.close().expect("close");
let reopened = Engine::open(&path_str).expect("reopen").engine;
let after = reopened.erase_source("tenant-a").expect("erase_source");
assert_eq!(
after.nodes_excised, 0,
"the erasure must be durable across a re-open, not an in-memory effect"
);
let other = reopened.erase_source("tenant-b").expect("erase_source");
assert_eq!(other.nodes_excised, 1, "the unrelated source must have survived the close/re-open");
reopened.close().expect("close");
}
#[test]
fn sdk_erase_source_rejects_empty_and_reserved_ids() {
let (_dir, path) = temp_db();
let engine = Engine::open(path.to_str().expect("utf-8 path")).expect("open").engine;
for reserved in ["", " ", "_engine:coverage", "_legacy:pre-0.8.20"] {
assert!(
engine.erase_source(reserved).is_err(),
"erase_source must reject the reserved/empty source id {reserved:?}"
);
}
engine.close().expect("close");
}