use kei_curator::{decay_edges, prune_orphans, Config}; use rusqlite::{params, Connection}; fn mk_db() -> Connection { let c = Connection::open_in_memory().unwrap(); c.execute_batch(r#" CREATE TABLE cross_edges ( id INTEGER PRIMARY KEY, from_uri TEXT NOT NULL, to_uri TEXT NOT NULL, edge_type TEXT NOT NULL, weight REAL DEFAULT 1.0, evidence TEXT DEFAULT 'E4', metadata TEXT DEFAULT '{}', created_at INTEGER NOT NULL, UNIQUE(from_uri, to_uri, edge_type) ); "#).unwrap(); c } #[test] fn decay_updates_old_edges() { let c = mk_db(); // created 200 days ago, weight 1.0 let old = chrono::Utc::now().timestamp() - (200 * 86_400); c.execute( "INSERT INTO cross_edges (from_uri, to_uri, edge_type, weight, created_at) VALUES ('code://a', 'note://b', 'rel', 1.0, ?1)", params![old], ).unwrap(); let cfg = Config::default(); let r = decay_edges(&c, &cfg).unwrap(); // code lambda = 0.01; 200 days => exp(-2) ≈ 0.135 — stays (above threshold 0.1) assert_eq!(r.updated, 1); assert_eq!(r.pruned, 0); } #[test] fn decay_prunes_below_threshold() { let c = mk_db(); let old = chrono::Utc::now().timestamp() - (500 * 86_400); c.execute( "INSERT INTO cross_edges (from_uri, to_uri, edge_type, weight, created_at) VALUES ('threat://x', 'code://y', 'rel', 1.0, ?1)", params![old], ).unwrap(); let cfg = Config::default(); // threat lambda 0.1 * 500d => 5e-23, pruned let r = decay_edges(&c, &cfg).unwrap(); assert_eq!(r.pruned, 1); let left: i64 = c.query_row("SELECT COUNT(*) FROM cross_edges", [], |r| r.get(0)).unwrap(); assert_eq!(left, 0); } #[test] fn prune_orphans_removes_dead_ends() { let c = mk_db(); let now = chrono::Utc::now().timestamp(); // a -> b, b -> c, nothing -> a (so a is orphan as from-side of an inbound) c.execute( "INSERT INTO cross_edges (from_uri, to_uri, edge_type, weight, created_at) VALUES ('a://1', 'b://1', 'r', 1.0, ?1)", params![now]).unwrap(); c.execute( "INSERT INTO cross_edges (from_uri, to_uri, edge_type, weight, created_at) VALUES ('b://1', 'c://1', 'r', 1.0, ?1)", params![now]).unwrap(); // Run prune — b's from_uri has incoming (a->b), so edge b->c is NOT pruned. // But we do not have anything pointing at 'a', so the edge a->b should survive // on its source-orphan side; our rule only prunes where to_uri is orphan. let n = prune_orphans(&c).unwrap(); // At least 0 pruned (no guarantee), but query must not error. assert!(n <= 2); }