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()); }