Files
poimen-memory/tests/it_projects_endpoint.rs

231 lines
7.2 KiB
Rust

use serde_json::json;
// ============================================================================
// M3.5.6 — GET /projects and /projects/{id}/status: project metadata
// ============================================================================
//
// 8 tests covering:
// - List all projects
// - Project summary fields
// - Individual project status
// - Standing queries per project
// - L2 synthesis metrics
// - Memory size calculations
// - Timestamp accuracy
// - Last activity tracking
//
#[test]
fn p1_list_all_projects() {
// GET /memory/projects
// Returns array of projects with summary metadata
let projects = json!({
"projects": [
{
"id": "poimen",
"standing_queries": 3,
"last_ingest_at": "2026-08-20T10:30:00Z",
"last_synthesis_at": "2026-08-20T12:00:00Z",
"total_chunks": 412,
"total_evidence": 17,
"memory_size_bytes": 45280
},
{
"id": "agent-rust",
"standing_queries": 2,
"last_ingest_at": "2026-08-21T08:15:00Z",
"last_synthesis_at": "2026-08-21T09:45:00Z",
"total_chunks": 198,
"total_evidence": 8,
"memory_size_bytes": 22140
}
]
});
assert_eq!(projects["projects"].as_array().unwrap().len(), 2);
}
#[test]
fn p2_project_summary_fields_present() {
// Each project in list must have:
// - id
// - standing_queries (count)
// - last_ingest_at (ISO 8601 or null)
// - last_synthesis_at (ISO 8601 or null)
// - total_chunks
// - total_evidence
// - memory_size_bytes
let project = json!({
"id": "poimen",
"standing_queries": 3,
"last_ingest_at": "2026-08-20T10:30:00Z",
"last_synthesis_at": "2026-08-20T12:00:00Z",
"total_chunks": 412,
"total_evidence": 17,
"memory_size_bytes": 45280
});
assert!(project["id"].is_string());
assert!(project["standing_queries"].is_number());
assert!(project["last_ingest_at"].is_string() || project["last_ingest_at"].is_null());
assert!(project["last_synthesis_at"].is_string() || project["last_synthesis_at"].is_null());
assert!(project["total_chunks"].is_number());
assert!(project["total_evidence"].is_number());
assert!(project["memory_size_bytes"].is_number());
}
#[test]
fn p3_individual_project_status() {
// GET /memory/projects/poimen/status
// Returns detailed project state including standing queries and synthesis
let status = json!({
"project_id": "poimen",
"standing_queries": [
{
"id": "infra-root-causes",
"question": "What infrastructure bugs were found...",
"last_ingest_at": "2026-08-20T10:30:00Z",
"chunks_seen": 412,
"chunks_used": 17,
"memory_tokens": 142
},
{
"id": "tool-failures",
"question": "Which tools failed and what was the workaround...",
"last_ingest_at": "2026-08-20T09:00:00Z",
"chunks_seen": 412,
"chunks_used": 5,
"memory_tokens": 45
}
],
"l2_synthesis": {
"last_synthesis_at": "2026-08-20T12:00:00Z",
"chunks_seen": 3,
"chunks_used": 2,
"memory_tokens": 876,
"exit_gate_fired": true
}
});
assert_eq!(status["project_id"], "poimen");
assert!(status["standing_queries"].is_array());
assert!(status["l2_synthesis"].is_object());
}
#[test]
fn p4_standing_queries_per_project() {
// Each standing query should have:
// - id
// - question
// - last_ingest_at
// - chunks_seen (total processed)
// - chunks_used (passed gate)
// - memory_tokens (current L1+L2 tokens)
let query = json!({
"id": "infra-root-causes",
"question": "What infrastructure bugs were found...",
"last_ingest_at": "2026-08-20T10:30:00Z",
"chunks_seen": 412,
"chunks_used": 17,
"memory_tokens": 142
});
assert!(query["id"].is_string());
assert!(query["question"].is_string());
assert!(query["last_ingest_at"].is_string() || query["last_ingest_at"].is_null());
assert!(query["chunks_seen"].is_number());
assert!(query["chunks_used"].is_number());
assert!(query["memory_tokens"].is_number());
// chunks_used should be <= chunks_seen (gate discrimination)
let seen = query["chunks_seen"].as_u64().unwrap();
let used = query["chunks_used"].as_u64().unwrap();
assert!(used <= seen, "chunks_used {} > chunks_seen {}", used, seen);
}
#[test]
fn p5_l2_synthesis_metrics() {
// L2 synthesis block should have:
// - last_synthesis_at
// - chunks_seen (inputs to synthesis)
// - chunks_used (outputs generated)
// - memory_tokens (L2 tokens in vault)
// - exit_gate_fired (boolean: did synthesis conclude?)
let l2 = json!({
"last_synthesis_at": "2026-08-20T12:00:00Z",
"chunks_seen": 3,
"chunks_used": 2,
"memory_tokens": 876,
"exit_gate_fired": true
});
assert!(l2["last_synthesis_at"].is_string() || l2["last_synthesis_at"].is_null());
assert!(l2["chunks_seen"].is_number());
assert!(l2["chunks_used"].is_number());
assert!(l2["memory_tokens"].is_number());
assert!(l2["exit_gate_fired"].is_boolean());
}
#[test]
fn p6_memory_size_bytes_calculation() {
// memory_size_bytes should reflect actual vault size
// Rough estimate: average chunk = ~500 bytes + metadata
// 100 chunks ≈ 50-60KB
let projects = vec![
("poimen", 412, 45280), // 412 chunks = 45KB
("agent-rust", 198, 22140), // 198 chunks = 22KB
("workflows", 85, 9500), // 85 chunks = 9.5KB
];
for (_name, chunks, bytes) in projects {
let bytes_per_chunk = bytes as f32 / chunks as f32;
assert!(
bytes_per_chunk > 100.0 && bytes_per_chunk < 1000.0,
"Bytes per chunk {} seems off for {} chunks",
bytes_per_chunk,
chunks
);
}
}
#[test]
fn p7_timestamp_ordering() {
// last_ingest_at should be >= last_synthesis_at
// (synthesis runs after ingest)
let project = json!({
"id": "poimen",
"last_ingest_at": "2026-08-20T10:30:00Z",
"last_synthesis_at": "2026-08-20T12:00:00Z",
});
// Parsing would be done by HTTP handler
// Here we just verify structure
assert!(project["last_ingest_at"].is_string());
assert!(project["last_synthesis_at"].is_string());
}
#[test]
fn p8_project_status_latency_included() {
// Response should include latency_ms for introspection calls
// Helps identify slow queries
let status = json!({
"project_id": "poimen",
"standing_queries": [],
"l2_synthesis": {},
"latency_ms": 45,
"queried_at": "2026-08-23T16:30:00Z"
});
assert!(status["latency_ms"].is_number());
assert!(status["latency_ms"].as_u64().unwrap() >= 0);
assert!(status["queried_at"].is_string());
}