Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
405 lines
11 KiB
Rust
405 lines
11 KiB
Rust
//! Integration tests for kei-dna-index.
|
||
//!
|
||
//! Each test builds a minimal `agents` table in a tempfile sqlite DB,
|
||
//! then opens it read-only via the library and asserts public-API behaviour.
|
||
|
||
use kei_dna_index::{
|
||
adjacent, cluster_by, open_read_only, precedent, split_dna, stats, AdjacencyKind, ClusterBy,
|
||
Relationship,
|
||
};
|
||
use rusqlite::{params, Connection};
|
||
use tempfile::NamedTempFile;
|
||
|
||
fn setup() -> NamedTempFile {
|
||
let f = NamedTempFile::new().unwrap();
|
||
let c = Connection::open(f.path()).unwrap();
|
||
c.execute_batch(
|
||
"CREATE TABLE agents (\
|
||
id TEXT PRIMARY KEY, \
|
||
dna TEXT, \
|
||
started_ts INTEGER NOT NULL, \
|
||
status TEXT NOT NULL)",
|
||
)
|
||
.unwrap();
|
||
drop(c);
|
||
f
|
||
}
|
||
|
||
fn insert(path: &std::path::Path, id: &str, dna: Option<&str>, ts: i64, status: &str) {
|
||
let c = Connection::open(path).unwrap();
|
||
c.execute(
|
||
"INSERT INTO agents (id, dna, started_ts, status) VALUES (?1, ?2, ?3, ?4)",
|
||
params![id, dna, ts, status],
|
||
)
|
||
.unwrap();
|
||
}
|
||
|
||
#[test]
|
||
fn parse_dna_valid_format() {
|
||
let p = split_dna("edit-local::NG-FW-FD-CP-CG-TG-ND-RF::5435F821::AC73A6A3-e9bf468d").unwrap();
|
||
assert_eq!(p.role, "edit-local");
|
||
assert_eq!(p.scope_sha, "5435F821");
|
||
assert_eq!(p.body_sha, "AC73A6A3");
|
||
assert_eq!(p.nonce, "e9bf468d");
|
||
}
|
||
|
||
#[test]
|
||
fn parse_dna_rejects_malformed() {
|
||
assert!(split_dna("nope").is_err());
|
||
assert!(split_dna("a::b::c::d-e").is_err()); // short hex
|
||
assert!(split_dna("a::b::12345678::ZZZZZZZZ-12345678").is_err()); // non-hex
|
||
assert!(split_dna("a::b::12345678::12345678_12345678").is_err()); // no dash
|
||
}
|
||
|
||
#[test]
|
||
fn adjacent_same_scope() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
insert(
|
||
p,
|
||
"a1",
|
||
Some("edit::CAPS1::AAAAAAAA::11111111-22222222"),
|
||
100,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a2",
|
||
Some("edit::CAPS1::AAAAAAAA::33333333-44444444"),
|
||
200,
|
||
"running",
|
||
);
|
||
insert(
|
||
p,
|
||
"a3",
|
||
Some("edit::CAPS1::BBBBBBBB::55555555-66666666"),
|
||
300,
|
||
"merged",
|
||
);
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = adjacent(
|
||
&conn,
|
||
"edit::CAPS1::AAAAAAAA::11111111-22222222",
|
||
AdjacencyKind::Scope,
|
||
10,
|
||
)
|
||
.unwrap();
|
||
assert_eq!(out.len(), 1);
|
||
assert_eq!(out[0].agent_id, "a2");
|
||
assert_eq!(out[0].relationship, Relationship::SameScope);
|
||
assert_eq!(out[0].distance, 0);
|
||
}
|
||
|
||
#[test]
|
||
fn adjacent_same_body() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
insert(
|
||
p,
|
||
"a1",
|
||
Some("edit::CAPS1::11111111::ABCDEF01-aaaaaaaa"),
|
||
100,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a2",
|
||
Some("edit::CAPS2::22222222::ABCDEF01-bbbbbbbb"),
|
||
200,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a3",
|
||
Some("edit::CAPS1::11111111::DEADBEEF-cccccccc"),
|
||
300,
|
||
"merged",
|
||
);
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = adjacent(
|
||
&conn,
|
||
"edit::CAPS1::11111111::ABCDEF01-aaaaaaaa",
|
||
AdjacencyKind::Body,
|
||
10,
|
||
)
|
||
.unwrap();
|
||
assert_eq!(out.len(), 1);
|
||
assert_eq!(out[0].agent_id, "a2");
|
||
assert_eq!(out[0].relationship, Relationship::SameBody);
|
||
}
|
||
|
||
#[test]
|
||
fn adjacent_role_caps() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
// Target role=edit caps=ABCDEFGH
|
||
insert(
|
||
p,
|
||
"a1",
|
||
Some("edit::ABCDEFGH::11111111::AAAAAAAA-aaaaaaaa"),
|
||
100,
|
||
"merged",
|
||
);
|
||
// Same role, caps 1-char different → Hamming=1
|
||
insert(
|
||
p,
|
||
"a2",
|
||
Some("edit::ABCDEFGX::22222222::BBBBBBBB-bbbbbbbb"),
|
||
200,
|
||
"merged",
|
||
);
|
||
// Same role, caps 3-char different → Hamming=3
|
||
insert(
|
||
p,
|
||
"a3",
|
||
Some("edit::ZBCZEFGZ::33333333::CCCCCCCC-cccccccc"),
|
||
300,
|
||
"merged",
|
||
);
|
||
// Different role → excluded
|
||
insert(
|
||
p,
|
||
"a4",
|
||
Some("plan::ABCDEFGH::44444444::DDDDDDDD-dddddddd"),
|
||
400,
|
||
"merged",
|
||
);
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = adjacent(
|
||
&conn,
|
||
"edit::ABCDEFGH::11111111::AAAAAAAA-aaaaaaaa",
|
||
AdjacencyKind::Role,
|
||
10,
|
||
)
|
||
.unwrap();
|
||
assert_eq!(out.len(), 2);
|
||
assert_eq!(out[0].agent_id, "a2");
|
||
assert_eq!(out[0].distance, 1);
|
||
assert_eq!(out[1].agent_id, "a3");
|
||
assert_eq!(out[1].distance, 3);
|
||
}
|
||
|
||
#[test]
|
||
fn adjacent_temporal() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
insert(
|
||
p,
|
||
"a1",
|
||
Some("edit::C1::11111111::AAAAAAAA-aaaaaaaa"),
|
||
1000,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a2",
|
||
Some("edit::C2::22222222::BBBBBBBB-bbbbbbbb"),
|
||
1005,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a3",
|
||
Some("edit::C3::33333333::CCCCCCCC-cccccccc"),
|
||
990,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a4",
|
||
Some("edit::C4::44444444::DDDDDDDD-dddddddd"),
|
||
1100,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a5",
|
||
Some("edit::C5::55555555::EEEEEEEE-eeeeeeee"),
|
||
500,
|
||
"merged",
|
||
);
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = adjacent(
|
||
&conn,
|
||
"edit::C1::11111111::AAAAAAAA-aaaaaaaa",
|
||
AdjacencyKind::Temporal,
|
||
3,
|
||
)
|
||
.unwrap();
|
||
assert_eq!(out.len(), 3);
|
||
assert_eq!(out[0].agent_id, "a2");
|
||
assert_eq!(out[0].distance, 5);
|
||
assert_eq!(out[1].agent_id, "a3");
|
||
assert_eq!(out[1].distance, 10);
|
||
assert_eq!(out[2].agent_id, "a4");
|
||
assert_eq!(out[2].distance, 100);
|
||
}
|
||
|
||
#[test]
|
||
fn adjacent_all_kind() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
// Target
|
||
insert(
|
||
p,
|
||
"t0",
|
||
Some("edit::ABCDEFGH::11111111::AAAAAAAA-aaaaaaaa"),
|
||
100,
|
||
"merged",
|
||
);
|
||
// Same scope AND same role/caps (should appear once with dist 0)
|
||
insert(
|
||
p,
|
||
"dup",
|
||
Some("edit::ABCDEFGH::11111111::BBBBBBBB-bbbbbbbb"),
|
||
200,
|
||
"merged",
|
||
);
|
||
// Only temporal neighbor
|
||
insert(
|
||
p,
|
||
"far",
|
||
Some("plan::ZZZZZZZZ::99999999::CCCCCCCC-cccccccc"),
|
||
150,
|
||
"merged",
|
||
);
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = adjacent(
|
||
&conn,
|
||
"edit::ABCDEFGH::11111111::AAAAAAAA-aaaaaaaa",
|
||
AdjacencyKind::All,
|
||
10,
|
||
)
|
||
.unwrap();
|
||
// Two distinct DNAs: "dup" and "far"
|
||
assert_eq!(out.len(), 2);
|
||
let dup = out.iter().find(|r| r.agent_id == "dup").unwrap();
|
||
// Dup should be reported with min distance (0 from scope/body match)
|
||
assert_eq!(dup.distance, 0);
|
||
let far = out.iter().find(|r| r.agent_id == "far").unwrap();
|
||
assert_eq!(far.distance, 50);
|
||
}
|
||
|
||
#[test]
|
||
fn cluster_by_scope() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
// scope AAAA×3
|
||
insert(p, "a1", Some("r::c::AAAAAAAA::00000001-11111111"), 1, "m");
|
||
insert(p, "a2", Some("r::c::AAAAAAAA::00000002-22222222"), 2, "m");
|
||
insert(p, "a3", Some("r::c::AAAAAAAA::00000003-33333333"), 3, "m");
|
||
// scope BBBB×2
|
||
insert(p, "b1", Some("r::c::BBBBBBBB::00000004-44444444"), 4, "m");
|
||
insert(p, "b2", Some("r::c::BBBBBBBB::00000005-55555555"), 5, "m");
|
||
// scope CCCC×1 (singleton → filtered)
|
||
insert(p, "c1", Some("r::c::CCCCCCCC::00000006-66666666"), 6, "m");
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = cluster_by(&conn, ClusterBy::Scope).unwrap();
|
||
assert_eq!(out.len(), 2);
|
||
let a = out.iter().find(|c| c.key == "AAAAAAAA").unwrap();
|
||
assert_eq!(a.members.len(), 3);
|
||
let b = out.iter().find(|c| c.key == "BBBBBBBB").unwrap();
|
||
assert_eq!(b.members.len(), 2);
|
||
}
|
||
|
||
#[test]
|
||
fn cluster_filters_single_member_groups() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
// All scopes unique → no clusters
|
||
insert(p, "a", Some("r::c::AAAAAAAA::11111111-11111111"), 1, "m");
|
||
insert(p, "b", Some("r::c::BBBBBBBB::22222222-22222222"), 2, "m");
|
||
insert(p, "c", Some("r::c::CCCCCCCC::33333333-33333333"), 3, "m");
|
||
let conn = open_read_only(p).unwrap();
|
||
let out = cluster_by(&conn, ClusterBy::Scope).unwrap();
|
||
assert!(out.is_empty());
|
||
}
|
||
|
||
#[test]
|
||
fn precedent_finds_merged_only() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
insert(
|
||
p,
|
||
"a1",
|
||
Some("edit::C::11111111::DEADBEEF-11111111"),
|
||
1,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a2",
|
||
Some("plan::C::22222222::DEADBEEF-22222222"),
|
||
2,
|
||
"failed",
|
||
);
|
||
insert(
|
||
p,
|
||
"a3",
|
||
Some("edit::C::33333333::DEADBEEF-33333333"),
|
||
3,
|
||
"merged",
|
||
);
|
||
insert(
|
||
p,
|
||
"a4",
|
||
Some("edit::C::44444444::CAFEBABE-44444444"),
|
||
4,
|
||
"merged",
|
||
);
|
||
let conn = open_read_only(p).unwrap();
|
||
let merged = precedent(&conn, "DEADBEEF", Some("merged")).unwrap();
|
||
assert_eq!(merged.len(), 2);
|
||
assert!(merged.iter().all(|r| r.status == "merged"));
|
||
let all = precedent(&conn, "DEADBEEF", None).unwrap();
|
||
assert_eq!(all.len(), 3);
|
||
let all_explicit = precedent(&conn, "DEADBEEF", Some("all")).unwrap();
|
||
assert_eq!(all_explicit.len(), 3);
|
||
}
|
||
|
||
#[test]
|
||
fn stats_aggregates() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
// 4 DNAs, 2 unique scopes, 3 unique bodies, 1 scope-cluster, 1 body-cluster
|
||
insert(p, "a1", Some("r::c::AAAAAAAA::b0d10001-11111111"), 1, "m");
|
||
insert(p, "a2", Some("r::c::AAAAAAAA::b0d10002-22222222"), 2, "m");
|
||
insert(p, "a3", Some("r::c::BBBBBBBB::b0d10001-33333333"), 3, "m");
|
||
insert(p, "a4", Some("r::c::BBBBBBBB::b0d10003-44444444"), 4, "m");
|
||
let conn = open_read_only(p).unwrap();
|
||
let s = stats(&conn).unwrap();
|
||
assert_eq!(s.total_dnas, 4);
|
||
assert_eq!(s.unique_scopes, 2);
|
||
assert_eq!(s.unique_bodies, 3);
|
||
assert_eq!(s.clusters_scope, 2); // AAAA×2 + BBBB×2
|
||
assert_eq!(s.clusters_body, 1); // BODY0001×2
|
||
assert!(s.avg_cluster_size > 1.0);
|
||
}
|
||
|
||
#[test]
|
||
fn empty_ledger_returns_empty() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
let conn = open_read_only(p).unwrap();
|
||
let s = stats(&conn).unwrap();
|
||
assert_eq!(s.total_dnas, 0);
|
||
assert_eq!(s.unique_scopes, 0);
|
||
assert_eq!(s.clusters_scope, 0);
|
||
assert_eq!(s.avg_cluster_size, 0.0);
|
||
assert!(cluster_by(&conn, ClusterBy::Scope).unwrap().is_empty());
|
||
assert!(precedent(&conn, "DEADBEEF", None).unwrap().is_empty());
|
||
}
|
||
|
||
#[test]
|
||
fn malformed_dna_skipped_silently() {
|
||
let f = setup();
|
||
let p = f.path();
|
||
insert(p, "good", Some("r::c::AAAAAAAA::BBBBBBBB-cccccccc"), 1, "m");
|
||
insert(p, "bad1", Some("totally-wrong"), 2, "m");
|
||
insert(p, "bad2", Some("r::c::short::xx-yy"), 3, "m");
|
||
insert(p, "nullrow", None, 4, "m");
|
||
let conn = open_read_only(p).unwrap();
|
||
let s = stats(&conn).unwrap();
|
||
// Only 1 well-formed DNA survives the loader
|
||
assert_eq!(s.total_dnas, 1);
|
||
}
|