use std::fs; use serde::Deserialize; #[derive(Debug, Deserialize)] struct G2CandidateBatch { #[serde(rename = "batchId")] batch_id: String, family: String, source: String, #[serde(rename = "supportingSources")] supporting_sources: Vec, #[serde(rename = "ledgerClusterLabel")] ledger_cluster_label: String, #[serde(rename = "selectionRule")] selection_rule: String, #[serde(rename = "candidateCount")] candidate_count: u32, #[serde(rename = "representativeBaseline")] representative_baseline: String, #[serde(rename = "promotedBatchExpansionBaselines")] promoted_batch_expansion_baselines: Vec, #[serde(rename = "expectedSharedContract")] expected_shared_contract: SharedContract, candidates: Vec, notes: Vec, } #[derive(Debug, Deserialize)] struct PromotedExpansionBaseline { #[serde(rename = "fixtureDir")] fixture_dir: String, #[serde(rename = "sceneId")] scene_id: String, #[serde(rename = "sceneName")] scene_name: String, assertions: ExpansionAssertions, } #[derive(Debug, Deserialize)] struct ExpansionAssertions { #[serde(rename = "requiredDefaultMode")] required_default_mode: String, } #[derive(Debug, Deserialize)] struct SharedContract { archetype: String, #[serde(rename = "requiredGateNames")] required_gate_names: Vec, #[serde(rename = "requiredVariantPatterns")] required_variant_patterns: Vec, } #[derive(Debug, Deserialize)] struct BatchCandidate { #[serde(rename = "sceneKey")] scene_key: String, #[serde(rename = "batchRole")] batch_role: String, status: String, } #[test] fn g2_candidate_batch_asset_is_actionable() { let batch = load_batch(); assert_eq!(batch.batch_id, "g2-lineloss-family-candidates-2026-04-18"); assert_eq!(batch.family, "G2"); assert!(batch.source.ends_with(".md")); assert_eq!(batch.ledger_cluster_label, "lineloss-family-candidate"); assert!(batch.selection_rule.contains("Track A")); assert_eq!(batch.candidate_count, 6); assert!(batch.representative_baseline.ends_with("multi_mode")); assert_eq!(batch.promoted_batch_expansion_baselines.len(), 5); assert_eq!( batch.expected_shared_contract.archetype, "multi_mode_request" ); assert!(batch .expected_shared_contract .required_gate_names .iter() .any(|item| item == "request_contract_complete")); assert!(batch .expected_shared_contract .required_variant_patterns .iter() .any(|item| item == "g2_f_diagnosis_drilldown")); assert!(batch .expected_shared_contract .required_variant_patterns .iter() .any(|item| item == "g2_d_prediction_compute")); assert_eq!(batch.candidates.len() as u32, batch.candidate_count); assert_eq!(batch.supporting_sources.len(), 2); assert!(!batch.notes.is_empty()); } #[test] fn g2_candidate_batch_promotes_four_expansion_variants() { let batch = load_batch(); assert!(batch .promoted_batch_expansion_baselines .iter() .any(|item| item.fixture_dir.ends_with("g2_weekly_single_mode") && item.scene_id == "p1-g2-weekly-single-mode-report" && item.scene_name == "P1 G2 weekly single mode report" && item.assertions.required_default_mode == "week")); assert!(batch .promoted_batch_expansion_baselines .iter() .any( |item| item.fixture_dir.ends_with("g2_mixed_linked_workflow") && item.scene_id == "p1-g2-mixed-linked-workflow-report" && item.scene_name == "P1 G2 mixed linked workflow report" && item.assertions.required_default_mode == "primary" )); assert!(batch .promoted_batch_expansion_baselines .iter() .any( |item| item.fixture_dir.ends_with("g2_comparison_crosscheck") && item.scene_id == "p1-g2-comparison-crosscheck-report" && item.scene_name == "P1 G2 comparison crosscheck report" && item.assertions.required_default_mode == "comparison" )); assert!(batch .promoted_batch_expansion_baselines .iter() .any(|item| item.fixture_dir.ends_with("g2_diagnosis_drilldown") && item.scene_id == "p1-g2-diagnosis-drilldown-report" && item.scene_name == "P1 G2 diagnosis drilldown report" && item.assertions.required_default_mode == "diagnosis")); assert!(batch .promoted_batch_expansion_baselines .iter() .any(|item| item.fixture_dir.ends_with("g2_prediction_compute") && item.scene_id == "p1-g2-prediction-compute-report" && item.scene_name == "P1 G2 prediction compute report" && item.assertions.required_default_mode == "prediction")); } #[test] fn g2_candidate_batch_keeps_promoted_candidates_visible() { let batch = load_batch(); assert!(batch .candidates .iter() .any(|item| item.scene_key == "tq_lineloss_report" && item.batch_role == "p0-anchor" && item.status == "promoted-baseline")); assert!(batch .candidates .iter() .any(|item| item.scene_key == "baiyin_lineloss_weekly" && item.batch_role == "first-expansion-anchor" && item.status == "promoted-expansion")); assert!(batch .candidates .iter() .any(|item| item.scene_key == "predicted_compute_variant" && item.batch_role == "fifth-expansion-anchor" && item.status == "promoted-expansion")); } fn load_batch() -> G2CandidateBatch { serde_json::from_str( &fs::read_to_string("tests/fixtures/generated_scene/g2_candidate_batch_2026-04-18.json") .unwrap(), ) .unwrap() }