KeiSeiKit-1.0/_primitives/_rust/kei-registry/tests/index_substrate_smoke.rs
Parfii-bot 0be354a920 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

134 lines
5.5 KiB
Rust

//! Integration smoke test for `index-substrate`.
//!
//! Creates a synthetic mini-kit, runs `handle_index_substrate`, verifies row
//! counts, then verifies idempotency (second run → 0 new) and supersede (one
//! file modified → 1 superseded, rest unchanged).
use kei_registry::index_substrate::handle_index_substrate;
use kei_registry::{list, open_db, BlockType};
use std::fs;
use std::path::PathBuf;
use tempfile::tempdir;
fn mini_kit_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("fixtures")
.join("mini-kit")
}
// ── Smoke: 1 run → at least 6 rows (1 per type) ──────────────────────────
#[test]
fn index_substrate_registers_all_types() {
let tmp = tempdir().unwrap();
let db = tmp.path().join("reg.sqlite");
handle_index_substrate(Some(mini_kit_root()), Some(db.clone()), false).unwrap();
let conn = open_db(&db).unwrap();
let all = list(&conn, false, 1000).unwrap();
// Expect at least one of each type (primitive, skill, hook, block/atom, capability, role).
let has_primitive = all.iter().any(|b| b.block_type == BlockType::Primitive);
let has_skill = all.iter().any(|b| b.block_type == BlockType::Skill);
let has_hook = all.iter().any(|b| b.block_type == BlockType::Hook);
// blocks/capabilities/roles all land as Atom
let atom_count = all.iter().filter(|b| b.block_type == BlockType::Atom).count();
assert!(has_primitive, "primitive registered");
assert!(has_skill, "skill registered");
assert!(has_hook, "hook registered");
assert!(atom_count >= 3, "at least 3 atoms (block + capability + role), got {atom_count}");
}
// ── Idempotency: second run → 0 new rows ─────────────────────────────────
#[test]
fn index_substrate_idempotent() {
let tmp = tempdir().unwrap();
let db = tmp.path().join("reg.sqlite");
handle_index_substrate(Some(mini_kit_root()), Some(db.clone()), false).unwrap();
let count_after_first: i64 = {
let conn = open_db(&db).unwrap();
conn.query_row("SELECT COUNT(*) FROM blocks WHERE superseded_by IS NULL", [], |r| r.get(0)).unwrap()
};
handle_index_substrate(Some(mini_kit_root()), Some(db.clone()), false).unwrap();
let count_after_second: i64 = {
let conn = open_db(&db).unwrap();
conn.query_row("SELECT COUNT(*) FROM blocks WHERE superseded_by IS NULL", [], |r| r.get(0)).unwrap()
};
assert_eq!(count_after_first, count_after_second, "second run must not add rows");
}
// ── Supersede: modify one file → 1 superseded, rest unchanged ────────────
#[test]
fn index_substrate_supersede_on_change() {
let tmp = tempdir().unwrap();
// Copy the mini-kit to a mutable temp location.
let kit_tmp = tmp.path().join("kit");
copy_mini_kit(&kit_tmp);
let db = tmp.path().join("reg.sqlite");
handle_index_substrate(Some(kit_tmp.clone()), Some(db.clone()), false).unwrap();
let active_before: i64 = {
let conn = open_db(&db).unwrap();
conn.query_row("SELECT COUNT(*) FROM blocks WHERE superseded_by IS NULL", [], |r| r.get(0)).unwrap()
};
// Modify one file.
let role_file = kit_tmp.join("_roles").join("mini-role.toml");
let mut content = fs::read_to_string(&role_file).unwrap();
content.push_str("\n# modified");
fs::write(&role_file, content).unwrap();
handle_index_substrate(Some(kit_tmp.clone()), Some(db.clone()), false).unwrap();
let active_after: i64 = {
let conn = open_db(&db).unwrap();
conn.query_row("SELECT COUNT(*) FROM blocks WHERE superseded_by IS NULL", [], |r| r.get(0)).unwrap()
};
let superseded_count: i64 = {
let conn = open_db(&db).unwrap();
conn.query_row("SELECT COUNT(*) FROM blocks WHERE superseded_by IS NOT NULL", [], |r| r.get(0)).unwrap()
};
assert_eq!(active_after, active_before, "active count unchanged (new row replaces old)");
assert_eq!(superseded_count, 1, "exactly one superseded row");
}
// ── Dry-run: no rows written ──────────────────────────────────────────────
#[test]
fn index_substrate_dry_run_writes_nothing() {
let tmp = tempdir().unwrap();
let db = tmp.path().join("reg.sqlite");
// open_db once to create schema; then dry-run should leave it empty.
{
let _ = open_db(&db).unwrap();
}
handle_index_substrate(Some(mini_kit_root()), Some(db.clone()), true).unwrap();
let conn = open_db(&db).unwrap();
let count: i64 = conn
.query_row("SELECT COUNT(*) FROM blocks", [], |r| r.get(0))
.unwrap();
assert_eq!(count, 0, "dry-run must not write rows");
}
// ── Helper ────────────────────────────────────────────────────────────────
fn copy_mini_kit(dest: &std::path::Path) {
let src = mini_kit_root();
copy_dir_all(&src, dest).expect("copy mini-kit");
}
fn copy_dir_all(src: &std::path::Path, dst: &std::path::Path) -> std::io::Result<()> {
fs::create_dir_all(dst)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
let dest_path = dst.join(entry.file_name());
if ty.is_dir() {
copy_dir_all(&entry.path(), &dest_path)?;
} else {
fs::copy(entry.path(), dest_path)?;
}
}
Ok(())
}