use super::*;
fn add_vectorless_drawer(
handle: &trusty_common::memory_core::PalaceHandle,
content: &str,
) -> uuid::Uuid {
let drawer = trusty_common::memory_core::palace::Drawer::new(uuid::Uuid::new_v4(), content);
let id = drawer.id;
handle.add_drawer(drawer);
id
}
#[tokio::test]
async fn verify_embedded_names_the_unembedded_id() {
let (state, _tmp) = test_state();
dispatch_tool(&state, "palace_create", json!({"name": "verify-test"}))
.await
.expect("palace_create");
let embedded = dispatch_tool(
&state,
"memory_remember",
json!({
"palace": "verify-test",
"text": "Harbour measures the ledger on bay 1, an embedded fact about caching.",
}),
)
.await
.expect("memory_remember");
let embedded_id = embedded["drawer_id"]
.as_str()
.unwrap_or_else(|| panic!("memory_remember returned no drawer_id: {embedded}"))
.to_string();
let handle =
crate::tools::helpers::open_palace_handle(&state, "verify-test").expect("open palace");
let missing_id = add_vectorless_drawer(&handle, "a fact with no vector").to_string();
let out = dispatch_tool(
&state,
"palace_verify_embedded",
json!({
"palace": "verify-test",
"drawer_ids": [embedded_id.clone(), missing_id.clone()],
}),
)
.await
.expect("palace_verify_embedded must be dispatchable");
assert_eq!(
out["missing"].as_array().map(Vec::len),
Some(1),
"the vectorless drawer must be named: {out}"
);
assert_eq!(out["missing"][0], missing_id, "{out}");
assert_eq!(out["embedded"][0], embedded_id, "{out}");
assert_eq!(
out["verified"], false,
"a palace with an unembedded drawer must not pass the deletion gate: {out}"
);
}
#[tokio::test]
async fn verify_embedded_separates_an_unknown_id_from_an_unembedded_one() {
let (state, _tmp) = test_state();
dispatch_tool(&state, "palace_create", json!({"name": "verify-unknown"}))
.await
.expect("palace_create");
let stranger = uuid::Uuid::new_v4().to_string();
let out = dispatch_tool(
&state,
"palace_verify_embedded",
json!({"palace": "verify-unknown", "drawer_ids": [stranger.clone()]}),
)
.await
.expect("palace_verify_embedded");
assert_eq!(out["unknown"][0], stranger, "{out}");
assert!(
out["missing"].as_array().is_some_and(|a| a.is_empty()),
"an id that is not a drawer is not an unembedded drawer: {out}"
);
assert_eq!(out["verified"], false, "{out}");
}
#[tokio::test]
async fn verify_embedded_refuses_a_malformed_id() {
let (state, _tmp) = test_state();
dispatch_tool(&state, "palace_create", json!({"name": "verify-malformed"}))
.await
.expect("palace_create");
let err = dispatch_tool(
&state,
"palace_verify_embedded",
json!({"palace": "verify-malformed", "drawer_ids": ["not-a-uuid"]}),
)
.await
.expect_err("a malformed id must be refused, not skipped");
assert!(
format!("{err:#}").contains("not a UUID"),
"the error must name the problem: {err:#}"
);
}
#[tokio::test]
async fn embed_sweep_sees_a_palace_the_cache_never_opened() {
let (state, tmp) = test_state();
dispatch_tool(&state, "palace_create", json!({"name": "sweep-test"}))
.await
.expect("palace_create");
dispatch_tool(
&state,
"memory_remember",
json!({
"palace": "sweep-test",
"text": "Harbour measures the ledger on bay 1, a durable fact worth keeping.",
}),
)
.await
.expect("memory_remember");
drop(state);
let cold = AppState::new(tmp.path().to_path_buf());
cold.set_ready();
assert!(
cold.registry.list().is_empty(),
"the fixture must actually start cold, or this proves nothing"
);
let out = dispatch_tool(&cold, "palace_embed_sweep", json!({}))
.await
.expect("palace_embed_sweep must be dispatchable");
assert_eq!(
out["palace_count"], 1,
"the sweep must enumerate from disk, not from the handle cache: {out}"
);
assert_eq!(out["palaces"][0]["palace"], "sweep-test", "{out}");
assert_eq!(
out["palaces"][0]["drawer_count"], 1,
"a palace the cache never opened must report its real count, not 0: {out}"
);
assert_eq!(
out["unhealthy"], 0,
"nothing is missing a vector here: {out}"
);
}
#[tokio::test]
async fn embed_sweep_reports_a_palace_with_an_unembedded_drawer() {
let (state, _tmp) = test_state();
dispatch_tool(&state, "palace_create", json!({"name": "sweep-unhealthy"}))
.await
.expect("palace_create");
let handle =
crate::tools::helpers::open_palace_handle(&state, "sweep-unhealthy").expect("open palace");
add_vectorless_drawer(&handle, "a fact with no vector");
let out = dispatch_tool(&state, "palace_embed_sweep", json!({}))
.await
.expect("palace_embed_sweep");
assert_eq!(
out["unhealthy"], 1,
"a palace with an unembedded drawer is not healthy: {out}"
);
assert_eq!(out["palaces"][0]["missing"], 1, "{out}");
assert_eq!(out["palaces"][0]["healthy"], false, "{out}");
}
#[tokio::test]
async fn embed_sweep_reports_a_palace_it_could_not_open() {
let (state, tmp) = test_state();
dispatch_tool(&state, "palace_create", json!({"name": "sweep-readable"}))
.await
.expect("palace_create");
let broken = "sweep-unreadable";
let data_dir = tmp.path().join(broken);
std::fs::create_dir_all(&data_dir).expect("mkdir palace data dir");
trusty_common::memory_core::store::PalaceStore::save_palace(
&trusty_common::memory_core::Palace {
id: trusty_common::memory_core::PalaceId::new(broken),
name: broken.to_string(),
description: None,
created_at: chrono::Utc::now(),
data_dir: data_dir.clone(),
},
)
.expect("persist palace metadata");
std::fs::write(data_dir.join("index.usearch.redb"), [0xABu8; 4096])
.expect("write incompatible-format store");
let out = dispatch_tool(&state, "palace_embed_sweep", json!({}))
.await
.expect("one unopenable palace must not fail the whole sweep");
assert_eq!(
out["palace_count"], 2,
"both palaces are on disk, so both must be enumerated: {out}"
);
assert_eq!(
out["unreadable"], 1,
"a palace that could not be opened must be counted, not omitted: {out}"
);
let rows = out["palaces"].as_array().expect("palaces array");
let broken_row = rows
.iter()
.find(|r| r["palace"] == broken)
.unwrap_or_else(|| panic!("the unreadable palace must still have a row: {out}"));
assert!(
broken_row["error"].as_str().is_some_and(|e| !e.is_empty()),
"an unreadable row must say why, or it reads as an empty palace: {out}"
);
assert!(
rows.iter()
.any(|r| r["palace"] == "sweep-readable" && r["drawer_count"].is_number()),
"one bad palace must not cost the readable palace its row: {out}"
);
}