fluxdi 1.2.1

FluxDI - Semi-Automatic Dependency Injector
Documentation
use fluxdi::{ErrorKind, GraphValidationIssueKind, Injector, Provider, Shared};

#[derive(Debug)]
struct ServiceA;
#[derive(Debug)]
struct ServiceB;

#[test]
fn dependency_graph_exports_dot_and_mermaid() {
    let injector = Injector::root();
    injector.provide::<ServiceA>(
        Provider::singleton(|_| Shared::new(ServiceA)).with_dependency::<ServiceB>(),
    );
    injector.provide::<ServiceB>(Provider::singleton(|_| Shared::new(ServiceB)));

    let graph = injector.dependency_graph();
    assert!(graph.nodes.len() >= 2);
    assert!(
        graph
            .edges
            .iter()
            .any(|edge| edge.from.contains("ServiceA") && edge.to.contains("ServiceB"))
    );

    let dot = graph.to_dot();
    assert!(dot.contains("digraph fluxdi"));
    assert!(dot.contains("ServiceA"));
    assert!(dot.contains("ServiceB"));

    let mermaid = graph.to_mermaid();
    assert!(mermaid.contains("graph TD"));
}

#[test]
fn validate_graph_reports_missing_dependency() {
    let injector = Injector::root();
    injector.provide::<ServiceA>(
        Provider::singleton(|_| Shared::new(ServiceA)).with_dependency::<ServiceB>(),
    );

    let report = injector.validate_graph();
    assert!(!report.is_valid());
    assert!(report.issues.iter().any(|issue| {
        issue.kind == GraphValidationIssueKind::MissingDependency
            && issue.message.contains("ServiceB")
    }));
}

#[test]
fn validate_graph_reports_cycles() {
    let injector = Injector::root();
    injector.provide::<ServiceA>(
        Provider::singleton(|_| Shared::new(ServiceA)).with_dependency::<ServiceB>(),
    );
    injector.provide::<ServiceB>(
        Provider::singleton(|_| Shared::new(ServiceB)).with_dependency::<ServiceA>(),
    );

    let report = injector.validate_graph();
    assert!(!report.is_valid());
    assert!(
        report
            .issues
            .iter()
            .any(|issue| issue.kind == GraphValidationIssueKind::CircularDependency)
    );
}

#[test]
fn try_validate_graph_returns_error_when_invalid() {
    let injector = Injector::root();
    injector.provide::<ServiceA>(
        Provider::singleton(|_| Shared::new(ServiceA)).with_dependency::<ServiceB>(),
    );

    let err = injector.try_validate_graph().unwrap_err();
    assert_eq!(err.kind, ErrorKind::GraphValidationFailed);
    assert!(err.message.contains("ServiceB"));
}

#[test]
fn validate_graph_handles_set_dependencies() {
    let injector = Injector::root();
    injector.provide_into_set::<ServiceB>(Provider::singleton(|_| Shared::new(ServiceB)));
    injector.provide::<ServiceA>(
        Provider::singleton(|_| Shared::new(ServiceA)).with_set_dependency::<ServiceB>(),
    );

    let report = injector.validate_graph();
    assert!(report.is_valid());

    let graph = injector.dependency_graph();
    assert!(
        graph
            .edges
            .iter()
            .any(|edge| edge.label.as_deref() == Some("all"))
    );
}