47 crates, 771 tests green (up from 753 at v0.33.0). Zero new features — pure hygiene. ## kei-shared extract (SSoT for DNA format) New crate `kei-shared` consolidates DNA-parse logic that was duplicated across kei-agent-runtime + kei-dna-index. Both consumers migrated to import ParsedDna / parse_dna / is_hex8 from kei_shared. - 12 tests (10 integration + 2 unit) - kei-dna-index LOC reduction: -60 in parsed.rs (body replaced by wrapper) - kei-agent-runtime preserves lenient DnaError (legacy 4-hex parse path) - Format-string SSoT: kei_shared::compose_dna is sole source ## MEDIUM audit residuals closed (kei-entity-store) A. DDL panic coverage — verified exhaustive match across all 12 FieldKind variants; new test ddl_never_panics_on_any_fieldkind compile-time-breaks if a variant added without test update. B. Update FTS reindex invariant — doc + new update_invariant.rs module with debug_assert validating non-input FTS columns don't drift pre/post UPDATE. Zero release-mode cost (cfg-gated). C. WAL fallback — wal_pragma_fallback_keeps_store_usable test (cfg(unix)) verifies read-only-parent dir doesn't brick Store::open. D. Search Unicode edge cases — 4 new tests (punctuation, emoji, zero-width, mixed RTL). has_searchable_token already correct, no source change needed; tests pin current behavior. Added: residual_audit_smoke.rs (8 tests), update_invariant.rs module. kei-entity-store: 57 → 65 tests. ## Docs drift fixed (count claims → reality) - README.md: "36 crates → 47 crates", "500+ tests → 800+ tests" - PLUGIN.md, docs/INSTALL.md, docs/REFERENCE.md, docs/SUBSTRATE-SCHEMA.md all synced to real counts. - CHANGELOG.md: 6 new version blocks (v0.28 → v0.33) consolidated in existing style. - Historical snapshots (HANDOFF-WAKE v0.29, CONVERGENCE-PLAN, etc) deliberately preserved — they're version-scoped, not drift. ## Known deviation from task spec kei-shared's [workspace] table was dropped (Cargo rejected "multiple workspace roots" when parent workspace pulls via path dep). Crate registered in workspace.members instead. Verified cargo check + test clean in both modes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
2.4 KiB
Rust
86 lines
2.4 KiB
Rust
//! Smoke tests for the shared DNA parser.
|
|
|
|
use kei_shared::dna::{compose_dna, is_hex8, parse_dna, DnaError};
|
|
|
|
const CANONICAL: &str =
|
|
"edit-local::NG-FW-FD-CP-CG-TG-ND-RF::5435F821::AC73A6A3-e9bf468d";
|
|
|
|
#[test]
|
|
fn parse_valid_round_trip() {
|
|
let parsed = parse_dna(CANONICAL).expect("canonical DNA must parse");
|
|
assert_eq!(parsed.role, "edit-local");
|
|
assert_eq!(parsed.caps, "NG-FW-FD-CP-CG-TG-ND-RF");
|
|
assert_eq!(parsed.scope_sha, "5435F821");
|
|
assert_eq!(parsed.body_sha, "AC73A6A3");
|
|
assert_eq!(parsed.nonce, "e9bf468d");
|
|
}
|
|
|
|
#[test]
|
|
fn parse_empty_rejected() {
|
|
assert_eq!(parse_dna("").unwrap_err(), DnaError::Empty);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_missing_segments() {
|
|
let err = parse_dna("only::two::segments").unwrap_err();
|
|
assert!(matches!(err, DnaError::MissingSegments(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_missing_nonce_delim() {
|
|
// 4 `::` segments but no '-' in tail.
|
|
let err = parse_dna("r::C::12345678::AC73A6A3e9bf468d").unwrap_err();
|
|
assert_eq!(err, DnaError::MissingNonceDelim);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_non_hex_rejected() {
|
|
let err = parse_dna("r::C::12345678::AC73A6A3-ZZZZZZZZ").unwrap_err();
|
|
assert!(matches!(err, DnaError::NonHex { .. }));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_short_hex_rejected() {
|
|
// scope_sha has only 4 hex chars — strict parser rejects.
|
|
let err = parse_dna("r::C::1234::AC73A6A3-e9bf468d").unwrap_err();
|
|
assert!(matches!(err, DnaError::HexWidth { .. }));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_rejects_empty_role() {
|
|
let err = parse_dna("::C::12345678::AC73A6A3-e9bf468d").unwrap_err();
|
|
assert_eq!(err, DnaError::EmptyRole);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_rejects_empty_caps() {
|
|
let err = parse_dna("r::::12345678::AC73A6A3-e9bf468d").unwrap_err();
|
|
assert_eq!(err, DnaError::EmptyCaps);
|
|
}
|
|
|
|
#[test]
|
|
fn compose_parse_round_trip() {
|
|
let composed = compose_dna(
|
|
"edit-local",
|
|
"NG-FW-FD-CP-CG-TG-ND-RF",
|
|
"5435F821",
|
|
"AC73A6A3",
|
|
"e9bf468d",
|
|
);
|
|
assert_eq!(composed, CANONICAL);
|
|
let parsed = parse_dna(&composed).expect("round-trip parse");
|
|
assert_eq!(parsed.scope_sha, "5435F821");
|
|
assert_eq!(parsed.body_sha, "AC73A6A3");
|
|
assert_eq!(parsed.nonce, "e9bf468d");
|
|
}
|
|
|
|
#[test]
|
|
fn is_hex8_accepts_valid_rejects_invalid() {
|
|
assert!(is_hex8("00000000"));
|
|
assert!(is_hex8("DeAdBeEf"));
|
|
assert!(is_hex8("abcdef01"));
|
|
assert!(!is_hex8("abcdefg1"), "non-hex 'g' must be rejected");
|
|
assert!(!is_hex8(""));
|
|
assert!(!is_hex8("1234567"));
|
|
assert!(!is_hex8("123456789"));
|
|
}
|