dpm-model 0.1.0

Domain model for DPM execution planning
Documentation
use super::*;

#[test]
fn unsupported_version_names_the_preserving_conversion_path() {
    for version in [0, 1, 2, 4, u32::MAX] {
        let input = format!("{{\"format_version\":{version}}}");
        let error = serde_json::from_str::<Plan>(&input)
            .expect_err("unsupported")
            .to_string();
        assert!(error.contains(&format!("unsupported plan format {version}")));
        assert!(error.contains("Preserve the original database"));
    }
}

#[test]
fn version_precedes_shape_errors_for_every_document_field_order() {
    for version in [0, 1, 2, 4, u32::MAX] {
        for body in [
            r#""resources":{}"#,
            r#""work_items":{"not-a-uuid":{"objective":"flat work"}}"#,
            r#""future_field":{"nested":[true,0,null]}"#,
        ] {
            for input in [
                format!("{{{body},\"format_version\":{version}}}"),
                format!("{{\"format_version\":{version},{body}}}"),
            ] {
                let error = serde_json::from_str::<Plan>(&input)
                    .expect_err("unsupported")
                    .to_string();
                assert!(
                    error.contains(&format!("unsupported plan format {version}")),
                    "{error}"
                );
                assert!(error.contains("Preserve the original database"), "{error}");
            }
        }
    }
}

#[test]
fn version_buffer_keeps_duplicate_fields_strict_and_full_revision_precision() {
    let mut plan = Plan::empty("portable");
    plan.revision = u64::MAX;
    let json = serde_json::to_string(&plan).expect("json");
    assert_eq!(serde_json::from_str::<Plan>(&json).expect("decode"), plan);
    for (input, field) in [
        (
            json.replacen("\"revision\":", "\"revision\":0,\"revision\":", 1),
            "revision",
        ),
        (
            json.replacen("\"name\":", "\"name\":\"extra\",\"name\":", 1),
            "name",
        ),
    ] {
        let error = serde_json::from_str::<Plan>(&input)
            .expect_err("duplicate")
            .to_string();
        assert!(
            error.contains(&format!("duplicate field `{field}`")),
            "{error}"
        );
    }
}

#[test]
fn buffered_plan_refuses_unconsumed_enum_and_sequence_entries() {
    let original = include_str!("../../../../tests/support/execution-plan.json");
    let plan: serde_json::Value = serde_json::from_str(original).expect("fixture");
    let asset = plan["assets"]
        .as_object()
        .expect("assets")
        .keys()
        .next()
        .expect("asset")
        .clone();
    let task = plan["work_items"]
        .as_object()
        .expect("work")
        .iter()
        .find(|(_, w)| w["kind"] == "Task")
        .expect("task")
        .0
        .clone();
    for estimate in [serde_json::json!([2, 4, 6, 999]), serde_json::json!([2, 4])] {
        let mut invalid = plan.clone();
        invalid["work_items"][&task]["schedule"]["estimate"] = estimate;
        assert!(serde_json::from_value::<Plan>(invalid).is_err());
    }
    let mut invalid = plan.clone();
    invalid["assets"][&asset]["kind"] =
        serde_json::json!({"GitRepository": {"remotes": []}, "Other": "must not disappear"});
    assert!(serde_json::from_value::<Plan>(invalid).is_err());
    let json = serde_json::to_string(&plan).expect("json");
    let duplicate = json.replace(
        r#""kind":{"GitRepository":{"remotes":[]}}"#,
        r#""kind":{"GitRepository":{"remotes":[]},"GitRepository":{"remotes":[]}}"#,
    );
    assert_ne!(duplicate, json);
    assert!(serde_json::from_str::<Plan>(&duplicate).is_err());
}