canic-host 0.70.9

Host-side build, install, deployment, and fleet-template library for Canic workspaces
Documentation
use super::*;

#[test]
fn promotion_target_execution_lineage_round_trips_through_json() {
    let transform = sample_promotion_transform();
    let preflight = sample_execution_preflight_for_plan("promoted-plan-1");

    let lineage = promotion_target_execution_lineage(PromotionTargetExecutionLineageRequest {
        lineage_id: "target-execution-lineage-1".to_string(),
        generated_at: "2026-05-25T00:00:00Z".to_string(),
        transform,
        execution_preflight: preflight,
    })
    .expect("target execution lineage should be produced");

    assert_json_round_trip(&lineage);
    let encoded = serde_json::to_value(&lineage).expect("lineage should encode");
    assert_object_keys(
        &encoded,
        &[
            "schema_version",
            "lineage_id",
            "generated_at",
            "target_execution_lineage_digest",
            "transform",
            "execution_preflight",
            "execution_attempted",
        ],
    );
    assert_eq!(encoded["lineage_id"], "target-execution-lineage-1");
    assert_eq!(encoded["execution_attempted"], false);
}

#[test]
fn promotion_target_execution_lineage_validation_accepts_generated_lineage() {
    let transform = sample_promotion_transform();
    let preflight = sample_execution_preflight_for_plan("promoted-plan-1");

    let lineage = promotion_target_execution_lineage(PromotionTargetExecutionLineageRequest {
        lineage_id: "target-execution-lineage-1".to_string(),
        generated_at: "2026-05-25T00:00:00Z".to_string(),
        transform,
        execution_preflight: preflight,
    })
    .expect("target execution lineage should be produced");

    validate_promotion_target_execution_lineage(&lineage)
        .expect("generated lineage should validate");
}

#[test]
fn promotion_target_execution_lineage_rejects_preflight_for_other_plan() {
    let transform = sample_promotion_transform();
    let preflight = sample_execution_preflight_for_plan("other-promoted-plan");

    let err = promotion_target_execution_lineage(PromotionTargetExecutionLineageRequest {
        lineage_id: "target-execution-lineage-1".to_string(),
        generated_at: "2026-05-25T00:00:00Z".to_string(),
        transform,
        execution_preflight: preflight,
    })
    .expect_err("preflight for another plan should fail");

    std::assert_matches!(
        err,
        PromotionTargetExecutionLineageError::LinkageMismatch {
            field: "execution_preflight.plan_id"
        }
    );
}

#[test]
fn promotion_target_execution_lineage_rejects_execution_claim() {
    let transform = sample_promotion_transform();
    let preflight = sample_execution_preflight_for_plan("promoted-plan-1");
    let mut lineage = promotion_target_execution_lineage(PromotionTargetExecutionLineageRequest {
        lineage_id: "target-execution-lineage-1".to_string(),
        generated_at: "2026-05-25T00:00:00Z".to_string(),
        transform,
        execution_preflight: preflight,
    })
    .expect("target execution lineage should be produced");
    lineage.execution_attempted = true;

    let err = validate_promotion_target_execution_lineage(&lineage)
        .expect_err("execution claim should fail");

    std::assert_matches!(
        err,
        PromotionTargetExecutionLineageError::ExecutionAttempted
    );
}

#[test]
fn promotion_target_execution_lineage_rejects_stale_digest() {
    let transform = sample_promotion_transform();
    let preflight = sample_execution_preflight_for_plan("promoted-plan-1");
    let mut lineage = promotion_target_execution_lineage(PromotionTargetExecutionLineageRequest {
        lineage_id: "target-execution-lineage-1".to_string(),
        generated_at: "2026-05-25T00:00:00Z".to_string(),
        transform,
        execution_preflight: preflight,
    })
    .expect("target execution lineage should be produced");
    lineage.target_execution_lineage_digest = sample_sha256("9");

    let err = validate_promotion_target_execution_lineage(&lineage)
        .expect_err("stale lineage digest should fail");

    std::assert_matches!(
        err,
        PromotionTargetExecutionLineageError::LinkageMismatch {
            field: "target_execution_lineage_digest"
        }
    );
}

#[test]
fn promotion_target_execution_lineage_text_reports_passive_boundary() {
    let transform = sample_promotion_transform();
    let preflight = sample_execution_preflight_for_plan("promoted-plan-1");
    let lineage = promotion_target_execution_lineage(PromotionTargetExecutionLineageRequest {
        lineage_id: "target-execution-lineage-1".to_string(),
        generated_at: "2026-05-25T00:00:00Z".to_string(),
        transform,
        execution_preflight: preflight,
    })
    .expect("target execution lineage should be produced");

    let text = promotion_target_execution_lineage_text(&lineage);

    assert!(text.contains("Promotion target execution lineage"));
    assert!(text.contains("mode: passive"));
    assert!(text.contains("execution: none"));
    assert!(text.contains("execution_attempted: false"));
    assert!(text.contains("lineage_id: target-execution-lineage-1"));
    assert!(text.contains("target_execution_lineage_digest: "));
    assert!(text.contains("transform_id: promotion-transform:promoted-plan-1"));
    assert!(text.contains("preflight_plan_id: promoted-plan-1"));
    assert!(text.contains("  Deployment execution preflight"));
}