errcraft 0.1.0

Beautiful, structured, and colorful error handling for Rust.
Documentation
//! Integration tests for errcraft

use errcraft::{ErrFrame, ErrResult};

#[test]
fn test_basic_error_creation() {
    let err = ErrFrame::new("Test error");
    assert_eq!(err.message(), "Test error");
}

#[test]
fn test_error_with_context() {
    let err = ErrFrame::new("Database error")
        .context("table", "users")
        .context("id", 42);

    assert_eq!(err.message(), "Database error");

    let context_vec: Vec<_> = err.context_layers().collect();
    assert_eq!(context_vec.len(), 2);
}

#[test]
fn test_error_with_text_context() {
    let err = ErrFrame::new("Upload failed").context_text("File too large");

    let context_vec: Vec<_> = err.context_layers().collect();
    assert_eq!(context_vec.len(), 1);
    assert!(context_vec[0].key().is_none());
}

#[test]
fn test_error_with_source() {
    let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
    let err = ErrFrame::new("Failed to read file").with_source(io_err);

    assert_eq!(err.message(), "Failed to read file");
    assert!(std::error::Error::source(&err).is_some());
}

#[test]
fn test_error_chaining() {
    let inner = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
    let middle = ErrFrame::new("Could not write file").with_source(inner);
    let outer = ErrFrame::new("Configuration update failed").with_source(middle);

    assert_eq!(outer.message(), "Configuration update failed");
}

#[test]
fn test_display_impl() {
    let err = ErrFrame::new("Test error").context("key", "value");

    let display = format!("{}", err);
    assert!(display.contains("Test error"));
    assert!(display.contains("key=value"));
}

#[test]
fn test_craft_macro() {
    let err = errcraft::craft!("Error: {}", 404);
    assert_eq!(err.message(), "Error: 404");
}

#[test]
fn test_bail_macro() {
    fn failing_function() -> ErrResult<()> {
        errcraft::bail!("Something went wrong");
    }

    let result = failing_function();
    assert!(result.is_err());
    assert_eq!(result.unwrap_err().message(), "Something went wrong");
}

#[test]
fn test_ensure_macro() {
    fn check_value(x: i32) -> ErrResult<()> {
        errcraft::ensure!(x > 0, "Value must be positive");
        Ok(())
    }

    assert!(check_value(5).is_ok());
    assert!(check_value(-1).is_err());
}

#[test]
fn test_from_std_error() {
    let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "not found");
    let err = ErrFrame::new("IO error occurred").with_source(io_err);

    assert!(err.message().contains("IO error"));
}

#[test]
fn test_display_options() {
    use errcraft::{ColorMode, DisplayOptions};

    let opts = DisplayOptions::new()
        .with_emoji(true)
        .with_color(ColorMode::Never)
        .with_max_depth(Some(3));

    assert!(opts.emoji);
    assert_eq!(opts.color, ColorMode::Never);
    assert_eq!(opts.max_depth, Some(3));
}

#[test]
fn test_styled_output() {
    use errcraft::DisplayOptions;

    let err = ErrFrame::new("Test error").context("key", "value");

    let opts = DisplayOptions::new();
    let output = err.to_string_styled(&opts);

    assert!(output.contains("Error:"));
    assert!(output.contains("Test error"));
}

#[test]
fn test_backtrace_capture() {
    let err = ErrFrame::new("Test with backtrace").capture_backtrace();

    // Just ensure it doesn't panic
    assert_eq!(err.message(), "Test with backtrace");
}

#[cfg(feature = "serde")]
#[test]
fn test_json_serialization() {
    let err = ErrFrame::new("JSON test").context("key", "value");

    let json = err.to_json();
    assert!(json["message"].as_str().unwrap().contains("JSON test"));

    let json_str = err.to_json_string();
    assert!(json_str.contains("JSON test"));
}

#[cfg(feature = "markdown")]
#[test]
fn test_markdown_output() {
    let err = ErrFrame::new("Markdown test").context("key", "value");

    let md = err.to_markdown();
    assert!(md.contains("# Error"));
    assert!(md.contains("Markdown test"));
}

#[cfg(feature = "anyhow")]
#[test]
fn test_anyhow_conversion() {
    let anyhow_err = anyhow::anyhow!("anyhow error");
    let err: ErrFrame = anyhow_err.into();

    assert!(err.message().contains("anyhow error"));
}

#[test]
fn test_multiple_context_layers() {
    let err = ErrFrame::new("Multi-context error")
        .context("layer1", "value1")
        .context("layer2", "value2")
        .context("layer3", "value3")
        .context_text("Additional info");

    let layers: Vec<_> = err.context_layers().collect();
    assert_eq!(layers.len(), 4);
}

#[test]
fn test_nested_error_source_chain() {
    let e1 = std::io::Error::new(std::io::ErrorKind::NotFound, "base error");
    let e2 = ErrFrame::new("level 2").with_source(e1);
    let e3 = ErrFrame::new("level 3").with_source(e2);

    assert_eq!(e3.message(), "level 3");

    let source = std::error::Error::source(&e3);
    assert!(source.is_some());
}