use crate::docs::snippet_summary::{
attribute_capability_capped, attribute_declared_capped, attribute_results, attribute_unavailable,
};
use crate::snippets::types::{
DowngradeReason, Language, RunSummary, Snippet, SnippetMetadata, SnippetStatus, SourceOrigin, ValidationLevel,
ValidationResult,
};
fn downgraded(id: &str, language: Language) -> ValidationResult {
result(id, language, SnippetStatus::Downgraded)
}
fn downgraded_with_reason(id: &str, language: Language, reason: DowngradeReason) -> ValidationResult {
ValidationResult {
downgrade_reason: Some(reason),
..downgraded(id, language)
}
}
fn capability_capped(id: &str, language: Language) -> ValidationResult {
ValidationResult {
status: SnippetStatus::Pass,
capability_capped: true,
downgrade_reason: Some(DowngradeReason::ValidatorCapability),
..result(id, language, SnippetStatus::Pass)
}
}
fn declared_capped(id: &str, language: Language) -> ValidationResult {
ValidationResult {
status: SnippetStatus::Pass,
downgrade_reason: Some(DowngradeReason::Declared),
..result(id, language, SnippetStatus::Pass)
}
}
fn unresolved_dependency(id: &str, language: Language) -> ValidationResult {
ValidationResult {
unresolved_dependency: true,
..result(id, language, SnippetStatus::Unavailable)
}
}
fn toolchain_missing(id: &str, language: Language) -> ValidationResult {
result(id, language, SnippetStatus::Unavailable)
}
fn result(id: &str, language: Language, status: SnippetStatus) -> ValidationResult {
ValidationResult {
snippet: Snippet {
id: Some(id.to_string()),
path: "docs/example.md".into(),
language,
title: None,
code: String::new(),
start_line: 1,
block_index: 0,
annotation: None,
metadata: SnippetMetadata::default(),
source_origin: SourceOrigin {
path: "docs/example.md".into(),
line: 7,
block_index: 0,
},
},
status,
level: ValidationLevel::Syntax,
requested_level: ValidationLevel::TypeCheck,
effective_level: ValidationLevel::Syntax,
message: None,
duration_ms: 0,
capability_capped: false,
downgrade_reason: None,
unresolved_dependency: false,
timed_out: false,
preflight_skipped: false,
}
}
#[test]
fn attribution_names_language_count_and_level_transition() {
let summary = RunSummary::from_results(vec![
downgraded("fixture_c_smoke", Language::C),
downgraded("fixture_py_smoke", Language::Python),
]);
let detail = attribute_results(&summary, SnippetStatus::Downgraded);
assert!(
detail.contains("fixture_c_smoke (typecheck -> syntax)"),
"got: {detail}"
);
assert!(
detail.contains("fixture_py_smoke (typecheck -> syntax)"),
"got: {detail}"
);
assert!(detail.contains("c: 1"), "per-language count missing, got: {detail}");
assert!(
detail.contains("python: 1"),
"per-language count missing, got: {detail}"
);
}
#[test]
fn attribution_bounds_the_sample_and_reports_the_remainder() {
let results = (0..10)
.map(|index| downgraded(&format!("fixture_c_{index}"), Language::C))
.collect::<Vec<_>>();
let summary = RunSummary::from_results(results);
let detail = attribute_results(&summary, SnippetStatus::Downgraded);
assert!(detail.contains("c: 10"), "total must be the real count, got: {detail}");
assert!(
detail.contains("+7 more"),
"elided remainder must be reported, got: {detail}"
);
assert_eq!(
detail.matches("fixture_c_").count(),
3,
"sample must be bounded to three ids per language, got: {detail}"
);
}
#[test]
fn attribution_filters_to_the_requested_status() {
let summary = RunSummary::from_results(vec![
downgraded("fixture_downgraded", Language::C),
result("fixture_failed", Language::C, SnippetStatus::Fail),
]);
let detail = attribute_results(&summary, SnippetStatus::Downgraded);
assert!(detail.contains("fixture_downgraded"), "got: {detail}");
assert!(!detail.contains("fixture_failed"), "got: {detail}");
}
#[test]
fn attribution_is_empty_when_nothing_matches() {
let summary = RunSummary::from_results(vec![result("fixture_passed", Language::C, SnippetStatus::Pass)]);
assert_eq!(attribute_results(&summary, SnippetStatus::Downgraded), "");
}
#[test]
fn attribution_breaks_downgrades_down_by_reason_within_a_language() {
let summary = RunSummary::from_results(vec![
downgraded_with_reason("fixture_suppressed", Language::C, DowngradeReason::Annotation),
downgraded_with_reason("fixture_env_broke", Language::C, DowngradeReason::Environment),
]);
let detail = attribute_results(&summary, SnippetStatus::Downgraded);
assert!(detail.contains("c: 2"), "total must cover both reasons, got: {detail}");
assert!(detail.contains("author suppressed via annotation: 1"), "got: {detail}");
assert!(
detail.contains("environment could not reach this level: 1"),
"got: {detail}"
);
}
#[test]
fn attribution_tolerates_a_downgrade_with_no_recorded_reason() {
let summary = RunSummary::from_results(vec![downgraded("fixture_c_smoke", Language::C)]);
let detail = attribute_results(&summary, SnippetStatus::Downgraded);
assert!(detail.contains("c: 1"), "got: {detail}");
assert!(
!detail.contains('['),
"no reason must mean no reason breakdown, got: {detail}"
);
}
#[test]
fn attribution_capability_capped_names_the_validator_reason() {
let summary = RunSummary::from_results(vec![
capability_capped("fixture_php_typecheck", Language::Php),
capability_capped("fixture_ruby_typecheck", Language::Ruby),
]);
let detail = attribute_capability_capped(&summary);
assert!(detail.contains("fixture_php_typecheck"), "got: {detail}");
assert!(detail.contains("php: 1"), "got: {detail}");
assert!(detail.contains("ruby: 1"), "got: {detail}");
assert!(detail.contains("validator cannot reach this level: 1"), "got: {detail}");
}
#[test]
fn attribution_declared_capped_names_the_front_matter_reason() {
let summary = RunSummary::from_results(vec![
declared_capped("fixture_go_smoke", Language::Go),
declared_capped("fixture_java_smoke", Language::Java),
]);
let detail = attribute_declared_capped(&summary);
assert!(detail.contains("fixture_go_smoke"), "got: {detail}");
assert!(detail.contains("go: 1"), "got: {detail}");
assert!(detail.contains("java: 1"), "got: {detail}");
assert!(
detail.contains("author declared this level via front matter: 1"),
"got: {detail}"
);
}
#[test]
fn attribution_declared_capped_does_not_absorb_capability_capped_results() {
let summary = RunSummary::from_results(vec![capability_capped("fixture_ruby_typecheck", Language::Ruby)]);
let detail = attribute_declared_capped(&summary);
assert_eq!(detail, "", "got: {detail}");
}
#[test]
fn attribution_unavailable_counts_by_language_and_cause() {
let summary = RunSummary::from_results(vec![
unresolved_dependency("fixture_ts_import", Language::TypeScript),
unresolved_dependency("fixture_ts_import_2", Language::TypeScript),
toolchain_missing("fixture_zig_missing", Language::Zig),
]);
let detail = attribute_unavailable(&summary);
assert!(
detail.contains("typescript: 2 unresolved dependency, 0 toolchain missing"),
"got: {detail}"
);
assert!(
detail.contains("zig: 0 unresolved dependency, 1 toolchain missing"),
"got: {detail}"
);
}
#[test]
fn attribution_unavailable_has_no_empty_reason_bracket_or_misleading_arrow() {
let summary = RunSummary::from_results(vec![unresolved_dependency("fixture_ts_import", Language::TypeScript)]);
let detail = attribute_unavailable(&summary);
assert!(!detail.contains('['), "no dangling reason bracket, got: {detail}");
assert!(!detail.contains("->"), "no level-transition arrow, got: {detail}");
assert!(!detail.contains("fixture_ts_import"), "no sample ids, got: {detail}");
}
#[test]
fn attribution_unavailable_is_empty_when_nothing_matches() {
let summary = RunSummary::from_results(vec![result("fixture_passed", Language::C, SnippetStatus::Pass)]);
assert_eq!(attribute_unavailable(&summary), "");
}