use graphs_tui::{
check, is_supported, render, render_d2_to_tui, render_mermaid_to_tui, DiagramWarning,
RenderOptions, SUPPORTED_LANGUAGES,
};
#[test]
fn test_issue_7_cycle_warning_in_result() {
let input = r#"flowchart LR
A --> B
B --> C
C --> A"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert!(
!result.warnings.is_empty(),
"Cycle should produce a warning"
);
assert!(
result.warnings[0].to_string().contains("Cycle"),
"Warning should mention cycle"
);
assert!(result.output.contains("A"));
assert!(result.output.contains("B"));
assert!(result.output.contains("C"));
}
#[test]
fn test_issue_7_no_warning_without_cycle() {
let input = "flowchart LR\nA --> B --> C";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert!(result.warnings.is_empty(), "No cycle means no warnings");
}
#[test]
fn test_issue_7_d2_cycle_warning() {
let input = r#"
A -> B
B -> C
C -> A
"#;
let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
assert!(
!result.warnings.is_empty(),
"D2 cycle should produce a warning"
);
}
#[test]
fn test_issue_7_pie_no_warnings() {
let input = r#"pie
"A" : 50
"B" : 50
"#;
let result = graphs_tui::render_pie_chart(input, RenderOptions::default()).unwrap();
assert!(result.warnings.is_empty());
}
#[test]
fn test_issue_8_cylinder_5_rows() {
let input = "flowchart LR\nDB[(Database)]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
let output = &result.output;
assert!(output.contains('├'), "Cylinder should have ├ T-junction");
assert!(output.contains('┤'), "Cylinder should have ┤ T-junction");
assert!(output.contains('╭'), "Cylinder should have ╭ top-left");
assert!(output.contains('╰'), "Cylinder should have ╰ bottom-left");
assert!(output.contains("Database"), "Label should be present");
let lines: Vec<&str> = output.lines().collect();
let top = lines.iter().position(|l| l.contains('╭')).unwrap();
let bot = lines.iter().position(|l| l.contains('╰')).unwrap();
assert_eq!(bot - top, 4, "Cylinder should span exactly 5 rows (0..4)");
assert!(lines[top + 1].contains('├'), "Row 1 should have ├");
assert!(lines[top + 3].contains('├'), "Row 3 should have ├");
}
#[test]
fn test_issue_8_edge_connects_cylinder_midpoint() {
let input = "flowchart LR\nA[Start] --> DB[(Database)]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
let output = &result.output;
let lines: Vec<&str> = output.lines().collect();
let arrow_line = lines
.iter()
.position(|l| l.contains('▶'))
.expect("Should have arrow");
let label_line = lines
.iter()
.position(|l| l.contains("Database"))
.expect("Should have Database label");
assert_eq!(
arrow_line, label_line,
"Arrow should connect at cylinder midpoint (same row as label)"
);
}
#[test]
fn test_issue_8_subgraph_borders_preserved() {
let input = r#"flowchart LR
subgraph sg1 [Group]
A[Node]
end"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
let output = &result.output;
assert!(
output.contains('║'),
"Subgraph should have ║ vertical borders"
);
assert!(output.contains('╔'), "Subgraph should have ╔ corner");
assert!(output.contains('╗'), "Subgraph should have ╗ corner");
assert!(output.contains('╚'), "Subgraph should have ╚ corner");
assert!(output.contains('╝'), "Subgraph should have ╝ corner");
}
#[test]
fn test_issue_8_subgraph_labels_intact() {
let input = r#"flowchart TB
subgraph sg1 [My Group]
A[Node1]
B[Node2]
end
A --> B"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
let output = &result.output;
assert!(
output.contains("My Group"),
"Subgraph label should be intact"
);
}
#[test]
fn test_issue_9_deterministic_mermaid() {
let input = r#"flowchart LR
A[Start] --> B[Middle]
A --> C[Other]
B --> D[End]
C --> D"#;
let first = render_mermaid_to_tui(input, RenderOptions::default())
.unwrap()
.output;
for i in 1..20 {
let result = render_mermaid_to_tui(input, RenderOptions::default())
.unwrap()
.output;
assert_eq!(first, result, "Mermaid run {i} differs from first run");
}
}
#[test]
fn test_issue_9_deterministic_d2() {
let input = r#"
A -> B
A -> C
B -> D
C -> D
"#;
let first = render_d2_to_tui(input, RenderOptions::default())
.unwrap()
.output;
for i in 1..20 {
let result = render_d2_to_tui(input, RenderOptions::default())
.unwrap()
.output;
assert_eq!(first, result, "D2 run {i} differs from first run");
}
}
#[test]
fn test_issue_9_deterministic_d2_with_cycle() {
let input = r#"
users: Users
api: Production API
pgbouncer: PgBouncer { shape: cylinder }
analytics: Analytics Query
users -> api: requests
api -> pgbouncer: need conn
analytics -> pgbouncer: 60 conns held
api -> users: 503 errors
"#;
let first = render_d2_to_tui(input, RenderOptions::default())
.unwrap()
.output;
for i in 1..20 {
let result = render_d2_to_tui(input, RenderOptions::default())
.unwrap()
.output;
assert_eq!(first, result, "D2 cycle run {i} differs");
}
}
#[test]
fn test_issue_9_edge_label_legend() {
let input = "flowchart LR\nA -->|This is a very long label that will not fit| B";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert!(
result.output.contains("Labels:"),
"Legend section should appear when label is dropped"
);
assert!(
result
.output
.contains("This is a very long label that will not fit"),
"Legend should contain the dropped label text"
);
let has_label_warning = result
.warnings
.iter()
.any(|w| matches!(w, DiagramWarning::LabelDropped { .. }));
assert!(has_label_warning, "Should have a LabelDropped warning");
}
#[test]
fn test_issue_9_cycle_warning_nodes() {
let input = r#"flowchart LR
X --> Y
Y --> Z
Z --> X"#;
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert_eq!(result.warnings.len(), 1);
match &result.warnings[0] {
DiagramWarning::CycleDetected { nodes } => {
assert!(nodes.contains(&"X".to_string()), "Should contain X");
assert!(nodes.contains(&"Y".to_string()), "Should contain Y");
assert!(nodes.contains(&"Z".to_string()), "Should contain Z");
assert_eq!(nodes, &["X", "Y", "Z"]);
}
other => panic!("Expected CycleDetected, got: {other:?}"),
}
}
#[test]
fn test_issue_9_warning_display() {
let w = DiagramWarning::CycleDetected {
nodes: vec!["A".into(), "B".into()],
};
assert_eq!(w.to_string(), "Cycle detected involving nodes: A, B");
let w2 = DiagramWarning::LabelDropped {
marker: "[1]".into(),
edge_from: "X".into(),
edge_to: "Y".into(),
label: "my label".into(),
};
assert_eq!(
w2.to_string(),
"Label 'my label' on edge X -> Y moved to legend as [1]"
);
}
#[test]
fn test_issue_9_no_legend_when_labels_fit() {
let input = "flowchart LR\nA[Start] -->|yes| B[End]";
let result = render_mermaid_to_tui(input, RenderOptions::default()).unwrap();
assert!(
!result.output.contains("Labels:"),
"No legend when labels fit inline"
);
assert!(result.output.contains("yes"), "Label should appear inline");
}
#[test]
fn test_issue_12_supported_languages() {
assert!(SUPPORTED_LANGUAGES.contains(&"mermaid"));
assert!(SUPPORTED_LANGUAGES.contains(&"d2"));
}
#[test]
fn test_issue_12_is_supported() {
assert!(is_supported("mermaid"));
assert!(is_supported("Mermaid"));
assert!(is_supported("D2"));
assert!(is_supported("d2"));
assert!(!is_supported("graphviz"));
assert!(!is_supported(""));
}
#[test]
fn test_issue_11_render_d2() {
let result = render("d2", "A -> B", RenderOptions::default()).unwrap();
assert!(result.output.contains("A"));
assert!(result.output.contains("B"));
}
#[test]
fn test_issue_11_render_mermaid() {
let result = render(
"mermaid",
"flowchart LR\nA[Start] --> B[End]",
RenderOptions::default(),
)
.unwrap();
assert!(result.output.contains("Start"));
assert!(result.output.contains("End"));
}
#[test]
fn test_issue_11_render_mermaid_pie() {
let result = render(
"mermaid",
"pie\n \"A\" : 60\n \"B\" : 40",
RenderOptions::default(),
)
.unwrap();
assert!(result.output.contains("A"));
assert!(result.output.contains("60"));
}
#[test]
fn test_issue_11_render_case_insensitive() {
let result = render("D2", "X -> Y", RenderOptions::default()).unwrap();
assert!(result.output.contains("X"));
assert!(result.output.contains("Y"));
}
#[test]
fn test_issue_13_check_cycle() {
let warnings = check("mermaid", "flowchart LR\nA --> B\nB --> A").unwrap();
assert!(!warnings.is_empty(), "Should detect cycle");
assert!(
matches!(&warnings[0], DiagramWarning::CycleDetected { .. }),
"Should be CycleDetected"
);
}
#[test]
fn test_issue_13_check_no_warnings() {
let warnings = check("mermaid", "flowchart LR\nA --> B --> C").unwrap();
assert!(warnings.is_empty());
}
#[test]
fn test_issue_13_check_d2_cycle() {
let warnings = check("d2", "A -> B\nB -> A").unwrap();
assert!(!warnings.is_empty());
}
#[test]
fn test_issue_13_check_pie_valid() {
let warnings = check("mermaid", "pie\n \"A\" : 50\n \"B\" : 50").unwrap();
assert!(warnings.is_empty());
}
#[test]
fn test_issue_13_check_sequence_valid() {
let warnings = check("mermaid", "sequenceDiagram\n A->>B: Hi").unwrap();
assert!(warnings.is_empty());
}
#[test]
fn test_issue_13_check_invalid() {
let result = check("mermaid", "flowchart\n");
assert!(result.is_err(), "Should fail on invalid input");
}