use super::*;
use crate::schema::parse_contract_str;
const REGISTRY_META: &str = r#"
metadata:
version: "1.0.0"
description: "registry fixture"
references: ["Paper (2024)"]
kind: kernel
registry: true
"#;
const KERNEL_META: &str = r#"
metadata:
version: "1.0.0"
description: "kernel fixture"
references: ["Paper (2024)"]
kind: kernel
equations:
f:
formula: "f(x) = x"
proof_obligations:
- type: invariant
property: "output is finite"
kani_harnesses:
- id: KANI-001
obligation: "output is finite"
bound: 8
qa_gate:
id: F-001
name: "Test Gate"
checks: ["finiteness"]
pass_criteria: "All tests pass"
"#;
fn test_entry(id: &str, harness_line: &str) -> String {
format!(
" - id: {id}\n rule: \"finiteness\"\n prediction: \"output is always finite\"\n if_fails: \"overflow\"\n{harness_line}"
)
}
fn count(violations: &[Violation], rule: &str) -> usize {
violations
.iter()
.filter(|v| v.rule == rule && v.severity == Severity::Error)
.count()
}
#[test]
fn schema_024_025_case_table() {
let one_ok = format!(
"falsification_tests:\n{}",
test_entry("F-1", " test_harness: \"cargo test -p x finite\"\n")
);
let six_blank: String = (1..=6)
.map(|i| test_entry(&format!("F-{i}"), " test_harness: \"\"\n"))
.collect();
let six_blank = format!("falsification_tests:\n{six_blank}");
let rows: Vec<(&str, &str, String, usize, usize)> = vec![
("registry, key absent", REGISTRY_META, String::new(), 0, 0),
("registry, one test with a harness", REGISTRY_META, one_ok.clone(), 0, 0),
(
"registry, test names no harness at all (not this rule)",
REGISTRY_META,
format!("falsification_tests:\n{}", test_entry("F-1", "")),
0,
0,
),
("kernel, one test with a harness", KERNEL_META, one_ok, 0, 0),
(
"registry, written and empty (#5290 mutant 1)",
REGISTRY_META,
"falsification_tests: []\n".to_string(),
1,
0,
),
(
"registry, six blank test_harness (#5290 mutant 2)",
REGISTRY_META,
six_blank,
0,
6,
),
(
"registry, whitespace-only test_harness",
REGISTRY_META,
format!("falsification_tests:\n{}", test_entry("F-1", " test_harness: \" \"\n")),
0,
1,
),
(
"registry, blank test:",
REGISTRY_META,
format!("falsification_tests:\n{}", test_entry("F-1", " test: \"\"\n")),
0,
1,
),
(
"registry, blank command: (alias of test:)",
REGISTRY_META,
format!("falsification_tests:\n{}", test_entry("F-1", " command: \"\"\n")),
0,
1,
),
(
"kernel, written and empty",
KERNEL_META,
"falsification_tests: []\n".to_string(),
1,
0,
),
];
for (row, meta, block, want_024, want_025) in rows {
let yaml = format!("{meta}{block}");
let contract = parse_contract_str(&yaml).unwrap_or_else(|e| panic!("{row}: parse: {e}"));
let violations = validate_contract(&contract);
assert_eq!(count(&violations, "SCHEMA-024"), want_024, "{row}: SCHEMA-024 in {violations:?}");
assert_eq!(count(&violations, "SCHEMA-025"), want_025, "{row}: SCHEMA-025 in {violations:?}");
}
}
#[test]
fn kernel_empty_block_still_fails_provability() {
let contract = parse_contract_str(&format!("{KERNEL_META}falsification_tests: []\n")).unwrap();
let violations = validate_contract(&contract);
assert!(violations.iter().any(|v| v.rule == "PROVABILITY-001"), "{violations:?}");
}
#[test]
fn falsification_tests_declared_tells_absent_from_empty() {
let absent = parse_contract_str(REGISTRY_META).unwrap();
let empty = parse_contract_str(&format!("{REGISTRY_META}falsification_tests: []\n")).unwrap();
assert!(absent.falsification_tests.is_empty() && !absent.falsification_tests_declared);
assert!(empty.falsification_tests.is_empty() && empty.falsification_tests_declared);
}