Files
claw/tests/post_roadmap_execution_assets_test.rs

607 lines
19 KiB
Rust

use std::fs;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct HandoverAsset {
#[serde(rename = "handoverDate")]
handover_date: String,
#[serde(rename = "closedRoadmapPlan")]
closed_roadmap_plan: String,
#[serde(rename = "postRoadmapPlan")]
post_roadmap_plan: String,
#[serde(rename = "roadmapClosureStatus")]
roadmap_closure_status: String,
#[serde(rename = "scopeStatement")]
scope_statement: String,
#[serde(rename = "mainlineFamilyStateMatrix")]
mainline_family_state_matrix: Vec<MainlineFamilyState>,
#[serde(rename = "boundaryFamilyStateMatrix")]
boundary_family_state_matrix: Vec<GroupState>,
#[serde(rename = "deferredFamilyStateMatrix")]
deferred_family_state_matrix: Vec<GroupState>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct MainlineFamilyState {
group: String,
status: String,
#[serde(rename = "representativeBaseline")]
representative_baseline: String,
#[serde(rename = "promotedExpansions")]
promoted_expansions: u32,
#[serde(rename = "candidateQueueCount")]
candidate_queue_count: u32,
}
#[derive(Debug, Deserialize)]
struct GroupState {
group: String,
status: String,
}
#[derive(Debug, Deserialize)]
struct SceneExecutionBoard {
#[serde(rename = "boardDate")]
board_date: String,
scope: String,
#[serde(rename = "sourceAssets")]
source_assets: SourceAssets,
#[serde(rename = "statusVocabulary")]
status_vocabulary: Vec<String>,
summary: ExecutionBoardSummary,
scenes: Vec<SceneBoardEntry>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct SourceAssets {
workbook: String,
#[serde(rename = "ledgerSnapshot")]
ledger_snapshot: String,
#[serde(rename = "ledgerStatusOverlay")]
ledger_status_overlay: String,
#[serde(rename = "roadmapExecutionStatus")]
roadmap_execution_status: String,
}
#[derive(Debug, Deserialize)]
struct ExecutionBoardSummary {
#[serde(rename = "totalScenes")]
total_scenes: u32,
#[serde(rename = "statusCounts")]
status_counts: serde_json::Map<String, serde_json::Value>,
#[serde(rename = "selectedRealSamples")]
selected_real_samples: u32,
#[serde(rename = "executedRealSamples")]
executed_real_samples: u32,
#[serde(rename = "pendingRealSamples")]
pending_real_samples: u32,
}
#[derive(Debug, Deserialize)]
struct SceneBoardEntry {
#[serde(rename = "sceneName")]
scene_name: String,
#[serde(rename = "snapshotGroupingResult")]
snapshot_grouping_result: String,
#[serde(rename = "snapshotFamilyJudgement")]
snapshot_family_judgement: String,
#[serde(rename = "hasExplicitValidationConclusion")]
has_explicit_validation_conclusion: String,
#[serde(rename = "snapshotValidationStatus")]
snapshot_validation_status: String,
#[serde(rename = "snapshotValidationResult")]
snapshot_validation_result: String,
#[serde(rename = "snapshotNote")]
snapshot_note: String,
#[serde(rename = "currentGroup")]
current_group: Option<String>,
#[serde(rename = "currentStatus")]
current_status: String,
#[serde(rename = "currentSourceAsset")]
current_source_asset: Option<String>,
#[serde(rename = "realSampleRecordId")]
real_sample_record_id: Option<String>,
#[serde(rename = "realSampleLayerStatus")]
real_sample_layer_status: String,
}
#[derive(Debug, Deserialize)]
struct RealSampleValidationPlan {
#[serde(rename = "planDate")]
plan_date: String,
scope: String,
criteria: Vec<String>,
#[serde(rename = "selectedFamilies")]
selected_families: Vec<SelectedFamily>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct SelectedFamily {
group: String,
#[serde(rename = "recordId")]
record_id: String,
#[serde(rename = "sceneName")]
scene_name: String,
#[serde(rename = "currentBoardStatus")]
current_board_status: String,
#[serde(rename = "sourceEvidence")]
source_evidence: String,
#[serde(rename = "selectionReason")]
selection_reason: String,
}
#[derive(Debug, Deserialize)]
struct RealSampleValidationTemplate {
#[serde(rename = "templateDate")]
template_date: String,
scope: String,
#[serde(rename = "requiredFields")]
required_fields: Vec<String>,
#[serde(rename = "allowedValidationStates")]
allowed_validation_states: Vec<String>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct RealSampleValidationRecords {
#[serde(rename = "recordDate")]
record_date: String,
scope: String,
records: Vec<RealSampleValidationRecord>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct RealSampleValidationRecord {
#[serde(rename = "recordId")]
record_id: String,
group: String,
#[serde(rename = "sceneName")]
scene_name: String,
#[serde(rename = "currentBoardStatus")]
current_board_status: String,
#[serde(rename = "validationState")]
validation_state: String,
#[serde(rename = "compileSuccess")]
compile_success: Option<bool>,
#[serde(rename = "readinessCorrectness")]
readiness_correctness: Option<bool>,
#[serde(rename = "dataCorrectness")]
data_correctness: Option<bool>,
#[serde(rename = "outputCorrectness")]
output_correctness: Option<bool>,
#[serde(rename = "failClosedCorrectness")]
fail_closed_correctness: Option<bool>,
result: String,
#[serde(rename = "mismatchCodes")]
mismatch_codes: Vec<String>,
#[serde(rename = "sourceEvidence")]
source_evidence: Vec<String>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct MismatchTaxonomy {
#[serde(rename = "taxonomyDate")]
taxonomy_date: String,
scope: String,
codes: Vec<MismatchCode>,
}
#[derive(Debug, Deserialize)]
struct MismatchCode {
code: String,
category: String,
description: String,
}
#[derive(Debug, Deserialize)]
struct BoundaryRuntimeEntryRules {
#[serde(rename = "assetDate")]
asset_date: String,
scope: String,
#[serde(rename = "boundaryReadiness")]
boundary_readiness: Vec<BoundaryReadiness>,
#[serde(rename = "deferredFamilyEntryCriteria")]
deferred_family_entry_criteria: Vec<DeferredFamilyEntryCriteria>,
#[serde(rename = "runtimeGapMatrix")]
runtime_gap_matrix: Vec<RuntimeGap>,
prioritization: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct BoundaryReadiness {
group: String,
status: String,
readiness: String,
#[serde(rename = "nextEntryCondition")]
next_entry_condition: String,
}
#[derive(Debug, Deserialize)]
struct DeferredFamilyEntryCriteria {
group: String,
#[serde(rename = "currentStatus")]
current_status: String,
#[serde(rename = "entryCriteria")]
entry_criteria: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct RuntimeGap {
gap: String,
category: String,
status: String,
blocks: Vec<String>,
}
#[test]
fn post_roadmap_handover_asset_is_actionable() {
let handover = load_handover();
assert_eq!(handover.handover_date, "2026-04-18");
assert!(handover.closed_roadmap_plan.ends_with(".md"));
assert!(handover.post_roadmap_plan.ends_with(".md"));
assert_eq!(handover.roadmap_closure_status, "completed");
assert!(handover.scope_statement.contains("roadmap is closed"));
assert_eq!(handover.mainline_family_state_matrix.len(), 3);
assert_eq!(handover.boundary_family_state_matrix.len(), 3);
assert_eq!(handover.deferred_family_state_matrix.len(), 2);
assert!(!handover.notes.is_empty());
}
#[test]
fn post_roadmap_execution_board_stays_minimal_and_validation_oriented() {
let board = load_execution_board();
assert_eq!(board.board_date, "2026-04-19");
assert_eq!(board.scope, "post-roadmap-minimum-current-execution-board");
assert!(board.source_assets.workbook.ends_with(".xlsx"));
assert!(board.source_assets.ledger_snapshot.ends_with(".json"));
assert!(board.source_assets.ledger_status_overlay.ends_with(".json"));
assert!(board
.source_assets
.roadmap_execution_status
.ends_with(".json"));
assert_eq!(board.status_vocabulary.len(), 6);
assert_eq!(board.summary.total_scenes, 102);
assert_eq!(board.summary.selected_real_samples, 5);
assert_eq!(board.summary.executed_real_samples, 5);
assert_eq!(board.summary.pending_real_samples, 0);
assert_eq!(
board
.summary
.status_counts
.get("promoted-baseline")
.and_then(|value| value.as_u64()),
Some(3)
);
assert_eq!(
board
.summary
.status_counts
.get("boundary-family")
.and_then(|value| value.as_u64()),
Some(3)
);
assert_eq!(
board
.summary
.status_counts
.get("unvalidated")
.and_then(|value| value.as_u64()),
Some(79)
);
assert!(!board.notes.is_empty());
}
#[test]
fn post_roadmap_execution_board_maps_real_sample_records_immediately() {
let board = load_execution_board();
let g2 = board
.scenes
.iter()
.find(|item| item.scene_name == "台区线损大数据-月_周累计线损率统计分析")
.expect("expected G2 board entry");
assert_eq!(g2.current_group.as_deref(), Some("G2"));
assert_eq!(g2.current_status, "promoted-baseline");
assert_eq!(g2.real_sample_record_id.as_deref(), Some("rsv-g2-001"));
assert_eq!(g2.real_sample_layer_status, "executed-pass");
let g1e = board
.scenes
.iter()
.find(|item| item.scene_name == "高低压新增报装容量月度统计表")
.expect("expected G1-E board entry");
assert_eq!(g1e.current_group.as_deref(), Some("G1-E"));
assert_eq!(g1e.current_status, "promoted-baseline");
assert_eq!(g1e.real_sample_record_id.as_deref(), Some("rsv-g1e-001"));
assert_eq!(g1e.real_sample_layer_status, "executed-pass");
let g3 = board
.scenes
.iter()
.find(|item| item.scene_name == "95598工单明细表")
.expect("expected G3 board entry");
assert_eq!(g3.current_group.as_deref(), Some("G3"));
assert_eq!(g3.current_status, "promoted-baseline");
assert_eq!(g3.real_sample_record_id.as_deref(), Some("rsv-g3-001"));
assert_eq!(g3.real_sample_layer_status, "executed-pass");
let g7 = board
.scenes
.iter()
.find(|item| item.current_group.as_deref() == Some("G7"))
.expect("expected G7 board entry");
assert_eq!(g7.current_group.as_deref(), Some("G7"));
assert_eq!(g7.current_status, "boundary-family");
assert_eq!(g7.real_sample_record_id.as_deref(), Some("rsv-g7-001"));
assert_eq!(g7.real_sample_layer_status, "executed-pass");
let g6 = board
.scenes
.iter()
.find(|item| item.current_group.as_deref() == Some("G6"))
.expect("expected G6 board entry");
assert_eq!(g6.current_group.as_deref(), Some("G6"));
assert_eq!(g6.current_status, "boundary-family");
assert_eq!(g6.real_sample_record_id.as_deref(), Some("rsv-g6-001"));
assert_eq!(g6.real_sample_layer_status, "executed-pass");
}
#[test]
fn post_roadmap_real_sample_validation_assets_are_consistent() {
let plan = load_validation_plan();
let template = load_validation_template();
let records = load_validation_records();
let taxonomy = load_mismatch_taxonomy();
assert_eq!(plan.plan_date, "2026-04-18");
assert_eq!(
plan.scope,
"post-roadmap-first-round-real-sample-validation"
);
assert_eq!(plan.criteria.len(), 5);
assert_eq!(plan.selected_families.len(), 3);
assert!(plan.selected_families.iter().any(|item| item.group == "G2"));
assert!(plan
.selected_families
.iter()
.any(|item| item.group == "G1-E"));
assert!(plan.selected_families.iter().any(|item| item.group == "G3"));
assert!(!plan.notes.is_empty());
assert_eq!(template.template_date, "2026-04-18");
assert_eq!(template.scope, "real-sample-validation-record-template");
assert!(template
.required_fields
.iter()
.any(|item| item == "recordId"));
assert!(template
.allowed_validation_states
.iter()
.any(|item| item == "selected-not-yet-run"));
assert!(!template.notes.is_empty());
assert_eq!(records.record_date, "2026-04-19");
assert_eq!(
records.scope,
"post-roadmap-first-round-real-sample-records"
);
assert_eq!(records.records.len(), 5);
assert!(!records.notes.is_empty());
assert_eq!(taxonomy.taxonomy_date, "2026-04-18");
assert_eq!(taxonomy.scope, "post-roadmap-real-sample-mismatch-taxonomy");
assert!(taxonomy
.codes
.iter()
.any(|item| item.code == "archetype_mismatch"));
assert!(taxonomy
.codes
.iter()
.any(|item| item.code == "selected_not_run"));
let g2 = records
.records
.iter()
.find(|item| item.record_id == "rsv-g2-001")
.expect("expected G2 record");
assert_eq!(g2.group, "G2");
assert_eq!(g2.validation_state, "executed-pass");
assert_eq!(g2.compile_success, Some(true));
assert_eq!(g2.result, "passed");
assert_eq!(g2.readiness_correctness, Some(true));
assert_eq!(g2.data_correctness, Some(true));
assert_eq!(g2.output_correctness, Some(true));
assert_eq!(g2.fail_closed_correctness, Some(true));
assert!(g2.mismatch_codes.is_empty());
let g1e = records
.records
.iter()
.find(|item| item.record_id == "rsv-g1e-001")
.expect("expected G1-E record");
assert_eq!(g1e.group, "G1-E");
assert_eq!(g1e.validation_state, "executed-pass");
assert_eq!(g1e.compile_success, Some(true));
assert_eq!(g1e.result, "passed");
assert!(g1e.mismatch_codes.is_empty());
let g3 = records
.records
.iter()
.find(|item| item.record_id == "rsv-g3-001")
.expect("expected G3 record");
assert_eq!(g3.group, "G3");
assert_eq!(g3.validation_state, "executed-pass");
assert_eq!(g3.compile_success, Some(true));
assert_eq!(g3.result, "passed");
assert_eq!(g3.data_correctness, Some(true));
assert_eq!(g3.output_correctness, Some(true));
assert!(g3.mismatch_codes.is_empty());
let g7 = records
.records
.iter()
.find(|item| item.record_id == "rsv-g7-001")
.expect("expected G7 record");
assert_eq!(g7.group, "G7");
assert_eq!(g7.validation_state, "executed-pass");
assert_eq!(g7.compile_success, Some(true));
assert_eq!(g7.result, "passed");
assert_eq!(g7.data_correctness, Some(true));
assert_eq!(g7.output_correctness, Some(true));
assert!(g7.mismatch_codes.is_empty());
let g6 = records
.records
.iter()
.find(|item| item.record_id == "rsv-g6-001")
.expect("expected G6 record");
assert_eq!(g6.group, "G6");
assert_eq!(g6.validation_state, "executed-pass");
assert_eq!(g6.compile_success, Some(true));
assert_eq!(g6.result, "passed");
assert_eq!(g6.data_correctness, Some(true));
assert_eq!(g6.output_correctness, Some(true));
assert!(g6.mismatch_codes.is_empty());
}
#[test]
fn post_roadmap_boundary_and_runtime_entry_rules_keep_scope_bounded() {
let rules = load_boundary_runtime_rules();
assert_eq!(rules.asset_date, "2026-04-19");
assert_eq!(rules.scope, "post-roadmap-boundary-and-runtime-entry-rules");
assert_eq!(rules.boundary_readiness.len(), 3);
assert!(rules.boundary_readiness.iter().all(|item| {
matches!(item.group.as_str(), "G6" | "G7" | "G8")
&& item.status == "boundary-family-established"
}));
assert!(rules
.boundary_readiness
.iter()
.any(|item| item.group == "G6" && item.readiness == "executed-pass"));
assert!(rules
.boundary_readiness
.iter()
.any(|item| item.group == "G8" && item.readiness == "hold-as-boundary"));
assert!(rules
.boundary_readiness
.iter()
.any(|item| item.group == "G7" && item.readiness == "executed-pass"));
assert_eq!(rules.deferred_family_entry_criteria.len(), 2);
assert!(rules
.deferred_family_entry_criteria
.iter()
.any(|item| item.group == "G4" && item.current_status == "deferred"));
assert!(rules
.deferred_family_entry_criteria
.iter()
.any(|item| item.group == "G5" && item.current_status == "degraded"));
assert_eq!(rules.runtime_gap_matrix.len(), 6);
assert!(rules
.runtime_gap_matrix
.iter()
.any(|item| item.gap == "login-recovery" && item.category == "runtime-platform-gap"));
assert!(rules
.runtime_gap_matrix
.iter()
.any(|item| item.gap == "host-runtime-integration"));
assert!(rules.runtime_gap_matrix.iter().any(|item| item.gap
== "g3-real-sample-output-contract-verification"
&& item.category == "mainline-contract-gap"
&& item.status == "closed-in-mainline"));
assert!(rules
.runtime_gap_matrix
.iter()
.any(|item| item.gap == "g2-real-sample-contract-correction"
&& item.category == "mainline-contract-gap"
&& item.status == "closed-in-mainline"));
assert!(!rules.prioritization.is_empty());
assert!(rules
.prioritization
.iter()
.any(|item| item.contains("Both G3 and G2")));
assert!(rules
.prioritization
.iter()
.any(|item| item.contains("G7 is now the first boundary family")));
assert!(rules
.prioritization
.iter()
.any(|item| item.contains("G6 is now the second boundary family")));
}
fn load_handover() -> HandoverAsset {
serde_json::from_str(
&fs::read_to_string("tests/fixtures/generated_scene/post_roadmap_handover_2026-04-18.json")
.unwrap(),
)
.unwrap()
}
fn load_execution_board() -> SceneExecutionBoard {
serde_json::from_str(
&fs::read_to_string("tests/fixtures/generated_scene/scene_execution_board_2026-04-18.json")
.unwrap(),
)
.unwrap()
}
fn load_validation_plan() -> RealSampleValidationPlan {
serde_json::from_str(
&fs::read_to_string(
"tests/fixtures/generated_scene/real_sample_validation_plan_2026-04-18.json",
)
.unwrap(),
)
.unwrap()
}
fn load_validation_template() -> RealSampleValidationTemplate {
serde_json::from_str(
&fs::read_to_string(
"tests/fixtures/generated_scene/real_sample_validation_record_template_2026-04-18.json",
)
.unwrap(),
)
.unwrap()
}
fn load_validation_records() -> RealSampleValidationRecords {
serde_json::from_str(
&fs::read_to_string(
"tests/fixtures/generated_scene/real_sample_validation_records_2026-04-18.json",
)
.unwrap(),
)
.unwrap()
}
fn load_mismatch_taxonomy() -> MismatchTaxonomy {
serde_json::from_str(
&fs::read_to_string(
"tests/fixtures/generated_scene/real_sample_mismatch_taxonomy_2026-04-18.json",
)
.unwrap(),
)
.unwrap()
}
fn load_boundary_runtime_rules() -> BoundaryRuntimeEntryRules {
serde_json::from_str(
&fs::read_to_string(
"tests/fixtures/generated_scene/boundary_runtime_entry_rules_2026-04-18.json",
)
.unwrap(),
)
.unwrap()
}