use super::tests::crud::{add, added_importance, added_status, supersede_with_importance};
use crate::memory::NewMemory;
use crate::memory::lifecycle::{MemoryImportance, MemoryStatus, MemoryType};
use crate::memory::store::MemoryStore;
use crate::memory_types::IngestPolicy;
fn supersede_with_status(
store: &mut MemoryStore,
project: &str,
content: &str,
old_id: &str,
importance: MemoryImportance,
status: MemoryStatus,
) -> String {
store
.supersede_with(&crate::memory::NewSupersedingMemory {
row: NewMemory {
project_id: project,
content,
metadata: None,
memory_type: MemoryType::Fact,
status,
},
old_id,
importance,
})
.expect("supersede must succeed")
}
pub(super) fn all_statuses(store: &MemoryStore, project: &str) -> Vec<crate::sqlite::Memory> {
store
.list(
project,
20,
None,
Some(["active", "candidate", "superseded", "deprecated"].as_slice()),
)
.expect("list")
}
#[test]
fn add_persists_the_importance_it_was_given() {
let (_dir, mut store) = MemoryStore::test_store();
let cases = [
(None, "medium"),
(Some(MemoryImportance::Medium), "medium"),
(Some(MemoryImportance::Low), "low"),
(Some(MemoryImportance::High), "high"),
(Some(MemoryImportance::Critical), "critical"),
];
for (i, (level, expected)) in cases.iter().enumerate() {
let id = add(
&mut store,
"imp-proj",
&format!("importance probe {i}"),
*level,
);
assert_eq!(
added_importance(&store, &id, "imp-proj"),
*expected,
"the level the operator named must reach the column, not its DEFAULT"
);
}
}
#[test]
fn add_superseding_persists_the_status_on_the_new_row() {
let (_dir, mut store) = MemoryStore::test_store();
let old_id = add(&mut store, "status-proj", "original fact", None);
let candidate_id = supersede_with_status(
&mut store,
"status-proj",
"candidate replacement",
&old_id,
MemoryImportance::High,
MemoryStatus::Candidate,
);
assert_eq!(
added_status(&store, &candidate_id, "status-proj"),
MemoryStatus::Candidate.as_str(),
"the status the caller named must reach the superseding row"
);
assert_eq!(
added_importance(&store, &candidate_id, "status-proj"),
"high",
"the level still travels with it"
);
let replaced = store
.get(&old_id, "status-proj")
.expect("get")
.expect("superseded row readable");
assert_eq!(replaced.status, "superseded");
assert_eq!(
replaced.superseded_by.as_deref(),
Some(candidate_id.as_str())
);
}
#[test]
fn add_superseding_wrappers_bind_the_lifecycle_defaults_by_name() {
let (_dir, mut store) = MemoryStore::test_store();
let old_id = add(&mut store, "sup-defaults", "original fact", None);
let plain_id = store
.supersede(
"sup-defaults",
"active replacement",
None,
MemoryType::Fact,
&old_id,
)
.unwrap();
assert_eq!(added_status(&store, &plain_id, "sup-defaults"), "active");
assert_eq!(
added_importance(&store, &plain_id, "sup-defaults"),
"medium",
"the level-less wrapper binds the default level by name"
);
}
#[test]
fn superseding_candidate_stays_out_of_the_default_reads() {
let (_dir, mut store) = MemoryStore::test_store();
let old_id = add(&mut store, "status-visible", "original fact", None);
let control_id = add(&mut store, "status-visible", "unrelated active fact", None);
let candidate_id = supersede_with_status(
&mut store,
"status-visible",
"candidate replacement",
&old_id,
MemoryImportance::Medium,
MemoryStatus::Candidate,
);
let default_list = store.list("status-visible", 20, None, None).expect("list");
assert!(
default_list.iter().all(|m| m.id != candidate_id),
"default list stays active-only"
);
assert!(
default_list.iter().any(|m| m.id == control_id),
"the read path itself works: an ordinary active row is listed"
);
assert!(
store
.list("status-visible", 20, None, Some(["candidate"].as_slice()))
.expect("list")
.iter()
.any(|m| m.id == candidate_id),
"an explicit candidate filter finds the hidden replacement"
);
}
#[test]
fn add_superseding_refuses_a_status_that_is_not_insertable() {
let (_dir, mut store) = MemoryStore::test_store();
let old_id = add(&mut store, "status-bad", "original fact", None);
for status in [MemoryStatus::Superseded, MemoryStatus::Deprecated] {
let err = store
.supersede_with(&crate::memory::NewSupersedingMemory {
row: NewMemory {
project_id: "status-bad",
content: "bad replacement",
metadata: None,
memory_type: MemoryType::Fact,
status,
},
old_id: &old_id,
importance: MemoryImportance::Medium,
})
.expect_err("only active/candidate are insertable");
assert!(
err.to_string()
.contains("is not valid for new memory insertion"),
"unexpected error for {status:?}: {err}"
);
}
assert_eq!(added_status(&store, &old_id, "status-bad"), "active");
assert_eq!(
all_statuses(&store, "status-bad").len(),
1,
"a refused supersede must insert no replacement row"
);
}
#[test]
fn add_superseding_persists_the_importance_on_the_new_row() {
let (_dir, mut store) = MemoryStore::test_store();
let old_id = add(&mut store, "sup-proj", "original fact", None);
let new_id = supersede_with_importance(
&mut store,
"sup-proj",
"replacement fact",
&old_id,
MemoryImportance::Critical,
);
assert_eq!(
added_importance(&store, &new_id, "sup-proj"),
"critical",
"the superseding row must carry the level the operator asked for"
);
assert_eq!(
added_status(&store, &new_id, "sup-proj"),
MemoryStatus::Active.as_str(),
"the released wrapper's own semantics: the new row is active"
);
assert_eq!(added_status(&store, &old_id, "sup-proj"), "superseded");
assert_eq!(added_importance(&store, &old_id, "sup-proj"), "medium");
let plain_id = store
.supersede("sup-proj", "third fact", None, MemoryType::Fact, &new_id)
.unwrap();
assert_eq!(added_importance(&store, &plain_id, "sup-proj"), "medium");
}
#[test]
fn every_row_creating_entry_point_refuses_a_non_insertable_status() {
let (_dir, mut store) = MemoryStore::test_store();
let old_id = add(&mut store, "guard-proj", "original fact", None);
for status in [MemoryStatus::Superseded, MemoryStatus::Deprecated] {
let err = store
.supersede_with(&crate::memory::NewSupersedingMemory {
row: NewMemory {
project_id: "guard-proj",
content: "guard replacement",
metadata: None,
memory_type: MemoryType::Fact,
status,
},
old_id: &old_id,
importance: MemoryImportance::Medium,
})
.expect_err("a superseding row cannot be born superseded/deprecated");
assert!(
err.to_string()
.contains("is not valid for new memory insertion"),
"supersede_with accepted {status:?}: {err}"
);
let err = store
.ingest_with_importance(
&NewMemory {
project_id: "guard-proj",
content: "guard add",
metadata: None,
memory_type: MemoryType::Fact,
status,
},
IngestPolicy::Force,
MemoryImportance::Medium,
)
.expect_err("an added row cannot be born superseded/deprecated");
assert!(
err.to_string()
.contains("is not valid for new memory insertion"),
"ingest_with_importance accepted {status:?}: {err}"
);
let err = store
.add_with_conflict(
"guard-proj",
"guard add",
None,
true,
MemoryType::Fact,
status,
)
.expect_err("an added row cannot be born superseded/deprecated");
assert!(
err.to_string()
.contains("is not valid for new memory insertion"),
"add_with_conflict accepted {status:?}: {err}"
);
}
assert_eq!(added_status(&store, &old_id, "guard-proj"), "active");
assert_eq!(
all_statuses(&store, "guard-proj").len(),
1,
"a refused row creation must insert nothing"
);
}