xbp-analytics 11.1.7

Deterministic delivery analytics primitives for XBP.
Documentation
use chrono::{Duration, Utc};
use xbp_analytics::DoraMetrics;
use xbp_delivery::{DeliveryEvent, DeliveryEventKind, RepositoryId, SourceSystem};

#[test]
fn dora_metrics_project_from_delivery_events() {
    let now = Utc::now();
    let mut commit = DeliveryEvent::new(
        "commit",
        DeliveryEventKind::CommitCreated,
        SourceSystem::GitHub,
        "c1",
    );
    commit.occurred_at = now - Duration::hours(10);
    commit.repository_id = Some(RepositoryId("repo_1".into()));
    let mut deployment = DeliveryEvent::new(
        "deployment",
        DeliveryEventKind::DeploymentSucceeded,
        SourceSystem::Xbp,
        "d1",
    );
    deployment.occurred_at = now;
    deployment.repository_id = Some(RepositoryId("repo_1".into()));
    deployment.attributes.insert(
        "environment".into(),
        serde_json::Value::String("production".into()),
    );
    deployment
        .attributes
        .insert("commit_ids".into(), serde_json::json!(["commit"]));

    let metrics = DoraMetrics::from_events(&[commit, deployment], now - Duration::days(1), now);

    assert_eq!(metrics.sample_count, 1);
    assert!(metrics.lead_time_for_changes_hours.unwrap() >= 9.9);
    assert_eq!(metrics.change_failure_rate, Some(0.0));
}

#[test]
fn dora_does_not_correlate_changes_by_repository_proximity() {
    let now = Utc::now();
    let mut commit = DeliveryEvent::new(
        "commit",
        DeliveryEventKind::CommitCreated,
        SourceSystem::GitHub,
        "c1",
    );
    commit.occurred_at = now - Duration::hours(10);
    commit.repository_id = Some(RepositoryId("repo_1".into()));
    let mut deployment = DeliveryEvent::new(
        "deployment",
        DeliveryEventKind::DeploymentSucceeded,
        SourceSystem::Xbp,
        "d1",
    );
    deployment.occurred_at = now;
    deployment.repository_id = Some(RepositoryId("repo_1".into()));
    deployment.attributes.insert(
        "environment".into(),
        serde_json::Value::String("production".into()),
    );

    let metrics = DoraMetrics::from_events(&[commit, deployment], now - Duration::days(1), now);

    assert_eq!(metrics.sample_count, 0);
    assert!(metrics.lead_time_for_changes_hours.is_none());
}

#[test]
fn dora_resolves_linked_commits_before_the_reporting_window() {
    let now = Utc::now();
    let mut commit = DeliveryEvent::new(
        "commit",
        DeliveryEventKind::CommitCreated,
        SourceSystem::GitHub,
        "c1",
    );
    commit.occurred_at = now - Duration::days(2);
    let mut deployment = DeliveryEvent::new(
        "deployment",
        DeliveryEventKind::DeploymentSucceeded,
        SourceSystem::Xbp,
        "d1",
    );
    deployment.occurred_at = now;
    deployment.attributes.insert(
        "environment".into(),
        serde_json::Value::String("production".into()),
    );
    deployment
        .attributes
        .insert("commit_ids".into(), serde_json::json!(["commit"]));

    let metrics = DoraMetrics::from_events(&[commit, deployment], now - Duration::days(1), now);

    assert_eq!(metrics.sample_count, 1);
    assert!(metrics.lead_time_for_changes_hours.is_some());
}

#[test]
fn dora_serializes_no_change_failure_rate_as_unknown() {
    let now = Utc::now();
    let metrics = DoraMetrics::from_events(&[], now - Duration::days(1), now);

    let json = serde_json::to_value(&metrics).expect("serialize DORA metrics");

    assert!(json["change_failure_rate"].is_null());
    assert_eq!(
        metrics.deployment_frequency_evidence.availability,
        xbp_analytics::MetricAvailability::Available
    );
}

#[test]
fn dora_v1_projection_preserves_zero_based_wire_compatibility() {
    let now = Utc::now();
    let metrics = DoraMetrics::from_events(&[], now - Duration::days(1), now);
    let legacy = metrics.to_v1();

    assert_eq!(legacy.change_failure_rate, 0.0);
    assert_eq!(legacy.deployment_frequency_per_week, 0.0);
    assert_eq!(legacy.sample_count, 0);
}

#[test]
fn dora_treats_failure_outcomes_case_insensitively() {
    let now = Utc::now();
    let mut deployment = DeliveryEvent::new(
        "deployment",
        DeliveryEventKind::DeploymentSucceeded,
        SourceSystem::Xbp,
        "d1",
    );
    deployment.occurred_at = now;
    deployment.attributes.insert(
        "environment".into(),
        serde_json::Value::String("production".into()),
    );
    deployment
        .attributes
        .insert("outcome".into(), serde_json::Value::String("FAILED".into()));

    let metrics = DoraMetrics::from_events(&[deployment], now - Duration::days(1), now);

    assert_eq!(metrics.change_failure_rate, Some(1.0));
}