cadmpeg-ir 0.6.1

CAD data model, validation, diffing, and codec API for cadmpeg.
use crate::{
    features::{DesignParameter, DistinctMembers, ParameterId, ParameterValue},
    scalar::FiniteReal,
};

#[test]
fn parameter_real_admission_preserves_finite_signed_wire_values() {
    for value in [-f64::MAX, -1.0, -0.0, 0.0, 1.0, f64::MAX] {
        let parameter = ParameterValue::Real(FiniteReal::new(value).unwrap());
        let wire = serde_json::json!({"kind": "real", "value": value});
        assert_eq!(serde_json::to_value(&parameter).unwrap(), wire);
        assert_eq!(
            serde_json::from_value::<ParameterValue>(wire).unwrap(),
            parameter
        );
    }
    for value in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
        assert!(FiniteReal::new(value).is_none());
        let deserializer = serde::de::value::F64Deserializer::<serde::de::value::Error>::new(value);
        let error = super::super::deserialize_parameter_real(deserializer).unwrap_err();
        assert!(error.to_string().contains("value"));
    }
}

#[test]
fn parameter_dependencies_reject_duplicates_and_preserve_source_order() {
    let first = ParameterId::mint("test:test:parameter#first").unwrap();
    let second = ParameterId::mint("test:test:parameter#second").unwrap();
    assert!(DistinctMembers::try_from(
        vec![first.clone(), first.clone()],
        &cadmpeg_test_support::service_decode_context()
    )
    .is_err());
    let mut dependencies = DistinctMembers::try_from(
        vec![second.clone(), first.clone()],
        &cadmpeg_test_support::service_decode_context(),
    )
    .unwrap();
    assert!(!dependencies
        .insert(
            &cadmpeg_test_support::service_decode_context(),
            first.clone(),
            "insert fixture member"
        )
        .expect("member insertion admission"));
    assert_eq!(dependencies.as_slice(), &[second.clone(), first.clone()]);
    dependencies.retain(|id| id == &first);
    assert_eq!(dependencies.as_slice(), std::slice::from_ref(&first));
    dependencies.clear();
    assert!(dependencies.is_empty());

    let mut wire = serde_json::json!({
        "id":"test:test:parameter#owner", "ordinal":0, "name":"value", "expression":"first+second",
        "dependencies":[second, first]
    });
    let parameter: DesignParameter = serde_json::from_value(wire.clone()).unwrap();
    assert_eq!(serde_json::to_value(parameter).unwrap(), wire);
    wire["dependencies"] = serde_json::json!([first, first]);
    let error = serde_json::from_value::<DesignParameter>(wire.clone()).unwrap_err();
    assert!(error.to_string().contains("dependencies"));
    wire.as_object_mut().unwrap().remove("dependencies");
    let parameter: DesignParameter = serde_json::from_value(wire.clone()).unwrap();
    assert!(parameter.dependencies.is_empty());
    assert_eq!(serde_json::to_value(parameter).unwrap(), wire);
}

#[test]
fn reserved_parameter_dependencies_preserve_order_and_reject_repeats() {
    let first = ParameterId::mint("test:test:parameter#first").unwrap();
    let second = ParameterId::mint("test:test:parameter#second").unwrap();
    let ordered = crate::test_support::with_service_decode_context(|ctx| {
        DistinctMembers::try_from(vec![second.clone(), first.clone()], ctx)
            .map_err(cadmpeg_core::CodecError::from)
    })
    .expect("source-ordered members are distinct");
    assert_eq!(ordered.as_slice(), &[second.clone(), first.clone()]);
    assert!(crate::test_support::with_service_decode_context(|ctx| {
        DistinctMembers::try_from(vec![first.clone(), second, first], ctx)
            .map_err(cadmpeg_core::CodecError::from)
    })
    .is_err());
}

#[test]
fn distinct_constructor_preserves_collection_refusal_in_the_caller_session() {
    use cadmpeg_core::decode::{DecodeArena, DecodeContext, DecodePolicy, ResourceDimension};
    let arena = DecodeArena::new();
    let mut policy = DecodePolicy::service();
    policy.limits.max_collection_items = 0;
    let (ctx, _) = DecodeContext::from_root_bytes(&[], &arena, &policy).unwrap();
    let member = ParameterId::mint("test:model:parameter#member").unwrap();
    let Err(crate::features::FeatureCollectionError::Resource(limit)) =
        DistinctMembers::try_from(vec![member], &ctx)
    else {
        panic!("member slot must be refused");
    };
    assert_eq!(limit.dimension, ResourceDimension::CollectionItems);
    assert!(
        matches!(ctx.finish_session(), Err(cadmpeg_core::CodecError::ResourceLimit(sticky)) if sticky == limit)
    );
}