KeiSeiKit-1.0/_primitives/_rust/kei-spawn/tests/pipeline_unit.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

139 lines
4.3 KiB
Rust

//! pipeline_unit — fine-grained coverage for pipeline helpers + the
//! spawn-rollback-on-ledger-failure contract.
//!
//! Complements pipeline_smoke.rs (end-to-end) with unit-level assertions
//! that don't need the full spawn pipeline.
use kei_spawn::{
derive_steps, emit_pipeline_json, pipeline_json_path, spawn_from_task, PipelineChain,
PipelineStep,
};
use std::path::Path;
use tempfile::TempDir;
fn write_capability(root: &Path, cat: &str, slug: &str, body: &str) {
let dir = root.join("_capabilities").join(cat).join(slug);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("text.md"), body).unwrap();
}
fn write_role(root: &Path, name: &str, toml: &str) {
std::fs::create_dir_all(root.join("_roles")).unwrap();
std::fs::write(root.join("_roles").join(format!("{name}.toml")), toml).unwrap();
}
fn minimal_kit(root: &Path) {
write_capability(root, "policy", "no-git-ops", "## Never git.\n");
write_capability(root, "output", "report-format", "## Report fields.\n");
write_role(
root,
"edit-local",
r#"
[role]
name = "edit-local"
spawnable = true
claude-subagent-type = "code-implementer"
[capabilities]
required = ["policy::no-git-ops", "output::report-format"]
"#,
);
}
#[test]
fn derive_steps_child_ids_distinct() {
let roles = vec!["auditor".to_string(), "merger".to_string()];
let steps = derive_steps("ag-edit-local-zzz", &roles);
assert_eq!(steps.len(), 2);
assert_eq!(steps[0].agent_id, "ag-edit-local-zzz-auditor");
assert_eq!(steps[1].agent_id, "ag-edit-local-zzz-merger");
assert_ne!(steps[0].agent_id, steps[1].agent_id);
}
#[test]
fn derive_steps_skips_empty_role_names() {
let roles = vec![
"auditor".to_string(),
" ".to_string(),
"".to_string(),
"merger".to_string(),
];
let steps = derive_steps("ag-writer-001", &roles);
assert_eq!(steps.len(), 2);
assert_eq!(steps[0].role, "auditor");
assert_eq!(steps[1].role, "merger");
}
#[test]
fn emit_pipeline_json_creates_parent_dir() {
let tmp = TempDir::new().unwrap();
let nested = tmp.path().join("a").join("b").join("pipeline.json");
let chain = PipelineChain {
steps: vec![PipelineStep {
role: "auditor".into(),
agent_id: "ag-x-auditor".into(),
}],
};
emit_pipeline_json(&nested, &chain).expect("emit");
assert!(nested.is_file(), "{} should exist", nested.display());
let body = std::fs::read_to_string(&nested).unwrap();
assert!(body.contains("\"auditor\""), "json: {body}");
assert!(body.contains("\"ag-x-auditor\""), "json: {body}");
}
#[test]
fn pipeline_json_path_uses_convention() {
let root = Path::new("/tmp/kit");
let path = pipeline_json_path(root, "ag-writer-42");
assert_eq!(
path,
Path::new("/tmp/kit/tasks/ag-writer-42/pipeline.json")
);
}
#[test]
fn precedent_check_env_gated_off_silent() {
// Ensure env flag absent → run_advisory returns Ok(0) without shelling out.
std::env::remove_var("KEI_SPAWN_PRECEDENT_CHECK");
let n = kei_spawn::precedent::run_advisory("00".repeat(32).as_str()).unwrap();
assert_eq!(n, 0);
}
#[test]
fn spawn_rolls_back_task_dir_on_ledger_fail() {
// Force ledger failure by pointing at a bogus binary AND clearing the
// noop escape hatch so the subprocess actually runs (and fails).
std::env::remove_var("KEI_SPAWN_LEDGER_NOOP");
std::env::set_var("KEI_LEDGER_BIN", "/nonexistent/kei-ledger-rollback-test");
let tmp = TempDir::new().unwrap();
let root = tmp.path();
minimal_kit(root);
let task_path = root.join("task.toml");
std::fs::write(
&task_path,
r#"
[task]
role = "edit-local"
agent-id = "ag-edit-local-rollback-001"
[body]
text = "Rollback test."
"#,
)
.unwrap();
let result = spawn_from_task(&task_path, root);
assert!(result.is_err(), "ledger fail must propagate");
let agent_dir = root.join("tasks").join("ag-edit-local-rollback-001");
assert!(
!agent_dir.exists(),
"task dir must be cleaned up after ledger failure; still exists at {}",
agent_dir.display()
);
// Restore ledger env so other tests in the crate don't see the bogus path.
std::env::remove_var("KEI_LEDGER_BIN");
std::env::set_var("KEI_SPAWN_LEDGER_NOOP", "1");
}