KeiSeiKit-1.0/_primitives/_rust/kei-artifact/tests/validation.rs
Parfii-bot 537589e6a7 feat(primitives): kei-artifact typed handoff pipeline (BMAD-style doc passthrough)
- kei-artifact Rust crate (25th): schema registry + artifact store + SHA-256 id + chain walker
- 5 schemas (JSON Schema 2020-12 strict): spec / plan / patch / review / research
- Manifest extension: optional produces_artifact + expects_artifact per handoff (non-breaking)
- Validator extension: KNOWN_ARTIFACT_SCHEMAS whitelist check + 4 new tests
- 3 kei-* manifests updated with typed handoff (architect→code-implementer→critic chain)
- compose-solution phase-5 cross-ref to kei-artifact

Tests: 189 Rust workspace (was 167, +22 artifact tests) + 24 assembler (was 20, +4 validator tests)
2026-04-22 14:10:08 +08:00

139 lines
4.3 KiB
Rust

//! Integration tests — schema validation edge cases.
//!
//! Constructor Pattern: one scenario per test, one assertion focus.
use kei_artifact::artifact::{emit, get, register_schema, validate_by_id};
use kei_artifact::schemas::register_builtins;
use kei_artifact::Store;
use serde_json::json;
fn seed() -> Store {
let s = Store::open_memory().unwrap();
register_builtins(&s).unwrap();
s
}
#[test]
fn invalid_content_rejected_at_emit_time() {
let s = seed();
// spec requires goal, constraints, invariants — give only goal.
let bad = serde_json::to_vec(&json!({"goal": "x"})).unwrap();
let err = emit(&s, "spec", "kei-architect", &bad, None, None).unwrap_err();
let msg = format!("{err:#}");
assert!(
msg.contains("constraints") || msg.contains("invariants"),
"unexpected error: {msg}"
);
}
#[test]
fn validate_by_id_passes_for_conforming_content() {
let s = seed();
let content = serde_json::to_vec(&json!({
"summary": "ok",
"changes": [{"path": "x", "op": "mod", "summary": "tiny"}]
}))
.unwrap();
let id = emit(&s, "patch", "kei-code-implementer", &content, None, None).unwrap();
assert!(validate_by_id(&s, &id).is_ok());
}
#[test]
fn validate_by_id_detects_drift_after_schema_override() {
let s = seed();
let content = serde_json::to_vec(&json!({
"summary": "ok",
"changes": [{"path": "x", "op": "mod", "summary": "t"}]
}))
.unwrap();
let id = emit(&s, "patch", "kei-code-implementer", &content, None, None).unwrap();
let stricter = r#"{
"type": "object",
"additionalProperties": false,
"required": ["summary", "changes", "must_have"],
"properties": {
"summary": {"type": "string"},
"changes": {"type": "array"},
"must_have": {"type": "string"}
}
}"#;
register_schema(&s, "patch", stricter).unwrap();
let err = validate_by_id(&s, &id).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("must_have"), "unexpected: {msg}");
}
#[test]
fn unknown_schema_name_rejected_at_emit() {
let s = seed();
let err = emit(&s, "not-a-real-schema", "x", b"{}", None, None).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("unknown schema"), "unexpected: {msg}");
}
#[test]
fn review_schema_accepts_canonical_critic_output() {
let s = seed();
let content = serde_json::to_vec(&json!({
"reviewer": "kei-critic",
"findings": [
{
"severity": "high",
"category": "bug",
"title": "off-by-one",
"file": "src/x.rs",
"line": 42
}
],
"verdict": "request_changes"
}))
.unwrap();
let id = emit(&s, "review", "kei-critic", &content, None, None).unwrap();
let back = get(&s, &id).unwrap().unwrap();
assert_eq!(back.schema_name, "review");
}
#[test]
fn research_schema_accepts_claims_with_evidence_grade() {
let s = seed();
let content = serde_json::to_vec(&json!({
"question": "Does Rust's cargo support offline builds?",
"claims": [
{
"claim": "cargo --offline works with pre-cached deps",
"evidence_grade": "E1",
"confidence": "100",
"sources": [{"url": "https://doc.rust-lang.org/cargo/", "verified": true}]
}
]
}))
.unwrap();
let id = emit(&s, "research", "kei-researcher", &content, None, None).unwrap();
assert!(validate_by_id(&s, &id).is_ok());
}
#[test]
fn plan_schema_rejects_empty_steps() {
let s = seed();
let bad = serde_json::to_vec(&json!({
"goal": "g",
"steps": []
}))
.unwrap();
let err = emit(&s, "plan", "kei-architect", &bad, None, None).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("array"), "unexpected: {msg}");
}
#[test]
fn patch_schema_rejects_invalid_op_enum() {
let s = seed();
let bad = serde_json::to_vec(&json!({
"summary": "ok",
"changes": [{"path": "x", "op": "RENAME", "summary": "t"}]
}))
.unwrap();
let err = emit(&s, "patch", "kei-code-implementer", &bad, None, None).unwrap_err();
let msg = format!("{err:#}");
assert!(msg.contains("enum"), "unexpected: {msg}");
}