Files
claw/tests/boundary_family_entry_roadmap_test.rs

123 lines
3.5 KiB
Rust

use std::fs;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct BoundaryFamilyEntryDecision {
#[serde(rename = "decisionDate")]
decision_date: String,
scope: String,
#[serde(rename = "startingState")]
starting_state: StartingState,
#[serde(rename = "comparisonMatrix")]
comparison_matrix: Vec<ComparisonEntry>,
#[serde(rename = "selectedCandidate")]
selected_candidate: SelectedCandidate,
#[serde(rename = "holdReasons")]
hold_reasons: Vec<String>,
notes: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct StartingState {
#[serde(rename = "mainlineClosed")]
mainline_closed: Vec<String>,
#[serde(rename = "boundaryHeld")]
boundary_held: Vec<String>,
#[serde(rename = "deferredOutOfScope")]
deferred_out_of_scope: Vec<String>,
}
#[derive(Debug, Deserialize)]
struct ComparisonEntry {
group: String,
#[serde(rename = "representativeScene")]
representative_scene: String,
#[serde(rename = "entryCondition")]
entry_condition: String,
#[serde(rename = "smallestNewCapability")]
smallest_new_capability: String,
#[serde(rename = "entryCost")]
entry_cost: String,
decision: String,
reason: String,
}
#[derive(Debug, Deserialize)]
struct SelectedCandidate {
group: String,
#[serde(rename = "representativeScene")]
representative_scene: String,
#[serde(rename = "nextDesign")]
next_design: String,
#[serde(rename = "nextPlan")]
next_plan: String,
}
#[test]
fn boundary_family_entry_roadmap_selects_one_next_candidate() {
let decision: BoundaryFamilyEntryDecision = serde_json::from_str(
&fs::read_to_string(
"tests/fixtures/generated_scene/boundary_family_entry_decision_2026-04-19.json",
)
.unwrap(),
)
.unwrap();
assert_eq!(decision.decision_date, "2026-04-19");
assert_eq!(decision.scope, "boundary-family-real-sample-entry-roadmap");
assert_eq!(
decision.starting_state.mainline_closed,
vec!["G1-E", "G2", "G3"]
);
assert_eq!(
decision.starting_state.boundary_held,
vec!["G6", "G7", "G8"]
);
assert_eq!(
decision.starting_state.deferred_out_of_scope,
vec!["G4", "G5"]
);
assert_eq!(decision.comparison_matrix.len(), 3);
assert_eq!(
decision
.comparison_matrix
.iter()
.filter(|entry| entry.decision == "selected")
.count(),
1
);
assert!(decision
.comparison_matrix
.iter()
.any(|entry| entry.group == "G7"
&& entry.decision == "selected"
&& entry.entry_cost == "medium"));
assert!(decision
.comparison_matrix
.iter()
.any(|entry| entry.group == "G6"
&& entry.decision == "hold"
&& entry.entry_cost == "high"));
assert!(decision
.comparison_matrix
.iter()
.any(|entry| entry.group == "G8"
&& entry.decision == "hold"
&& entry.entry_cost == "high"));
let selected = &decision.selected_candidate;
assert_eq!(selected.group, "G7");
assert_eq!(selected.representative_scene, "计量资产库存统计");
assert!(selected
.next_design
.ends_with("2026-04-19-g7-real-sample-entry-design.md"));
assert!(selected
.next_plan
.ends_with("2026-04-19-g7-real-sample-entry-plan.md"));
assert_eq!(decision.hold_reasons.len(), 2);
assert!(!decision.notes.is_empty());
}