graphs-tui 0.2.0

Terminal renderer for Mermaid and D2 diagrams - flowcharts, state diagrams, pie charts in Unicode/ASCII
Documentation
//! Tests for GitHub issues #3, #4, #5, #7
use graphs_tui::{render_d2_to_tui, render_mermaid_to_tui, render_timeline, RenderOptions};

/// Issue #3: Timeline should be supported
/// Test the exact example from the issue
#[test]
fn test_issue_3_timeline_pied_piper() {
    let input = r#"timeline
    title Pied Piper Journey (2014-2019)
    2014 : DEC-001 Peter Gregory Seed Funding
         : DEC-002 Anti-Hooli Principle ⭐
         : ADR-001 Middle-Out Algorithm ⭐
         : INC-001 Hooli IP Lawsuit
    2015 : DEC-004 Hanneman Bridge Funding
         : ADR-002 Enterprise Platform
         : ADR-003 The Box
    2016 : INC-002 COPPA Violation
    2018 : POL-001 Tethics Framework ⭐
         : ADR-004 PiperNet Architecture
         : INC-003 51% Attack
    2019 : INC-004 AI Encryption Discovery
         : DEC-005 Sabotage Launch
"#;
    let result = render_timeline(input, RenderOptions::default()).unwrap();
    println!("Issue #3 Timeline output:\n{}", result.output);

    // Verify key elements
    assert!(result.output.contains("Pied Piper Journey"));
    assert!(result.output.contains("2014"));
    assert!(result.output.contains("2015"));
    assert!(result.output.contains("2016"));
    assert!(result.output.contains("2018"));
    assert!(result.output.contains("2019"));
    assert!(result.output.contains("Peter Gregory Seed Funding"));
    assert!(result.output.contains("Middle-Out Algorithm"));
    assert!(result.output.contains("Tethics Framework"));
    assert!(result.output.contains("Sabotage Launch"));
}

/// Issue #4: Cyclical D2 diagrams (bidirectional arrows)
#[test]
fn test_issue_4_cyclical_d2_simple() {
    let input = r#"
A <-> B: P2P
B <-> C: P2P
C <-> A: P2P
"#;
    let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
    println!("Issue #4 Cyclical D2 output:\n{}", result.output);

    // All nodes should be rendered
    assert!(result.output.contains("A"));
    assert!(result.output.contains("B"));
    assert!(result.output.contains("C"));
    // Edge labels should not be corrupted
    assert!(result.output.contains("P2P"));
}

/// Issue #4: Cyclical D2 with containers (from the issue)
#[test]
fn test_issue_4_cyclical_d2_with_containers() {
    let input = r#"direction: down

mesh: PiperNet Mesh {
  phone1: Phone Node
  laptop: Laptop Node
  iot: IoT Node
  phone2: Phone Node

  phone1 <-> laptop: P2P
  laptop <-> iot: P2P
  iot <-> phone2: P2P
  phone2 <-> phone1: P2P
}

ledger: Distributed Ledger

mesh -> ledger: Shard metadata
"#;
    let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
    println!("Issue #4 Cyclical D2 with containers:\n{}", result.output);

    // Verify nodes
    assert!(result.output.contains("Phone Node") || result.output.contains("phone1"));
    assert!(result.output.contains("Laptop Node") || result.output.contains("laptop"));
}

/// Issue #5: Edge labels should not be truncated/corrupted
#[test]
fn test_issue_5_edge_labels_not_truncated() {
    let input = r#"
phone1: Phone Node
laptop: Laptop Node
iot: IoT Node
phone2: Phone Node

phone1 <-> laptop: P2P
laptop <-> iot: P2P
iot <-> phone2: P2P
phone2 <-> phone1: P2P
"#;
    let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
    println!("Issue #5 Edge labels output:\n{}", result.output);

    // The issue says "P2P" is rendered as "P2┌" with a box-drawing character
    // Edge labels should be complete, not truncated
    // Check that we don't have truncated labels like "P2" without the full "P2P"
    let p2p_count = result.output.matches("P2P").count();
    println!("P2P count: {}", p2p_count);
    
    // At least some P2P labels should be visible
    // (some may be hidden due to space constraints, but they shouldn't be corrupted)
    assert!(p2p_count >= 1, "Expected at least one P2P label, found {}", p2p_count);
}

/// Issue #5: Verify edge labels don't contain corrupted box-drawing characters
#[test]
fn test_issue_5_no_corrupted_edge_labels() {
    let input = r#"
A: Phone Node
B: Laptop Node

A <-> B: P2P
"#;
    let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
    println!("Issue #5 Simple edge label:\n{}", result.output);

    // Check the label is complete
    assert!(result.output.contains("P2P"), "Edge label 'P2P' should be visible");
    
    // The bug was that characters get replaced with box-drawing chars
    // "P2P" should not appear as "P2┌" or similar
    let lines: Vec<&str> = result.output.lines().collect();
    for line in &lines {
        // If we see "P2" it should be followed by "P" not a box char
        if line.contains("P2") && !line.contains("P2P") {
            panic!("Found truncated label 'P2' without full 'P2P' in line: {}", line);
        }
    }
}

/// Issue #7: Warnings returned in RenderResult instead of eprintln
#[test]
fn test_issue_7_cycle_warning_in_result() {
    // Cyclical graph: A -> B -> C -> A
    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].contains("Cycle"),
        "Warning should mention cycle"
    );
    // Output should still render
    assert!(result.output.contains("A"));
    assert!(result.output.contains("B"));
    assert!(result.output.contains("C"));
}

/// Issue #7: Non-cyclic graphs produce no warnings
#[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");
}

/// Issue #7: D2 cyclic graph also produces warning
#[test]
fn test_issue_7_d2_cycle_warning() {
    let input = r#"
A <-> B: link
B <-> C: link
C <-> A: link
"#;
    let result = render_d2_to_tui(input, RenderOptions::default()).unwrap();
    assert!(!result.warnings.is_empty(), "D2 cycle should produce a warning");
}