tensogram 0.24.0

Fast binary N-tensor message format for scientific data — encode, decode, file I/O, streaming
Documentation
// (C) Copyright 2026- ECMWF and individual contributors.
//
// This software is licensed under the terms of the Apache Licence Version 2.0
// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
// In applying this licence, ECMWF does not waive the privileges and immunities
// granted to it by virtue of its status as an intergovernmental organisation nor
// does it submit to any jurisdiction.

//! Cross-language metadata-access parity — the Rust oracle.
//!
//! Loads the shared fixture `tests/fixtures/metadata_parity.tgm` (generated by
//! `tests/fixtures/gen_metadata_parity.py`) and asserts the full capability
//! matrix from `plans/METADATA_ACCESS_PARITY.md` §7. The C, C++, Python,
//! TypeScript, and Fortran bindings assert the same answers against the same
//! bytes (Python/TS) or an equivalent in-memory message (C/C++/Fortran).

use tensogram::{MetaType, decode_metadata};

const FIXTURE: &[u8] = include_bytes!(concat!(
    env!("CARGO_MANIFEST_DIR"),
    "/../../tests/fixtures/metadata_parity.tgm"
));

#[test]
fn metadata_parity_matrix() {
    let meta = decode_metadata(FIXTURE).expect("decode fixture metadata");

    // ── object count ───────────────────────────────────────────────────
    assert_eq!(meta.num_objects(), 2);

    // ── existence: absent is distinct from a real 0 / "" ───────────────
    assert!(meta.contains("shortName"));
    assert!(meta.contains("level")); // value is 0
    assert!(meta.contains("note")); // value is ""
    assert!(!meta.contains("missing"));

    // ── message-level first-match + typed, non-coercing access ─────────
    assert_eq!(meta.get("shortName").unwrap().as_str(), Some("2t")); // base[0] wins
    assert_eq!(meta.get("level").unwrap().as_i64(), Some(0));
    assert_eq!(meta.get("note").unwrap().as_str(), Some(""));
    assert_eq!(meta.get("count").unwrap().as_i64(), Some(-5));
    assert_eq!(meta.get("count").unwrap().as_u64(), None); // negative
    assert_eq!(meta.get("big").unwrap().as_i64(), Some(2_147_483_648));
    assert_eq!(meta.get("ratio").unwrap().as_f64(), Some(0.5));
    assert_eq!(meta.get("ratio").unwrap().as_i64(), None); // no coercion
    assert_eq!(meta.get("flag").unwrap().as_bool(), Some(true));
    assert_eq!(meta.get("shortName").unwrap().as_i64(), None); // wrong type != absent
    assert_eq!(meta.get("level").unwrap().value_type(), MetaType::Int);
    assert_eq!(meta.get("note").unwrap().value_type(), MetaType::String);

    // ── nested map navigation (dot-path and cursor agree) ──────────────
    assert_eq!(meta.get("mars.class").unwrap().as_str(), Some("od"));
    let mars = meta.get("mars").unwrap();
    assert_eq!(mars.value_type(), MetaType::Map);
    assert_eq!(mars.get_key("stream").unwrap().as_str(), Some("oper"));
    assert!(mars.contains_key("class"));

    // ── array navigation ───────────────────────────────────────────────
    let levels = meta.get("levels").unwrap();
    assert_eq!(levels.value_type(), MetaType::Array);
    assert_eq!(levels.len(), 3);
    assert_eq!(levels.get_index(1).unwrap().as_i64(), Some(850));
    assert!(levels.get_index(3).is_none());

    // ── _extra_ fallback + explicit prefix ─────────────────────────────
    assert_eq!(meta.get("source").unwrap().as_str(), Some("parity-fixture"));
    assert_eq!(
        meta.get("extra.source").unwrap().as_str(),
        Some("parity-fixture")
    );

    // ── _reserved_ hidden from path getters, visible on enumeration ────
    assert!(!meta.contains("_reserved_.tensor"));
    assert!(!meta.contains_at(0, "_reserved_.tensor"));
    let obj0 = meta.object(0).unwrap();
    assert!(obj0.contains_key("_reserved_"));
    assert!(obj0.contains_key("shortName"));
    let tensor = obj0
        .get_key("_reserved_")
        .and_then(|r| r.get_key("tensor"))
        .expect("reserved.tensor via enumeration");
    assert_eq!(tensor.get_key("dtype").unwrap().as_str(), Some("float32"));
    let shape = tensor.get_key("shape").unwrap();
    assert_eq!(shape.value_type(), MetaType::Array);
    assert_eq!(shape.get_index(0).unwrap().as_i64(), Some(2));
    assert_eq!(shape.get_index(1).unwrap().as_i64(), Some(3));

    // ── per-object scoping ─────────────────────────────────────────────
    assert_eq!(meta.get_at(0, "shortName").unwrap().as_str(), Some("2t"));
    assert_eq!(meta.get_at(1, "shortName").unwrap().as_str(), Some("msl"));
    assert_eq!(meta.get_at(0, "mars.class").unwrap().as_str(), Some("od"));
    assert_eq!(meta.get_at(1, "mars.class").unwrap().as_str(), Some("ea"));
    // `only0` lives in object 0 only — no cross-object match.
    assert!(meta.contains_at(0, "only0"));
    assert!(!meta.contains_at(1, "only0"));
    // out-of-range object.
    assert!(meta.get_at(2, "shortName").is_none());

    // ── extra / reserved section views ─────────────────────────────────
    assert!(meta.extra_view().contains_key("source"));
    assert_eq!(
        meta.extra_view().get_key("source").unwrap().as_str(),
        Some("parity-fixture")
    );
}