KeiSeiKit-1.0/_primitives/_rust/kei-agent-runtime/tests/dna_smoke.rs
Parfii-bot a4e667de10 KeiSeiKit-public — clean state
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.
2026-05-01 12:09:03 +08:00

152 lines
5.1 KiB
Rust

//! Layer G — DNA identity smoke tests.
//!
//! Asserts render ↔ parse round-trip, hashing is sensitive to the
//! documented inputs, and the total string length stays short enough to
//! embed in ledger rows.
use kei_agent_runtime::capability::TaskSpec;
use kei_agent_runtime::dna::{Dna, DnaError};
use kei_agent_runtime::role::ResolvedRole;
fn fixture_task(body: &str, wl: &[&str], dl: &[&str]) -> TaskSpec {
let mut t = TaskSpec::default();
t.task.role = "edit-local".into();
t.task.agent_id = "edit-local-forge-abc".into();
t.body.text = body.into();
t.scope.files_whitelist = wl.iter().map(|s| (*s).to_string()).collect();
t.scope.files_denylist = dl.iter().map(|s| (*s).to_string()).collect();
t
}
fn edit_local_resolved() -> ResolvedRole {
ResolvedRole {
required: vec![
"policy::no-git-ops".into(),
"scope::files-whitelist".into(),
"scope::files-denylist".into(),
"quality::constructor-pattern".into(),
"quality::cargo-check-green".into(),
"quality::tests-green".into(),
"safety::no-dep-bump".into(),
"output::report-format".into(),
],
warnings: Vec::new(),
}
}
#[test]
fn render_parse_roundtrip_equals_original() {
let task = fixture_task("Port kei-forge templating.", &["_primitives/_rust/kei-forge/**"], &[]);
let resolved = edit_local_resolved();
let dna = Dna::compose(&task, &resolved);
let rendered = dna.render();
let parsed = Dna::parse(&rendered).expect("parse");
assert_eq!(parsed, dna);
assert_eq!(parsed.render(), rendered);
}
#[test]
fn different_scopes_yield_different_scope_hashes() {
let a = fixture_task("same body", &["a/**"], &[]);
let b = fixture_task("same body", &["b/**"], &[]);
let r = edit_local_resolved();
let da = Dna::compose(&a, &r);
let db = Dna::compose(&b, &r);
assert_eq!(da.body_hash, db.body_hash, "body hash should match");
assert_ne!(da.scope_hash, db.scope_hash, "scope hash must differ");
}
#[test]
fn same_body_yields_same_body_hash() {
let a = fixture_task("exact body", &[], &[]);
let b = fixture_task("exact body", &[], &[]);
let r = edit_local_resolved();
assert_eq!(Dna::compose(&a, &r).body_hash, Dna::compose(&b, &r).body_hash);
}
#[test]
fn rendered_dna_length_within_budget() {
let task = fixture_task("body", &["a"], &["b"]);
let r = edit_local_resolved();
let s = Dna::compose(&task, &r).render();
// role(10) + sep(2) + caps bitmap (8 caps * 3 - 1 = 23) + sep(2) +
// scope(8) + sep(2) + body(8) + `-` + nonce(8) = 64. Budget ≤88 per
// H4/M4/S3 widening spec; hard ceiling stays comfortably short.
assert!(
s.len() <= 88,
"DNA string should stay short; got {} chars: {}",
s.len(),
s
);
}
#[test]
fn parse_rejects_malformed_shape() {
assert_eq!(Dna::parse("too::few::segments").unwrap_err(), DnaError::Shape);
assert_eq!(
Dna::parse("role::caps::scope::no_nonce").unwrap_err(),
DnaError::Shape,
"missing `-nonce` separator must fail"
);
}
#[test]
fn widened_dna_uses_8_hex_for_all_entropy_segments() {
let task = fixture_task("entropy budget check", &["x"], &["y"]);
let r = edit_local_resolved();
let dna = Dna::compose(&task, &r);
assert_eq!(
dna.scope_hash.len(),
8,
"scope_hash must be 8 hex (32-bit), got {}: {}",
dna.scope_hash.len(),
dna.scope_hash
);
assert_eq!(
dna.body_hash.len(),
8,
"body_hash must be 8 hex (32-bit), got {}: {}",
dna.body_hash.len(),
dna.body_hash
);
assert_eq!(
dna.nonce.len(),
8,
"nonce must be 8 hex (32-bit), got {}: {}",
dna.nonce.len(),
dna.nonce
);
let round = Dna::parse(&dna.render()).expect("parse widened");
assert_eq!(round, dna);
}
#[test]
fn parse_accepts_legacy_4_hex_segments_for_rolling_upgrade() {
// Hand-built legacy DNA: pre-widening format with 4-hex segments.
// Parser MUST accept it (with stderr warning) so rolling upgrade works.
let legacy = "edit-local::NG-FW::ABCD::1234-9f0a";
let parsed = Dna::parse(legacy).expect("legacy 4-hex DNA must parse");
assert_eq!(parsed.role, "edit-local");
assert_eq!(parsed.scope_hash, "ABCD");
assert_eq!(parsed.body_hash, "1234");
assert_eq!(parsed.nonce, "9f0a");
assert_eq!(parsed.render(), legacy, "render preserves legacy widths");
}
#[test]
fn nonce_is_unique_across_10000_generated_dnas() {
// 32-bit nonce → birthday collision at ~65k; at 10k the expected number
// of duplicates is ~0.6. A single collision fails the test loudly rather
// than silently on the regression path.
let task = fixture_task("same body", &["same"], &["same"]);
let r = edit_local_resolved();
let mut seen: std::collections::HashSet<String> =
std::collections::HashSet::with_capacity(10_000);
for _ in 0..10_000 {
let n = Dna::compose(&task, &r).nonce;
assert!(
seen.insert(n.clone()),
"nonce collision at 32-bit entropy: {n}"
);
}
}