use proptest::prelude::*;
use kkachi::recursive::validate::Validate;
use kkachi::recursive::{
all, any, checks, extract_all_code, extract_code, extract_section, rewrite, MockLlm, Template,
};
proptest! {
#[test]
fn checks_require_never_panic(text in "\\PC{0,500}") {
let v = checks().require("x").require("fn ");
let _ = v.validate(&text);
}
#[test]
fn checks_forbid_never_panic(text in "\\PC{0,500}") {
let v = checks().forbid("unsafe").forbid("panic!");
let _ = v.validate(&text);
}
#[test]
fn checks_require_all_never_panic(text in "\\PC{0,500}") {
let v = checks().require_all(["fn ", "->", "{"]);
let _ = v.validate(&text);
}
#[test]
fn checks_score_bounded(text in "\\PC{0,500}") {
let v = checks()
.require("x")
.forbid("y")
.min_len(10);
let score = v.validate(&text);
prop_assert!(score.value >= 0.0, "Score {} < 0.0", score.value);
prop_assert!(score.value <= 1.0, "Score {} > 1.0", score.value);
}
#[test]
fn checks_min_len_never_panic(text in "\\PC{0,500}", min_len in 0usize..1000) {
let v = checks().min_len(min_len);
let score = v.validate(&text);
prop_assert!(score.value >= 0.0 && score.value <= 1.0);
}
#[test]
fn empty_checks_always_pass(text in "\\PC{0,500}") {
let v = checks();
let score = v.validate(&text);
prop_assert_eq!(score.value, 1.0);
}
}
proptest! {
#[test]
fn compose_all_never_panic(text in "\\PC{0,500}") {
let v1 = checks().require("a");
let v2 = checks().forbid("b");
let composed = all([v1, v2]);
let score = composed.validate(&text);
prop_assert!(score.value >= 0.0 && score.value <= 1.0);
}
#[test]
fn compose_any_never_panic(text in "\\PC{0,500}") {
let v1 = checks().require("a");
let v2 = checks().require("b");
let composed = any([v1, v2]);
let score = composed.validate(&text);
prop_assert!(score.value >= 0.0 && score.value <= 1.0);
}
#[test]
fn compose_all_is_min(text in "\\PC{0,500}") {
let v1 = checks().require("a");
let v2 = checks().require("b");
let s1 = v1.validate(&text).value;
let s2 = v2.validate(&text).value;
let composed = all([
checks().require("a"),
checks().require("b"),
]);
let s_all = composed.validate(&text).value;
prop_assert!(s_all <= s1 + f64::EPSILON, "all score {} > v1 score {}", s_all, s1);
prop_assert!(s_all <= s2 + f64::EPSILON, "all score {} > v2 score {}", s_all, s2);
}
#[test]
fn compose_any_is_max(text in "\\PC{0,500}") {
let v1 = checks().require("a");
let v2 = checks().require("b");
let s1 = v1.validate(&text).value;
let s2 = v2.validate(&text).value;
let composed = any([
checks().require("a"),
checks().require("b"),
]);
let s_any = composed.validate(&text).value;
prop_assert!(s_any >= s1 - f64::EPSILON, "any score {} < v1 score {}", s_any, s1);
prop_assert!(s_any >= s2 - f64::EPSILON, "any score {} < v2 score {}", s_any, s2);
}
}
proptest! {
#[test]
fn extract_code_never_panic(text in "\\PC{0,500}", lang in "[a-z]{0,10}") {
let _ = extract_code(&text, &lang);
}
#[test]
fn extract_all_code_never_panic(text in "\\PC{0,500}", lang in "[a-z]{0,10}") {
let _ = extract_all_code(&text, &lang);
}
#[test]
fn extract_code_no_fences(text in "[^`]{0,200}") {
let result = extract_code(&text, "rust");
if !text.contains("```") {
prop_assert!(result.is_none());
}
}
#[test]
fn extract_code_is_substring(text in "\\PC{0,500}", lang in "[a-z]{1,5}") {
if let Some(code) = extract_code(&text, &lang) {
prop_assert!(
text.contains(code),
"Extracted code is not a substring of input"
);
}
}
}
proptest! {
#[test]
fn rewrite_section_never_panic(
text in "\\PC{0,300}",
title in "[a-zA-Z ]{1,20}",
replacement in "\\PC{0,100}"
) {
let _ = rewrite(&text).section(&title, &replacement).build();
}
#[test]
fn extract_section_never_panic(
text in "\\PC{0,300}",
title in "[a-zA-Z ]{1,20}"
) {
let _ = extract_section(&text, &title);
}
}
proptest! {
#[test]
fn template_new_never_panic(name in "\\PC{0,100}") {
let _ = Template::new(&name);
}
#[test]
fn template_render_never_panic(
input in "\\PC{0,200}"
) {
let t = Template::simple(&input);
let _ = t.render(&input);
}
}
proptest! {
#[test]
fn refine_never_panic(prompt in "\\PC{1,100}") {
let llm = MockLlm::new(|p, _| format!("response to: {}", p));
let v = checks().require("response");
let result = kkachi::recursive::refine(&llm, &prompt)
.validate(v)
.max_iter(2)
.go();
prop_assert!(result.is_ok());
}
}