use forge_engine::lab::suite::TaskExpected;
use forge_engine::*;
use tempfile::TempDir;
const DEFAULT_EXPECTED: TaskExpected = TaskExpected {
require_fmt: true,
require_clippy: true,
require_tests: true,
};
fn open_test_store() -> (TempDir, ForgeStore) {
let dir = TempDir::new().unwrap();
let db_path = dir.path().join("forge.db");
let store = ForgeStore::open(&db_path).unwrap();
(dir, store)
}
#[test]
fn h1_evaluate_one_candidate_one_task_mocked_patch() {
let (_dir, store) = open_test_store();
let spec = serde_json::to_string(&AlgebraSpec::default()).unwrap();
store
.insert_candidate("candidate-1", &spec, "[]", "active")
.unwrap();
let check = CheckResult {
fmt_pass: true,
clippy_pass: true,
test_pass: true,
fmt_output: ParsedCheckOutput::default(),
clippy_output: ParsedCheckOutput::default(),
test_output: ParsedCheckOutput::default(),
total_duration_ms: 100,
};
let correctness = compute_correctness(&check, &DEFAULT_EXPECTED);
assert!(
correctness > 0.0,
"All checks pass, correctness should be > 0"
);
assert!(
(correctness - 1.0).abs() < 0.01,
"All checks pass: raw correctness should be 1.0"
);
let result = EvalRunResult {
eval_id: "eval-1".to_string(),
candidate_id: "candidate-1".to_string(),
task_id: "task-1".to_string(),
scores: ScoreVector {
correctness,
novelty: 0.0,
stability: 0.0,
weighted_total: correctness,
cea_confidence: None,
cea_predicted_correctness: None,
},
check_result: Some(check),
patch_hash: "abc".to_string(),
structural_sig: "def".to_string(),
mindstate_hash: "ghi".to_string(),
};
persist_eval_run(&store, &result, "host", "[]", "inline:test", None).unwrap();
let runs = store.get_eval_runs_for_candidate("candidate-1").unwrap();
assert_eq!(runs.len(), 1);
assert_eq!(runs[0].eval_id, "eval-1");
}
#[test]
fn h2_archive_insert_replaces_lower_score() {
let (_dir, store) = open_test_store();
let config = ForgeConfig::default();
let patch = StructuredPatch {
patch_id: uuid::Uuid::new_v4(),
summary: "test".to_string(),
edits: vec![forge_engine::FileEdit {
path: std::path::PathBuf::from("src/lib.rs"),
ops: vec![forge_engine::EditOp::Insert {
anchor: forge_engine::Anchor::AfterLine {
line: 1,
context_before: vec![],
context_after: vec![],
},
lines: vec!["// change".to_string()],
}],
mode: Some(forge_engine::FileMode::Modify),
}],
notes: vec![],
};
let scores_a = ScoreVector {
correctness: 0.96,
novelty: 0.1,
stability: 0.0,
weighted_total: 0.7,
cea_confidence: None,
cea_predicted_correctness: None,
};
let result_a = archive_insert(&store, "cand-A", &scores_a, &patch, &config, None).unwrap();
assert!(
result_a == ArchiveUpdate::Inserted,
"First insert should succeed"
);
let scores_b = ScoreVector {
correctness: 0.98,
novelty: 0.15,
stability: 0.0,
weighted_total: 0.9,
cea_confidence: None,
cea_predicted_correctness: None,
};
let result_b = archive_insert(&store, "cand-B", &scores_b, &patch, &config, None).unwrap();
assert_eq!(result_b, ArchiveUpdate::Replaced);
}
#[test]
fn h3_archive_insert_below_gate_discarded() {
let (_dir, store) = open_test_store();
let config = ForgeConfig::default();
let patch = StructuredPatch {
patch_id: uuid::Uuid::new_v4(),
summary: "test".to_string(),
edits: vec![forge_engine::FileEdit {
path: std::path::PathBuf::from("src/lib.rs"),
ops: vec![forge_engine::EditOp::Insert {
anchor: forge_engine::Anchor::AfterLine {
line: 1,
context_before: vec![],
context_after: vec![],
},
lines: vec!["// change".to_string()],
}],
mode: Some(forge_engine::FileMode::Modify),
}],
notes: vec![],
};
let scores = ScoreVector {
correctness: 0.80,
novelty: 0.1,
stability: 0.0,
weighted_total: 0.6,
cea_confidence: None,
cea_predicted_correctness: None,
};
let result = archive_insert(&store, "cand-low", &scores, &patch, &config, None).unwrap();
assert_eq!(result, ArchiveUpdate::BelowGate);
}
#[test]
fn h4_archive_preserves_higher_score() {
let (_dir, store) = open_test_store();
let config = ForgeConfig::default();
let patch = StructuredPatch {
patch_id: uuid::Uuid::new_v4(),
summary: "test".to_string(),
edits: vec![forge_engine::FileEdit {
path: std::path::PathBuf::from("src/lib.rs"),
ops: vec![forge_engine::EditOp::Insert {
anchor: forge_engine::Anchor::AfterLine {
line: 1,
context_before: vec![],
context_after: vec![],
},
lines: vec!["// change".to_string()],
}],
mode: Some(forge_engine::FileMode::Modify),
}],
notes: vec![],
};
let scores_a = ScoreVector {
correctness: 0.98,
novelty: 0.15,
stability: 0.0,
weighted_total: 0.9,
cea_confidence: None,
cea_predicted_correctness: None,
};
archive_insert(&store, "cand-A", &scores_a, &patch, &config, None).unwrap();
let scores_b = ScoreVector {
correctness: 0.96,
novelty: 0.1,
stability: 0.0,
weighted_total: 0.8,
cea_confidence: None,
cea_predicted_correctness: None,
};
let result = archive_insert(&store, "cand-B", &scores_b, &patch, &config, None).unwrap();
assert_eq!(
result,
ArchiveUpdate::NoChange,
"Lower score should not replace higher"
);
}
#[test]
fn h5_raw_scoring_model() {
let (_dir, store) = open_test_store();
let check_fail = CheckResult {
fmt_pass: false,
clippy_pass: false,
test_pass: false,
fmt_output: ParsedCheckOutput::default(),
clippy_output: ParsedCheckOutput::default(),
test_output: ParsedCheckOutput::default(),
total_duration_ms: 50,
};
let c_fail = compute_correctness(&check_fail, &DEFAULT_EXPECTED);
assert!(
(c_fail - 0.0).abs() < 1e-9,
"All-fail correctness should be 0.0, got {c_fail}"
);
let check_fmt = CheckResult {
fmt_pass: true,
clippy_pass: false,
test_pass: false,
fmt_output: ParsedCheckOutput::default(),
clippy_output: ParsedCheckOutput::default(),
test_output: ParsedCheckOutput::default(),
total_duration_ms: 50,
};
let c_fmt = compute_correctness(&check_fmt, &DEFAULT_EXPECTED);
assert!(
(c_fmt - 0.10).abs() < 1e-9,
"Fmt-only correctness should be 0.10, got {c_fmt}"
);
let check_all = CheckResult {
fmt_pass: true,
clippy_pass: true,
test_pass: true,
fmt_output: ParsedCheckOutput::default(),
clippy_output: ParsedCheckOutput::default(),
test_output: ParsedCheckOutput::default(),
total_duration_ms: 50,
};
let c_all = compute_correctness(&check_all, &DEFAULT_EXPECTED);
assert!(
(c_all - 1.0).abs() < 1e-9,
"All-pass correctness should be 1.0, got {c_all}"
);
let task = EvalTask {
task_id: "t1".to_string(),
prompt: "Test".to_string(),
constraints: forge_engine::lab::suite::TaskConstraints {
allow_test_modifications: false,
max_files_changed: None,
},
weights: forge_engine::lab::suite::TaskWeights {
correctness: 0.5,
novelty: 0.3,
stability: 0.2,
},
expected: forge_engine::lab::suite::TaskExpected {
require_fmt: true,
require_clippy: true,
require_tests: true,
},
cea: forge_engine::lab::suite::TaskCea {
instrument: false,
risk_threshold_override: None,
},
};
let config = ForgeConfig::default();
let patch = StructuredPatch {
patch_id: uuid::Uuid::new_v4(),
summary: "test".to_string(),
edits: vec![forge_engine::FileEdit {
path: std::path::PathBuf::from("src/lib.rs"),
ops: vec![forge_engine::EditOp::Insert {
anchor: forge_engine::Anchor::AfterLine {
line: 1,
context_before: vec![],
context_after: vec![],
},
lines: vec!["// new".to_string()],
}],
mode: Some(forge_engine::FileMode::Modify),
}],
notes: vec![],
};
let scores = compute_scores(&check_all, &patch, &task, &store, "q1", &config).unwrap();
assert!(
(scores.correctness - 1.0).abs() < 1e-9,
"scores.correctness should be raw 1.0, got {}",
scores.correctness
);
assert!(
scores.weighted_total <= 1.0 && scores.weighted_total > 0.0,
"weighted_total should be in (0, 1], got {}",
scores.weighted_total
);
}
#[test]
fn h6_anti_fluff_guard() {
let (_dir, store) = open_test_store();
let config = ForgeConfig::default();
let patch = StructuredPatch {
patch_id: uuid::Uuid::new_v4(),
summary: "test".to_string(),
edits: vec![forge_engine::FileEdit {
path: std::path::PathBuf::from("src/lib.rs"),
ops: vec![forge_engine::EditOp::Insert {
anchor: forge_engine::Anchor::AfterLine {
line: 1,
context_before: vec![],
context_after: vec![],
},
lines: vec!["// new line".to_string()],
}],
mode: Some(forge_engine::FileMode::Modify),
}],
notes: vec![],
};
let tags = forge_engine::extract_strategy_tags(&patch);
let tags_json = serde_json::to_string(&tags).unwrap();
let structural_hash = {
let mut h = blake3::Hasher::new();
for edit in &patch.edits {
h.update(edit.path.to_string_lossy().as_bytes());
h.update(&(edit.ops.len() as u64).to_le_bytes());
}
h.finalize().to_hex().to_string()
};
store
.insert_answer_trace(
"trace-1",
"q-fluff",
"v0001",
&tags_json,
"patch-hash",
&structural_hash, "{}",
)
.unwrap();
let novelty =
forge_engine::lab::evaluate::compute_novelty_score(&patch, &store, "q-fluff", &config)
.unwrap();
assert!(
(novelty - 0.0).abs() < 1e-9,
"Novelty should be 0.0 due to anti-fluff guard, got {novelty}"
);
}