icydb-core 0.180.11

IcyDB — A schema-first typed query engine and persistence runtime for Internet Computer canisters
Documentation
use crate::{
    db::{
        data::{
            decode_structural_field_by_accepted_kind_bytes, decode_structural_field_by_kind_bytes,
            encode_structural_field_by_kind_bytes,
            validate_structural_field_by_accepted_kind_bytes,
            validate_structural_field_by_kind_bytes,
        },
        schema::{PersistedEnumVariant, PersistedFieldKind},
    },
    model::field::{FieldKind, FieldStorageDecode},
    value::{Value, ValueEnum},
};

fn assert_generated_and_accepted_decode_match(
    generated_kind: FieldKind,
    accepted_kind: &PersistedFieldKind,
    value: &Value,
    field_name: &str,
) {
    let encoded = encode_structural_field_by_kind_bytes(generated_kind, value, field_name)
        .expect("generated-compatible test payload should encode");
    let generated = decode_structural_field_by_kind_bytes(&encoded, generated_kind)
        .expect("generated decoder should decode test payload");
    let accepted = decode_structural_field_by_accepted_kind_bytes(&encoded, accepted_kind)
        .expect("accepted decoder should decode generated-compatible payload");

    validate_structural_field_by_accepted_kind_bytes(&encoded, accepted_kind)
        .expect("accepted kind should validate generated-compatible payload");

    assert_eq!(generated, *value);
    assert_eq!(accepted, generated);
}

fn assert_generated_and_accepted_reject_match(
    generated_kind: FieldKind,
    accepted_kind: &PersistedFieldKind,
    raw_bytes: &[u8],
) {
    assert!(decode_structural_field_by_kind_bytes(raw_bytes, generated_kind).is_err());
    assert!(decode_structural_field_by_accepted_kind_bytes(raw_bytes, accepted_kind).is_err());
    assert!(validate_structural_field_by_kind_bytes(raw_bytes, generated_kind).is_err());
    assert!(validate_structural_field_by_accepted_kind_bytes(raw_bytes, accepted_kind).is_err());
}

#[test]
fn accepted_kind_decoder_matches_generated_nested_collection_payloads() {
    let generated_kind = FieldKind::Map {
        key: &FieldKind::Text { max_len: None },
        value: &FieldKind::List(&FieldKind::Nat64),
    };
    let accepted_kind = PersistedFieldKind::Map {
        key: Box::new(PersistedFieldKind::Text { max_len: None }),
        value: Box::new(PersistedFieldKind::List(Box::new(
            PersistedFieldKind::Nat64,
        ))),
    };
    let value = Value::Map(vec![
        (
            Value::Text("alpha".to_string()),
            Value::List(vec![Value::Nat64(1), Value::Nat64(2)]),
        ),
        (
            Value::Text("beta".to_string()),
            Value::List(vec![Value::Nat64(3)]),
        ),
    ]);

    assert_generated_and_accepted_decode_match(generated_kind, &accepted_kind, &value, "payload");
}

#[test]
fn accepted_kind_decoder_rejects_malformed_nested_lists_like_generated_decoder() {
    let generated_kind = FieldKind::List(&FieldKind::Nat64);
    let accepted_kind = PersistedFieldKind::List(Box::new(PersistedFieldKind::Nat64));
    let value = Value::List(vec![Value::Nat64(1), Value::Nat64(2)]);
    let mut malformed = encode_structural_field_by_kind_bytes(generated_kind, &value, "numbers")
        .expect("generated-compatible list payload should encode");
    malformed.pop();

    assert_generated_and_accepted_reject_match(
        generated_kind,
        &accepted_kind,
        malformed.as_slice(),
    );
}

#[test]
fn accepted_kind_decoder_rejects_malformed_nested_maps_like_generated_decoder() {
    let generated_kind = FieldKind::Map {
        key: &FieldKind::Text { max_len: None },
        value: &FieldKind::Nat64,
    };
    let accepted_kind = PersistedFieldKind::Map {
        key: Box::new(PersistedFieldKind::Text { max_len: None }),
        value: Box::new(PersistedFieldKind::Nat64),
    };
    let value = Value::Map(vec![(Value::Text("alpha".to_string()), Value::Nat64(1))]);
    let mut malformed = encode_structural_field_by_kind_bytes(generated_kind, &value, "entries")
        .expect("generated-compatible map payload should encode");
    malformed.pop();

    assert_generated_and_accepted_reject_match(
        generated_kind,
        &accepted_kind,
        malformed.as_slice(),
    );
}

#[test]
fn accepted_kind_decoder_matches_generated_enum_payload_contracts() {
    static GENERATED_VARIANTS: &[crate::model::field::EnumVariantModel] =
        &[crate::model::field::EnumVariantModel::new(
            "Loaded",
            Some(&FieldKind::Nat64),
            FieldStorageDecode::ByKind,
        )];
    let generated_kind = FieldKind::Enum {
        path: "tests::State",
        variants: GENERATED_VARIANTS,
    };
    let accepted_kind = PersistedFieldKind::Enum {
        path: "tests::State".to_string(),
        variants: vec![PersistedEnumVariant::new(
            "Loaded".to_string(),
            Some(Box::new(PersistedFieldKind::Nat64)),
            FieldStorageDecode::ByKind,
        )],
    };
    let value =
        Value::Enum(ValueEnum::new("Loaded", Some("tests::State")).with_payload(Value::Nat64(9)));

    assert_generated_and_accepted_decode_match(generated_kind, &accepted_kind, &value, "state");
}

#[test]
fn accepted_kind_decoder_matches_generated_relation_list_payloads() {
    const RELATION_KEY_KIND: FieldKind = FieldKind::Ulid;
    let generated_kind = FieldKind::List(&FieldKind::Relation {
        target_path: "tests::Target",
        target_entity_name: "Target",
        target_entity_tag: crate::testing::PROBE_ENTITY_TAG,
        target_store_path: "tests::TargetStore",
        key_kind: &RELATION_KEY_KIND,
        strength: crate::model::field::RelationStrength::Strong,
    });
    let accepted_kind = PersistedFieldKind::List(Box::new(PersistedFieldKind::Relation {
        target_path: "tests::Target".to_string(),
        target_entity_name: "Target".to_string(),
        target_entity_tag: crate::testing::PROBE_ENTITY_TAG,
        target_store_path: "tests::TargetStore".to_string(),
        key_kind: Box::new(PersistedFieldKind::Ulid),
        strength: crate::db::schema::PersistedRelationStrength::Strong,
    }));
    let value = Value::List(vec![
        Value::Ulid(crate::types::Ulid::from_u128(11)),
        Value::Ulid(crate::types::Ulid::from_u128(12)),
    ]);

    assert_generated_and_accepted_decode_match(generated_kind, &accepted_kind, &value, "targets");
}