icydb-core 0.179.5

IcyDB — A schema-first typed query engine and persistence runtime for Internet Computer canisters
Documentation
use crate::{
    model::{
        canonicalize_grouped_having_numeric_literal_for_field_kind,
        canonicalize_strict_sql_literal_for_kind, classify_field_kind,
        field::FieldKind,
        field_kind_has_identity_group_canonical_form,
        field_kind_semantics::{FieldKindCategory, FieldKindNumericClass, FieldKindScalarClass},
    },
    value::Value,
};

#[test]
fn classify_numeric_scalar_field_kind() {
    let semantics = classify_field_kind(&FieldKind::Nat64);

    assert_eq!(
        semantics.category(),
        FieldKindCategory::Scalar(FieldKindScalarClass::Numeric(
            FieldKindNumericClass::Unsigned64,
        )),
    );
    assert!(semantics.supports_aggregate_numeric());
    assert!(semantics.supports_aggregate_ordering());
    assert!(semantics.supports_predicate_numeric_widen());
}

#[test]
fn classify_relation_uses_key_semantics_without_expr_numeric() {
    static NAT_KEY_KIND: FieldKind = FieldKind::Nat64;
    static RELATION_KIND: FieldKind = FieldKind::Relation {
        target_path: "demo::Target",
        target_entity_name: "Target",
        target_entity_tag: crate::types::EntityTag::new(1),
        target_store_path: "demo::store::TargetStore",
        key_kind: &NAT_KEY_KIND,
        strength: crate::model::field::RelationStrength::Strong,
    };

    let semantics = classify_field_kind(&RELATION_KIND);

    assert_eq!(
        semantics.category(),
        FieldKindCategory::Relation(FieldKindScalarClass::Numeric(
            FieldKindNumericClass::Unsigned64,
        )),
    );
    assert!(semantics.supports_aggregate_numeric());
    assert!(semantics.supports_aggregate_ordering());
    assert!(semantics.supports_predicate_numeric_widen());
}

#[test]
fn classify_collection_and_blob_stay_non_orderable() {
    let collection = classify_field_kind(&FieldKind::List(&FieldKind::Text { max_len: None }));
    let blob = classify_field_kind(&FieldKind::Blob { max_len: None });

    assert_eq!(collection.category(), FieldKindCategory::Collection);
    assert!(!collection.supports_aggregate_ordering());

    assert_eq!(
        blob.category(),
        FieldKindCategory::Scalar(FieldKindScalarClass::Opaque),
    );
    assert!(!blob.supports_aggregate_ordering());
}

#[test]
fn classify_wide_integer_and_temporal_kinds_keep_distinct_numeric_facets() {
    let wide = classify_field_kind(&FieldKind::Int128);
    let duration = classify_field_kind(&FieldKind::Duration);
    let timestamp = classify_field_kind(&FieldKind::Timestamp);

    assert_eq!(
        wide.category(),
        FieldKindCategory::Scalar(FieldKindScalarClass::Numeric(
            FieldKindNumericClass::SignedWide,
        )),
    );
    assert_eq!(
        duration.category(),
        FieldKindCategory::Scalar(FieldKindScalarClass::Numeric(
            FieldKindNumericClass::DurationLike,
        )),
    );
    assert_eq!(
        timestamp.category(),
        FieldKindCategory::Scalar(FieldKindScalarClass::Numeric(
            FieldKindNumericClass::TimestampLike,
        )),
    );

    assert!(!wide.supports_predicate_numeric_widen());
    assert!(!duration.supports_predicate_numeric_widen());
    assert!(!timestamp.supports_predicate_numeric_widen());
}

#[test]
fn grouped_field_kind_helpers_keep_decimal_relation_and_unit_edges_explicit() {
    static NAT_KEY_KIND: FieldKind = FieldKind::Nat64;
    static RELATION_KIND: FieldKind = FieldKind::Relation {
        target_path: "demo::Target",
        target_entity_name: "Target",
        target_entity_tag: crate::types::EntityTag::new(1),
        target_store_path: "demo::store::TargetStore",
        key_kind: &NAT_KEY_KIND,
        strength: crate::model::field::RelationStrength::Strong,
    };

    assert!(field_kind_has_identity_group_canonical_form(
        FieldKind::Text { max_len: None }
    ));
    assert!(!field_kind_has_identity_group_canonical_form(
        FieldKind::Decimal { scale: 2 }
    ));
    assert!(!field_kind_has_identity_group_canonical_form(RELATION_KIND));

    assert!(FieldKind::Decimal { scale: 2 }.supports_group_probe());
    assert!(RELATION_KIND.supports_group_probe());
    assert!(!FieldKind::Unit.supports_group_probe());
}

#[test]
fn runtime_value_acceptance_recurses_through_nested_field_kinds() {
    static TEXT_KIND: FieldKind = FieldKind::Text { max_len: None };
    static NAT_KIND: FieldKind = FieldKind::Nat64;
    static RELATION_KIND: FieldKind = FieldKind::Relation {
        target_path: "demo::Target",
        target_entity_name: "Target",
        target_entity_tag: crate::types::EntityTag::new(1),
        target_store_path: "demo::store::TargetStore",
        key_kind: &NAT_KIND,
        strength: crate::model::field::RelationStrength::Strong,
    };

    assert!(
        FieldKind::Map {
            key: &TEXT_KIND,
            value: &NAT_KIND,
        }
        .accepts_value(&Value::Map(vec![(
            Value::Text("a".into()),
            Value::Nat64(1)
        )]))
    );
    assert!(RELATION_KIND.accepts_value(&Value::Nat64(9)));
    assert!(!FieldKind::List(&TEXT_KIND).accepts_value(&Value::List(vec![Value::Nat64(1)])));
}

#[test]
fn grouped_having_numeric_canonicalization_keeps_numeric_relation_recursion() {
    static NAT_KIND: FieldKind = FieldKind::Nat64;
    static RELATION_KIND: FieldKind = FieldKind::Relation {
        target_path: "demo::Target",
        target_entity_name: "Target",
        target_entity_tag: crate::types::EntityTag::new(1),
        target_store_path: "demo::store::TargetStore",
        key_kind: &NAT_KIND,
        strength: crate::model::field::RelationStrength::Strong,
    };

    assert_eq!(
        canonicalize_grouped_having_numeric_literal_for_field_kind(
            Some(FieldKind::Int64),
            &Value::Nat64(7),
        ),
        Some(Value::Int64(7)),
    );
    assert_eq!(
        canonicalize_grouped_having_numeric_literal_for_field_kind(
            Some(RELATION_KIND),
            &Value::Int64(7),
        ),
        Some(Value::Nat64(7)),
    );
    assert_eq!(
        canonicalize_grouped_having_numeric_literal_for_field_kind(
            Some(FieldKind::Ulid),
            &Value::Text("01ARZ3NDEKTSV4RRFFQ69G5FAV".into()),
        ),
        None,
    );
}

#[test]
fn strict_sql_literal_canonicalization_adds_ulid_without_widening_other_kinds() {
    let ulid_text = "01ARZ3NDEKTSV4RRFFQ69G5FAV".to_string();

    std::assert_matches!(
        canonicalize_strict_sql_literal_for_kind(&FieldKind::Ulid, &Value::Text(ulid_text),),
        Some(Value::Ulid(_)),
    );
    assert_eq!(
        canonicalize_strict_sql_literal_for_kind(&FieldKind::Nat64, &Value::Int64(4)),
        Some(Value::Nat64(4)),
    );
    assert_eq!(
        canonicalize_strict_sql_literal_for_kind(
            &FieldKind::Text { max_len: None },
            &Value::Text("x".into())
        ),
        None,
    );
}