use std::fs; use serde::Deserialize; #[derive(Debug, Deserialize)] struct SceneLedgerSnapshot { #[serde(rename = "snapshotDate")] snapshot_date: String, #[serde(rename = "sourceWorkbook")] source_workbook: String, #[serde(rename = "generatedAt")] generated_at: String, summary: LedgerSummary, #[serde(rename = "groupingResultCounts")] grouping_result_counts: std::collections::BTreeMap, #[serde(rename = "familyJudgementCounts")] family_judgement_counts: std::collections::BTreeMap, #[serde(rename = "validatedEntries")] validated_entries: Vec, #[serde(rename = "alignmentNotes")] alignment_notes: Vec, } #[derive(Debug, Deserialize)] struct LedgerSummary { #[serde(rename = "totalScenes")] total_scenes: u32, #[serde(rename = "validatedScenes")] validated_scenes: u32, #[serde(rename = "notYetValidatedScenes")] not_yet_validated_scenes: u32, #[serde(rename = "passedRealSample")] passed_real_sample: u32, #[serde(rename = "failedRealSample")] failed_real_sample: u32, #[serde(rename = "failClosedScenes")] fail_closed_scenes: u32, #[serde(rename = "reassignedBoundaryScenes")] reassigned_boundary_scenes: u32, } #[derive(Debug, Deserialize)] struct ValidatedEntry { #[serde(rename = "sceneName")] scene_name: String, #[serde(rename = "targetFamily")] target_family: String, #[serde(rename = "groupingResult")] grouping_result: String, #[serde(rename = "validationStatus")] validation_status: String, #[serde(rename = "validationResult")] validation_result: String, } #[test] fn scene_ledger_snapshot_is_actionable() { let snapshot = load_snapshot(); assert_eq!(snapshot.snapshot_date, "2026-04-18"); assert!(snapshot.source_workbook.ends_with(".xlsx")); assert_eq!(snapshot.generated_at, "2026-04-18 16:48:05"); assert_eq!(snapshot.summary.total_scenes, 102); assert_eq!( snapshot.summary.validated_scenes + snapshot.summary.not_yet_validated_scenes, snapshot.summary.total_scenes ); assert_eq!(snapshot.summary.passed_real_sample, 1); assert_eq!(snapshot.summary.failed_real_sample, 3); assert_eq!(snapshot.summary.fail_closed_scenes, 2); assert_eq!(snapshot.summary.reassigned_boundary_scenes, 3); assert_eq!( snapshot.validated_entries.len() as u32, snapshot.summary.validated_scenes ); assert!(snapshot .grouping_result_counts .contains_key("95598/工单家族候选")); assert!(snapshot.family_judgement_counts.contains_key("待分组")); assert!(!snapshot.alignment_notes.is_empty()); } #[test] fn scene_ledger_snapshot_matches_current_family_asset_baseline() { let snapshot = load_snapshot(); let has_g1e_pass = snapshot.validated_entries.iter().any(|item| { item.scene_name == "高低压新增报装容量月度统计表" && item.target_family == "G1-E" && item.validation_result == "通过" }); assert!(has_g1e_pass); let has_g6_boundary = snapshot.validated_entries.iter().any(|item| { item.scene_name == "电能表现场检验完成率指标报表" && item.target_family == "G6" && item.validation_result == "已重分组" }); assert!(has_g6_boundary); let has_g7_boundary = snapshot.validated_entries.iter().any(|item| { item.scene_name == "计量资产库存统计" && item.target_family == "G7" && item.validation_result == "已重分组" }); assert!(has_g7_boundary); let has_g8_boundary = snapshot.validated_entries.iter().any(|item| { item.scene_name == "95598供电服务月报" && item.target_family == "G8" && item.validation_result == "已重分组" }); assert!(has_g8_boundary); let g2_validated = snapshot .validated_entries .iter() .filter(|item| item.target_family == "G2") .count(); assert_eq!(g2_validated, 3); let g1e_fail_closed = snapshot .validated_entries .iter() .filter(|item| { item.target_family.contains("G1-E") && item.validation_result == "Fail-closed" }) .count(); assert_eq!(g1e_fail_closed, 2); } fn load_snapshot() -> SceneLedgerSnapshot { serde_json::from_str( &fs::read_to_string("tests/fixtures/generated_scene/scene_ledger_snapshot_2026-04-18.json") .unwrap(), ) .unwrap() }