ferrotype 0.1.1

An opinionated wrapper for insta.rs
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

use ferrotype::Ferrotype;

#[cfg(feature = "bluegum")]
mod bluegum_tests {
  use super::*;

  // Simple test type that implements Bluegum
  #[derive(Debug)]
  struct TestNode {
    name:     String,
    children: Vec<TestNode>,
  }

  impl bluegum::Bluegum for TestNode {
    fn node(&self, builder: &mut bluegum::Builder) {
      builder.name(&self.name);
      for child in &self.children {
        builder.add_node(&format!("child_{}", child.name), child);
      }
    }
  }

  #[test]
  fn test_add_bluegum() {
    let mut snapshot = Ferrotype::new();
    let test_tree = TestNode {
      name:     "Root".to_string(),
      children: vec![
        TestNode {
          name:     "Child1".to_string(),
          children: vec![],
        },
        TestNode {
          name:     "Child2".to_string(),
          children: vec![],
        },
      ],
    };

    snapshot.add_bluegum("Test Tree", &test_tree);

    let output = snapshot.as_string();
    assert!(output.contains("Test Tree: >"));
    assert!(output.contains("Root"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_bluegum_builder() {
    let mut snapshot = Ferrotype::new();
    let mut builder = bluegum::Builder::new();
    builder.name("Test").field("child", "child");

    snapshot.add_bluegum_builder("Builder Test", builder);

    let output = snapshot.as_string();
    assert!(output.contains("Builder Test: >"));
    assert!(output.contains("Test"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_bluegum_builder_with() {
    let mut snapshot = Ferrotype::new();

    snapshot.add_bluegum_builder_with("Builder With Test", |b| {
      b.name("Dynamic").field("child", "dynamic child");
    });

    let output = snapshot.as_string();
    assert!(output.contains("Builder With Test: >"));
    assert!(output.contains("Dynamic"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_bluegum_with_custom_styles() {
    let mut snapshot = Ferrotype::new();
    let test_tree = TestNode {
      name:     "Styled".to_string(),
      children: vec![],
    };

    let custom_styles = bluegum::Styles {
      new_line_after_node: true,
      ..Default::default()
    };

    snapshot.add_bluegum_with("Styled Tree", &test_tree, custom_styles);

    let output = snapshot.as_string();
    assert!(output.contains("Styled Tree: >"));
    assert!(output.contains("Styled"));

    ferrotype::assert!(snapshot);
  }
}

#[cfg(feature = "tokenstream")]
mod tokenstream_tests {
  use super::*;

  #[test]
  fn test_add_token_stream_simple() {
    let mut snapshot = Ferrotype::new();
    let tokens: proc_macro2::TokenStream =
      "fn hello() { println!(\"Hello\"); }".parse().unwrap();

    snapshot.add_token_stream("Generated Code", &tokens);

    let output = snapshot.as_string();
    assert!(output.contains("Generated Code: >"));
    assert!(output.contains("fn hello"));
    assert!(output.contains("println!"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_token_stream_with_quote() {
    let mut snapshot = Ferrotype::new();

    // We can't directly use quote! in tests without it being a dependency,
    // but we can create a token stream programmatically
    let tokens: proc_macro2::TokenStream = r#"
      struct TestStruct {
        field: i32,
      }
      impl TestStruct {
        fn new(value: i32) -> Self {
          Self { field: value }
        }
      }
    "#
    .parse()
    .unwrap();

    snapshot.add_token_stream("Struct Definition", &tokens);

    let output = snapshot.as_string();
    assert!(output.contains("Struct Definition: >"));
    assert!(output.contains("struct TestStruct"));
    assert!(output.contains("field: i32"));
    assert!(output.contains("impl TestStruct"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  #[should_panic]
  fn test_add_token_stream_invalid_syntax() {
    let mut snapshot = Ferrotype::new();
    // This should panic because it's invalid Rust syntax for parsing
    let tokens: proc_macro2::TokenStream =
      "fn invalid_syntax { missing_parentheses".parse().unwrap();

    snapshot.add_token_stream("Invalid", &tokens);

    ferrotype::assert!(snapshot);
  }
}

#[cfg(feature = "anstream")]
mod anstream_tests {
  use super::*;

  #[test]
  fn test_add_strip_str_with_ansi_codes() {
    let mut snapshot = Ferrotype::new();
    let content_with_ansi =
      "\x1b[31mRed text\x1b[0m and \x1b[32mgreen text\x1b[0m";

    snapshot.add_strip_str("Colored Text", content_with_ansi.to_string());

    let output = snapshot.as_string();
    assert!(output.contains("Colored Text: >"));
    assert!(output.contains("Red text"));
    assert!(output.contains("green text"));
    // Should not contain ANSI escape codes
    assert!(!output.contains("\x1b[31m"));
    assert!(!output.contains("\x1b[32m"));
    assert!(!output.contains("\x1b[0m"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_strip_str_with_control_chars() {
    let mut snapshot = Ferrotype::new();
    let content_with_controls = "Text with\x08backspace and\x1b[2Jclear screen";

    snapshot.add_strip_str("Control Chars", content_with_controls.to_string());

    let output = snapshot.as_string();
    assert!(output.contains("Control Chars: >"));
    assert!(output.contains("Text with"));
    assert!(output.contains("backspace"));
    assert!(output.contains("clear screen"));
    // Should not contain control characters
    assert!(!output.contains("\x08"));
    assert!(!output.contains("\x1b[2J"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_strip_str_plain_text() {
    let mut snapshot = Ferrotype::new();
    let plain_text = "Just plain text with no special characters";

    snapshot.add_strip_str("Plain", plain_text.to_string());

    let output = snapshot.as_string();
    assert!(output.contains("Plain: >"));
    assert!(output.contains("Just plain text"));

    ferrotype::assert!(snapshot);
  }
}

#[cfg(feature = "hex")]
mod hex_tests {
  use super::*;

  #[test]
  fn test_add_hex_simple_data() {
    let mut snapshot = Ferrotype::new();
    let data = b"Hello, World!";

    snapshot.add_hex("Binary Data", data);

    let output = snapshot.as_string();
    assert!(output.contains("Binary Data: >"));
    // Should contain hex representation
    assert!(output.contains("48")); // 'H' in hex
    assert!(output.contains("65")); // 'e' in hex
                                    // Should contain ASCII representation
    assert!(output.contains("Hello"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_hex_binary_data() {
    let mut snapshot = Ferrotype::new();
    let binary_data = &[0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0xFC];

    snapshot.add_hex("Raw Binary", binary_data);

    let output = snapshot.as_string();
    assert!(output.contains("Raw Binary: >"));
    // Should contain hex values
    assert!(output.contains("00"));
    assert!(output.contains("01"));
    assert!(output.contains("ff"));
    assert!(output.contains("fe"));

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_hex_empty_data() {
    let mut snapshot = Ferrotype::new();
    let empty_data: &[u8] = &[];

    snapshot.add_hex("Empty", empty_data);

    let output = snapshot.as_string();
    assert!(output.contains("Empty: >"));
    // Should handle empty data gracefully

    ferrotype::assert!(snapshot);
  }

  #[test]
  fn test_add_hex_large_data() {
    let mut snapshot = Ferrotype::new();
    let large_data: Vec<u8> = (0..=255).collect();

    snapshot.add_hex("Large Data", &large_data);

    let output = snapshot.as_string();
    assert!(output.contains("Large Data: >"));
    // Should contain first and last bytes
    assert!(output.contains("00"));
    assert!(output.contains("ff"));

    ferrotype::assert!(snapshot);
  }
}

// Test features interaction
#[cfg(all(feature = "hex", feature = "anstream"))]
#[test]
fn test_multiple_features_together() {
  let mut snapshot = Ferrotype::new();

  // Add hex data
  snapshot.add_hex("Binary", b"test data");

  // Add stripped string
  snapshot.add_strip_str("Colored", "\x1b[31mRed\x1b[0m".to_string());

  let output = snapshot.as_string();
  assert!(output.contains("Binary: >"));
  assert!(output.contains("Colored: >"));
  assert!(output.contains("Red"));
  assert!(!output.contains("\x1b[31m"));

  ferrotype::assert!(snapshot);
}

// Test the Display trait with anstream feature
#[cfg(feature = "anstream")]
#[test]
fn test_display_with_anstream() {
  let mut snapshot = Ferrotype::new();
  snapshot.add("Test", "content with \x1b[31mcolor\x1b[0m".to_string());

  let display_output = format!("{snapshot}");

  // The Display implementation should strip ANSI codes when anstream is enabled
  assert!(display_output.contains("content with color"));
  assert!(!display_output.contains("\x1b[31m"));
  assert!(!display_output.contains("\x1b[0m"));

  ferrotype::assert!(snapshot);
}