use std::{error::Error, path::Path};
use expect_test::expect;
use super::*;
fn diagram(name: &str, qualified_name: &str) -> StateMachineDiagram {
StateMachineDiagram {
name: name.to_owned(),
qualified_name: qualified_name.to_owned(),
steps: vec![StepDiagram {
input: "Advance".to_owned(),
outcome: DiagramNode {
label: "Advanced".to_owned(),
children: vec![DiagramNode {
label: "Done".to_owned(),
children: Vec::new(),
}],
},
}],
}
}
fn fixture() -> Result<CrateArchitecture, ArchitectureError> {
CrateArchitecture::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/door"))
}
#[test]
fn derives_every_step_and_outcome_from_the_crate() -> Result<(), Box<dyn Error>> {
let architecture = fixture()?;
let diagram = architecture.state_machine("Door")?;
assert_eq!(architecture.state_machines().len(), 1);
expect![[r#"
● state machine ▸ step input ◆ outcome
● Door
├── ▸ CloseDoor
│ └── ◆ CloseDoorOutcome
│ ├── Closed
│ └── AlreadyClosed
└── ▸ OpenDoor
└── ◆ Result
├── Ok
│ └── OpenDoorOutcome
│ ├── Opened
│ └── AlreadyOpen
└── Err
└── DoorFaultReason
├── Jammed
└── Locked
"#]]
.assert_eq(&diagram.render(DiagramTarget::TextTree));
Ok(())
}
#[test]
fn projects_one_graph_to_mermaid_and_graphviz() -> Result<(), Box<dyn Error>> {
let architecture = fixture()?;
let diagram = architecture.state_machine_qualified("crate::door::Door")?;
expect![[r#"
flowchart LR
n0["Door"]
n1["CloseDoor"]
n2["CloseDoorOutcome"]
n3["Closed"]
n4["AlreadyClosed"]
n5["OpenDoor"]
n6["Result"]
n7["Ok"]
n8["OpenDoorOutcome"]
n9["Opened"]
n10["AlreadyOpen"]
n11["Err"]
n12["DoorFaultReason"]
n13["Jammed"]
n14["Locked"]
n0 --> n1
n1 --> n2
n2 --> n3
n2 --> n4
n0 --> n5
n5 --> n6
n6 --> n7
n7 --> n8
n8 --> n9
n8 --> n10
n6 --> n11
n11 --> n12
n12 --> n13
n12 --> n14
"#]]
.assert_eq(&diagram.render(DiagramTarget::MermaidFlowchart));
expect![[r#"
digraph "Door" {
rankdir=LR;
n0 [label="Door"];
n1 [label="CloseDoor"];
n2 [label="CloseDoorOutcome"];
n3 [label="Closed"];
n4 [label="AlreadyClosed"];
n5 [label="OpenDoor"];
n6 [label="Result"];
n7 [label="Ok"];
n8 [label="OpenDoorOutcome"];
n9 [label="Opened"];
n10 [label="AlreadyOpen"];
n11 [label="Err"];
n12 [label="DoorFaultReason"];
n13 [label="Jammed"];
n14 [label="Locked"];
n0 -> n1;
n1 -> n2;
n2 -> n3;
n2 -> n4;
n0 -> n5;
n5 -> n6;
n6 -> n7;
n7 -> n8;
n8 -> n9;
n8 -> n10;
n6 -> n11;
n11 -> n12;
n12 -> n13;
n12 -> n14;
}
"#]]
.assert_eq(&diagram.render(DiagramTarget::GraphvizDot));
Ok(())
}
#[test]
fn ansi_text_colors_only_role_markers() {
let rendering = diagram("Clock", "crate::clock::Clock").render(DiagramTarget::AnsiTextTree);
assert!(rendering.contains("\u{1b}[38;5;114m●\u{1b}[0m Clock"));
assert!(rendering.contains("\u{1b}[38;5;114m▸\u{1b}[0m Advance"));
assert!(rendering.contains("\u{1b}[38;5;114m◆\u{1b}[0m Advanced"));
assert!(!rendering.contains("\u{1b}[38;5;114mClock"));
assert!(!rendering.contains("\u{1b}[38;5;114mAdvance"));
assert!(!rendering.contains("\u{1b}[38;5;114mAdvanced"));
}
#[test]
fn exposes_diagram_structure() {
let diagram = diagram("Clock", "crate::clock::Clock");
assert_eq!(diagram.name(), "Clock");
assert_eq!(diagram.qualified_name(), "crate::clock::Clock");
assert_eq!(diagram.steps().len(), 1);
for step in diagram.steps() {
assert_eq!(step.input(), "Advance");
assert_eq!(step.outcome().label(), "Advanced");
assert_eq!(step.outcome().children().len(), 1);
for child in step.outcome().children() {
assert_eq!(child.label(), "Done");
}
}
}
#[test]
fn state_machine_lookup_preserves_absence_and_ambiguity() {
let architecture = CrateArchitecture {
state_machines: vec![
diagram("Clock", "crate::east::Clock"),
diagram("Clock", "crate::west::Clock"),
],
};
assert_eq!(architecture.state_machines().len(), 2);
assert_eq!(
architecture
.state_machine("Missing")
.map(|_| ())
.map_err(|error| error.to_string()),
Err("state machine `Missing` was not found".to_owned())
);
assert_eq!(
architecture
.state_machine("Clock")
.map(|_| ())
.map_err(|error| error.to_string()),
Err("state machine name `Clock` was ambiguous; use its qualified name".to_owned())
);
assert_eq!(
architecture
.state_machine_qualified("crate::east::Clock")
.map(StateMachineDiagram::qualified_name),
Ok("crate::east::Clock")
);
assert_eq!(
architecture
.state_machine_qualified("crate::north::Clock")
.map(|_| ())
.map_err(|error| error.to_string()),
Err("state machine `crate::north::Clock` was not found".to_owned())
);
}