use apr_qa_runner::ConversionFailureType;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DefectFixtureEntry {
pub upstream_fixture: String,
pub pygmy_builder: String,
pub ticket_template: String,
}
pub fn load_defect_fixture_map() -> Result<HashMap<String, DefectFixtureEntry>, String> {
let yaml = include_str!("defect_fixture_map.yaml");
serde_yaml::from_str(yaml).map_err(|e| format!("Failed to parse defect fixture map: {e}"))
}
#[must_use]
pub fn failure_type_to_key(ft: &ConversionFailureType) -> &'static str {
ft.key()
}
#[must_use]
pub fn render_ticket_template<S: ::std::hash::BuildHasher>(
template: &str,
fields: &HashMap<String, String, S>,
) -> String {
let mut result = template.to_string();
for (key, value) in fields {
let placeholder = format!("{{{key}}}");
result = result.replace(&placeholder, value);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_load_defect_fixture_map() {
let map = load_defect_fixture_map().expect("should load embedded map");
assert!(map.contains_key("tensor_name_mismatch"));
assert!(map.contains_key("dequantization_failure"));
assert!(map.contains_key("config_metadata_mismatch"));
assert!(map.contains_key("missing_artifact"));
assert!(map.contains_key("inference_failure"));
assert_eq!(map.len(), 5);
}
#[test]
fn test_entry_fields_populated() {
let map = load_defect_fixture_map().expect("load map");
let entry = map.get("tensor_name_mismatch").expect("should exist");
assert_eq!(entry.upstream_fixture, "fixtures/tensor_name_mismatch.py");
assert_eq!(entry.pygmy_builder, "test_tensor_naming_convention");
assert!(entry.ticket_template.contains("Tensor Name Mismatch"));
}
#[test]
fn test_dequantization_entry() {
let map = load_defect_fixture_map().expect("load map");
let entry = map.get("dequantization_failure").expect("should exist");
assert_eq!(
entry.upstream_fixture,
"fixtures/dequantization_roundtrip.py"
);
assert_eq!(entry.pygmy_builder, "test_dequantization_roundtrip");
assert!(entry.ticket_template.contains("Dequantization Failure"));
}
#[test]
fn test_failure_type_to_key_mapping() {
assert_eq!(
failure_type_to_key(&ConversionFailureType::TensorNameMismatch),
"tensor_name_mismatch"
);
assert_eq!(
failure_type_to_key(&ConversionFailureType::DequantizationFailure),
"dequantization_failure"
);
assert_eq!(
failure_type_to_key(&ConversionFailureType::ConfigMetadataMismatch),
"config_metadata_mismatch"
);
assert_eq!(
failure_type_to_key(&ConversionFailureType::MissingArtifact),
"missing_artifact"
);
assert_eq!(
failure_type_to_key(&ConversionFailureType::InferenceFailure),
"inference_failure"
);
assert_eq!(
failure_type_to_key(&ConversionFailureType::Unknown),
"unknown"
);
}
#[test]
fn test_render_ticket_template_basic() {
let template = "Model: {model_id}, Error: {error}";
let mut fields = HashMap::new();
fields.insert("model_id".to_string(), "test/model".to_string());
fields.insert("error".to_string(), "tensor mismatch".to_string());
let rendered = render_ticket_template(template, &fields);
assert_eq!(rendered, "Model: test/model, Error: tensor mismatch");
}
#[test]
fn test_render_ticket_template_missing_field() {
let template = "Model: {model_id}, Missing: {unknown}";
let mut fields = HashMap::new();
fields.insert("model_id".to_string(), "test/model".to_string());
let rendered = render_ticket_template(template, &fields);
assert_eq!(rendered, "Model: test/model, Missing: {unknown}");
}
#[test]
fn test_render_ticket_template_full() {
let map = load_defect_fixture_map().expect("load map");
let entry = map.get("tensor_name_mismatch").expect("should exist");
let mut fields = HashMap::new();
fields.insert("source_naming".to_string(), "HuggingFace".to_string());
fields.insert("target_naming".to_string(), "GGUF".to_string());
fields.insert("affected_count".to_string(), "42".to_string());
fields.insert("model_id".to_string(), "test/model".to_string());
fields.insert("model_path".to_string(), "/models/test".to_string());
let rendered = render_ticket_template(&entry.ticket_template, &fields);
assert!(rendered.contains("HuggingFace"));
assert!(rendered.contains("GGUF"));
assert!(rendered.contains("42"));
assert!(rendered.contains("test/model"));
}
#[test]
fn test_defect_fixture_entry_serde_roundtrip() {
let entry = DefectFixtureEntry {
upstream_fixture: "fixtures/test.py".to_string(),
pygmy_builder: "test_builder".to_string(),
ticket_template: "## Title\n\nBody {field}".to_string(),
};
let json = serde_json::to_string(&entry).expect("serialize");
let deserialized: DefectFixtureEntry = serde_json::from_str(&json).expect("deserialize");
assert_eq!(deserialized.upstream_fixture, entry.upstream_fixture);
assert_eq!(deserialized.pygmy_builder, entry.pygmy_builder);
assert_eq!(deserialized.ticket_template, entry.ticket_template);
}
}