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.
131 lines
4.5 KiB
Rust
131 lines
4.5 KiB
Rust
//! Integration tests for kei-refactor-engine.
|
|
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use tempfile::TempDir;
|
|
|
|
fn bin() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_BIN_EXE_kei-refactor-engine"))
|
|
}
|
|
|
|
fn sample_json(extra_manual: bool) -> String {
|
|
let mut items = vec![serde_json::json!({
|
|
"category": "blocks",
|
|
"severity": "medium",
|
|
"files": ["_blocks/a.md", "_blocks/b.md"],
|
|
"evidence": "shingle-Jaccard 72% overlap",
|
|
"suggested_fix": "keep better-cited",
|
|
"auto_resolvable": true
|
|
})];
|
|
if extra_manual {
|
|
items.push(serde_json::json!({
|
|
"category": "rules",
|
|
"severity": "high",
|
|
"files": ["rules/x.md", "rules/y.md"],
|
|
"evidence": "contradictory directive on 'push'",
|
|
"suggested_fix": "review both",
|
|
"auto_resolvable": false
|
|
}));
|
|
}
|
|
serde_json::json!({ "hit_count": items.len(), "conflicts": items }).to_string()
|
|
}
|
|
|
|
#[test]
|
|
fn plan_only_prints_markdown() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let input = tmp.path().join("c.json");
|
|
fs::write(&input, sample_json(false)).unwrap();
|
|
let out = std::process::Command::new(bin())
|
|
.args(["--input"])
|
|
.arg(&input)
|
|
.output()
|
|
.unwrap();
|
|
assert!(out.status.success());
|
|
let md = String::from_utf8(out.stdout).unwrap();
|
|
assert!(md.contains("# Deep-sleep refactor plan"));
|
|
assert!(md.contains("Auto-apply"));
|
|
}
|
|
|
|
#[test]
|
|
fn manual_items_listed_but_not_in_autoresolve() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let input = tmp.path().join("c.json");
|
|
let plan_out = tmp.path().join("plan.md");
|
|
let patch_out = tmp.path().join("plan-autoresolve.md");
|
|
fs::write(&input, sample_json(true)).unwrap();
|
|
let out = std::process::Command::new(bin())
|
|
.args(["--input"])
|
|
.arg(&input)
|
|
.args(["--apply-to-branch", "deep-sleep/test", "--plan-out"])
|
|
.arg(&plan_out)
|
|
.args(["--patch-out"])
|
|
.arg(&patch_out)
|
|
.output()
|
|
.unwrap();
|
|
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
|
|
let md = fs::read_to_string(&plan_out).unwrap();
|
|
assert!(md.contains("Requires human decision"));
|
|
let autoresolve = fs::read_to_string(&patch_out).unwrap();
|
|
// autoresolve must NOT reference rules/x.md from the manual item
|
|
assert!(!autoresolve.contains("rules/x.md"), "autoresolve leaked manual item: {}", autoresolve);
|
|
assert!(autoresolve.contains("_blocks/a.md"));
|
|
// And it must NOT claim to be a unified diff.
|
|
assert!(!autoresolve.contains("--- a/"));
|
|
assert!(!autoresolve.contains("+++ b/"));
|
|
}
|
|
|
|
#[test]
|
|
fn empty_conflicts_produce_valid_plan() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let input = tmp.path().join("c.json");
|
|
fs::write(&input, r#"{"hit_count": 0, "conflicts": []}"#).unwrap();
|
|
let out = std::process::Command::new(bin())
|
|
.args(["--input"])
|
|
.arg(&input)
|
|
.output()
|
|
.unwrap();
|
|
assert!(out.status.success());
|
|
let md = String::from_utf8(out.stdout).unwrap();
|
|
assert!(md.contains("Total conflicts: **0**"));
|
|
}
|
|
|
|
#[test]
|
|
fn stdin_input_works() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let _ = tmp; // kept for parity
|
|
let mut child = std::process::Command::new(bin())
|
|
.args(["--input", "-"])
|
|
.stdin(std::process::Stdio::piped())
|
|
.stdout(std::process::Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
{
|
|
use std::io::Write;
|
|
let stdin = child.stdin.as_mut().unwrap();
|
|
stdin.write_all(sample_json(false).as_bytes()).unwrap();
|
|
}
|
|
let out = child.wait_with_output().unwrap();
|
|
assert!(out.status.success());
|
|
assert!(String::from_utf8(out.stdout).unwrap().contains("refactor plan"));
|
|
}
|
|
|
|
#[test]
|
|
fn autoresolve_header_shows_counts() {
|
|
let tmp = TempDir::new().unwrap();
|
|
let input = tmp.path().join("c.json");
|
|
let patch_out = tmp.path().join("plan-autoresolve.md");
|
|
fs::write(&input, sample_json(true)).unwrap();
|
|
std::process::Command::new(bin())
|
|
.args(["--input"])
|
|
.arg(&input)
|
|
.args(["--apply-to-branch", "deep-sleep/a", "--patch-out"])
|
|
.arg(&patch_out)
|
|
.output()
|
|
.unwrap();
|
|
let autoresolve = fs::read_to_string(&patch_out).unwrap();
|
|
assert!(autoresolve.contains("Auto-apply candidates: 1"));
|
|
assert!(autoresolve.contains("Human-decision items"));
|
|
// Retraction check: no unified-diff headers.
|
|
assert!(!autoresolve.contains("--- a/"));
|
|
assert!(!autoresolve.contains("+++ b/"));
|
|
}
|