use std::fs; use serde::Deserialize; #[derive(Debug, Deserialize)] struct FamilyResults { #[serde(rename = "generatedAt")] generated_at: String, scope: String, families: Vec, } #[derive(Debug, Deserialize)] struct FamilyResult { id: String, group: String, #[serde(rename = "familyName")] family_name: String, #[serde(rename = "representativeRuns")] representative_runs: u32, #[serde(rename = "expansionRuns", default)] expansion_runs: u32, #[serde(rename = "candidateBatchCount", default)] candidate_batch_count: u32, #[serde(rename = "passedRuns")] passed_runs: u32, #[serde(rename = "failedRuns")] failed_runs: u32, #[serde(rename = "successRate")] success_rate: f64, #[serde(rename = "failureTaxonomy")] failure_taxonomy: Vec, notes: Vec, } #[derive(Debug, Deserialize)] struct FamilyExpansionPolicy { #[serde(rename = "policyVersion")] policy_version: String, scope: String, #[serde(rename = "mainlineGroups")] mainline_groups: Vec, #[serde(rename = "boundaryGroups", default)] boundary_groups: Vec, #[serde(rename = "deferredGroups")] deferred_groups: Vec, } #[derive(Debug, Deserialize)] struct GroupPolicy { group: String, policy: String, #[serde(rename = "executionRule")] execution_rule: String, #[serde(rename = "acceptanceFocus", default)] acceptance_focus: Vec, } #[test] fn family_results_asset_is_actionable() { let results: FamilyResults = serde_json::from_str( &fs::read_to_string("tests/fixtures/generated_scene/p1_family_results.json").unwrap(), ) .unwrap(); assert!(!results.generated_at.is_empty()); assert!(results.scope.contains("representative")); assert_eq!(results.families.len(), 7); for family in results.families { assert!(matches!( family.group.as_str(), "G1" | "G2" | "G3" | "G6" | "G7" | "G8" )); assert!(!family.id.is_empty()); assert!(!family.family_name.is_empty()); assert!(family.representative_runs >= 1); assert!(family.candidate_batch_count <= 102); assert_eq!( family.representative_runs + family.expansion_runs, family.passed_runs + family.failed_runs ); assert!(family.success_rate >= 0.0 && family.success_rate <= 1.0); assert!(!family.failure_taxonomy.is_empty()); assert!(!family.notes.is_empty()); } } #[test] fn family_expansion_policy_matches_mainline_scope() { let policy: FamilyExpansionPolicy = serde_json::from_str( &fs::read_to_string("tests/fixtures/generated_scene/family_expansion_policy.json").unwrap(), ) .unwrap(); assert!(!policy.policy_version.is_empty()); assert_eq!(policy.scope, "roadmap-plan-track-e"); assert_eq!(policy.mainline_groups.len(), 3); assert_eq!(policy.boundary_groups.len(), 3); assert_eq!(policy.deferred_groups.len(), 2); for group in &policy.mainline_groups { assert!(matches!(group.group.as_str(), "G1" | "G2" | "G3")); assert_eq!(group.policy, "mainline"); assert!(!group.execution_rule.is_empty()); assert!(!group.acceptance_focus.is_empty()); } for group in &policy.boundary_groups { assert!(matches!(group.group.as_str(), "G6" | "G7" | "G8")); assert_eq!(group.policy, "boundary-runtime"); assert!(!group.execution_rule.is_empty()); assert!(!group.acceptance_focus.is_empty()); } for group in &policy.deferred_groups { assert!(matches!(group.group.as_str(), "G4" | "G5")); assert!(matches!(group.policy.as_str(), "deferred" | "degraded")); assert!(!group.execution_rule.is_empty()); } }