use super::handlers::handle_add;
use crate::memory::MemoryStore;
use std::process::ExitCode;
fn add(
store: &mut MemoryStore,
project: &str,
text: &str,
importance: &str,
supersedes: Option<&str>,
) -> ExitCode {
add_with_status(store, project, text, "active", importance, supersedes)
}
fn add_with_status(
store: &mut MemoryStore,
project: &str,
text: &str,
status: &str,
importance: &str,
supersedes: Option<&str>,
) -> ExitCode {
handle_add(
store,
project,
text,
None,
supersedes.is_none(),
"fact",
status,
importance,
supersedes,
false,
)
.expect("handle_add ok")
}
fn stored(store: &MemoryStore, project: &str, content: &str) -> crate::sqlite::Memory {
store
.list(project, 10, None, None)
.expect("list")
.into_iter()
.find(|m| m.content == content)
.unwrap_or_else(|| panic!("no active row with content {content:?} was stored"))
}
fn supersede_pair(
store: &MemoryStore,
project: &str,
old_id: &str,
) -> (crate::sqlite::Memory, crate::sqlite::Memory) {
let replaced = store
.get(old_id, project)
.expect("get")
.expect("superseded row readable");
let replacement_id = replaced
.superseded_by
.as_deref()
.unwrap_or_else(|| panic!("row {old_id} carries no superseded_by"));
let replacement = store
.get(replacement_id, project)
.expect("get")
.expect("replacement row readable");
(replacement, replaced)
}
#[test]
fn handle_add_writes_the_importance_the_operator_named() {
let (_dir, mut store) = MemoryStore::test_store();
for (i, level) in ["critical", "high", "low", "medium"].iter().enumerate() {
let text = format!("importance probe {i}");
assert_eq!(
add(&mut store, "imp", &text, level, None),
ExitCode::SUCCESS
);
assert_eq!(
stored(&store, "imp", &text).importance,
*level,
"the level named on --importance must reach the column, not its DEFAULT"
);
}
}
#[test]
fn handle_add_superseding_writes_the_importance_on_the_new_row() {
let (_dir, mut store) = MemoryStore::test_store();
assert_eq!(
add(&mut store, "sup", "original fact", "medium", None),
ExitCode::SUCCESS
);
let old = stored(&store, "sup", "original fact");
assert_eq!(
add(
&mut store,
"sup",
"replacement fact",
"critical",
Some(&old.id),
),
ExitCode::SUCCESS
);
let fresh = stored(&store, "sup", "replacement fact");
assert_eq!(
fresh.importance, "critical",
"the superseding row must carry the level the operator asked for"
);
let (replacement, replaced) = supersede_pair(&store, "sup", &old.id);
assert_eq!(replacement.id, fresh.id);
assert_eq!(replaced.content, "original fact");
assert_eq!(replaced.status, "superseded");
assert_eq!(replaced.importance, "medium");
assert_eq!(replaced.superseded_by.as_deref(), Some(fresh.id.as_str()));
}
#[test]
fn handle_add_superseding_writes_the_status_the_operator_named() {
let (_dir, mut store) = MemoryStore::test_store();
assert_eq!(
add(&mut store, "st", "original fact", "medium", None),
ExitCode::SUCCESS
);
let old = stored(&store, "st", "original fact");
assert_eq!(
add_with_status(
&mut store,
"st",
"replacement fact",
"candidate",
"medium",
Some(&old.id),
),
ExitCode::SUCCESS
);
let (replacement, replaced) = supersede_pair(&store, "st", &old.id);
assert_eq!(
replacement.status, "candidate",
"the status named on --status must reach the superseding row, not 'active'"
);
assert_eq!(replacement.content, "replacement fact");
assert_eq!(replaced.status, "superseded");
assert_eq!(
replaced.superseded_by.as_deref(),
Some(replacement.id.as_str())
);
assert!(
store
.list("st", 20, None, None)
.expect("list")
.iter()
.all(|m| m.content != "replacement fact"),
"a candidate replacement must stay out of the active-only default list"
);
}
#[test]
fn handle_add_superseding_defaults_the_new_row_to_active() {
let (_dir, mut store) = MemoryStore::test_store();
assert_eq!(
add(&mut store, "sup-active", "original fact", "medium", None),
ExitCode::SUCCESS
);
let old = stored(&store, "sup-active", "original fact");
assert_eq!(
add(
&mut store,
"sup-active",
"replacement fact",
"medium",
Some(&old.id),
),
ExitCode::SUCCESS
);
let (replacement, replaced) = supersede_pair(&store, "sup-active", &old.id);
assert_eq!(replacement.status, "active");
assert_eq!(replaced.status, "superseded");
assert!(
store
.list("sup-active", 20, None, None)
.expect("list")
.iter()
.any(|m| m.id == replacement.id),
"the explicit-active case must remain visible to the default list"
);
}
#[test]
fn handle_add_superseding_rejects_a_status_that_is_not_insertable() {
let (_dir, mut store) = MemoryStore::test_store();
assert_eq!(
add(&mut store, "st-bad", "original fact", "medium", None),
ExitCode::SUCCESS
);
let old = stored(&store, "st-bad", "original fact");
for status in ["superseded", "deprecated"] {
let err = handle_add(
&mut store,
"st-bad",
"replacement fact",
None,
false,
"fact",
status,
"medium",
Some(&old.id),
false,
)
.expect_err("{status} is not a valid status for a new row");
assert!(
err.to_string()
.contains("not valid for new memory insertion"),
"unexpected error for --status {status}: {err}"
);
}
let err = handle_add(
&mut store,
"st-bad",
"replacement fact",
None,
false,
"fact",
"nonsense",
"medium",
Some(&old.id),
false,
)
.expect_err("an unknown status must be rejected");
assert!(
err.to_string().contains("Invalid memory status"),
"unexpected error for --status nonsense: {err}"
);
assert_eq!(stored(&store, "st-bad", "original fact").status, "active");
}