tidaldb/tidal/tests/review_pass2_query_for_session.rs
jx12n 9728194f16 fix: M0-M10 code-review pass2 remediation — all 91 findings
Resolves every finding in docs/reviews/M0-M10-code-review-2026-06-08-pass2.md
across the engine, network, server, and CLI crates: session restore,
replication/CRDT, WAL format and recovery, storage indexes, query/ranking
executors, cohort/community governance, and scatter-gather routing.

Adds regression tests:
- review_pass2_creator_search_filter
- review_pass2_d_replication
- review_pass2_query_for_session
- review_pass2_storage_indexes_bitmap_cache
- review_pass2_zone_a_sessions

Verified: cargo clippy -D warnings and full test suite green across all crates.
2026-06-09 12:21:00 -06:00

91 lines
2.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#![allow(clippy::unwrap_used, clippy::doc_markdown)]
//! M0M10 review pass 2, zone C regression: FOR SESSION missing-session handling
//! must NOT diverge between RETRIEVE and SEARCH.
//!
//! A RETRIEVE/SEARCH with `FOR SESSION <id>` pointing at an expired/evicted
//! session is a WELL-FORMED query. Per CODING_GUIDELINES §6 ("graceful
//! degradation, never failure") the engine must execute it WITHOUT the session
//! boost rather than hard-erroring. Before the fix RETRIEVE returned
//! `Err(SessionNotFound)` while the structurally-identical SEARCH degraded — a
//! confusing surface-specific outage the moment a session was swept. This test
//! proves both surfaces now degrade identically.
use std::{collections::HashMap, time::Duration};
use tidaldb::{
SessionId, TidalDb,
query::{retrieve::Retrieve, search::Search},
schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, Timestamp, Window},
};
fn test_schema() -> tidaldb::schema::Schema {
let mut builder = SchemaBuilder::new();
let _ = builder
.signal(
"view",
EntityKind::Item,
DecaySpec::Exponential {
half_life: Duration::from_secs(7 * 24 * 3600),
},
)
.windows(&[Window::AllTime])
.add();
builder.build().unwrap()
}
fn test_db() -> TidalDb {
TidalDb::builder()
.ephemeral()
.with_schema(test_schema())
.open()
.unwrap()
}
#[test]
fn retrieve_and_search_degrade_identically_on_missing_session() {
let db = test_db();
// Seed one item with a `view` signal so both queries have something to rank.
let now = Timestamp::now();
db.signal("view", EntityId::new(1), 1.0, now).unwrap();
let mut meta = HashMap::new();
meta.insert("title".to_string(), "jazz piano".to_string());
db.write_item_with_metadata(EntityId::new(1), &meta)
.unwrap();
// A session id that was never started (the "swept session" case).
let missing = SessionId::from_raw(9_999_999);
// RETRIEVE FOR SESSION <missing> must NOT error — it degrades to no-boost.
let retrieve = Retrieve::builder()
.profile("new")
.for_session(missing)
.limit(10)
.build()
.unwrap();
let r = db.retrieve(&retrieve);
assert!(
r.is_ok(),
"RETRIEVE with a missing session must degrade gracefully, got {:?}",
r.err()
);
// SEARCH FOR SESSION <missing> must likewise degrade (this already worked;
// asserted here to lock the two surfaces together).
let search = Search::builder()
.query("jazz")
.using_profile("search")
.for_session(missing)
.limit(10)
.build()
.unwrap();
let s = db.search(&search);
assert!(
s.is_ok(),
"SEARCH with a missing session must degrade gracefully, got {:?}",
s.err()
);
db.close().unwrap();
}