Rename all 19 JSON Schema custom keyword strings from "TypeDef:*"
to "AlkType:*" (e.g., "TypeDef:Struct" -> "AlkType:Struct")
across source, tests, and docs. This is a breaking change to the
schema format itself — existing schemas using the old keywords
must be updated.
Rename the Rust identifiers:
- TypedefEngine -> AlkTypeEngine
- TypedefError -> AlkTypeError
- TypeDefKind -> AlkTypeKind
- TYPEDEF_PREFIX -> ALKTYPE_PREFIX
- get_typedef_kind{,_loose,_loose_enum,_enum} ->
get_alktype_kind{,_loose,_loose_enum,_enum}
Update error message strings ("unknown TypeDef kind" ->
"unknown AlkType kind"), 11 test function names containing
typedef_kind/to_typedef_error, and doc-comment prose ("TypeDef
kind" -> "AlkType kind", "typedef engine" -> "alktype
engine", "typedef schema" -> "alktype schema"). Fix the broken
docs/architecture/crates/typedef/ path references in source doc
comments to point at docs/architecture/ directly. Rebrand the
typedef:annotation test fixture and the "not-a-typedef" test
string to their alktype equivalents.
Update ~20 generic "typedef" prose references in the architecture
docs ("typedef is the binary struct engine", "use typedef",
"typedef limitation", "replaced by typedef", etc.) to alktype.
Rename TypedefEngine in the ADR-007 code example to AlkTypeEngine.
Preserve as provenance per the prior prose-rebrand decision:
typedef.ts references (external TypeBox source file),
docs/research/alknet-typedef/findings.md research citations,
/workspace/alknet-typedef-poc/ POC path, and the
"alknet-typedef:" research section headers in findings.
Build, 295 tests, and clippy all pass clean.
390 lines
13 KiB
Rust
390 lines
13 KiB
Rust
//! Integration tests for the `AlkTypeEngine` public API.
|
|
//!
|
|
//! Exercises the engine across both layout modes, the convenience
|
|
//! accessors, validation convenience methods, and the aligned-mode
|
|
//! `read_field` / `write_field` round-trip for the fixed-size primitive
|
|
//! kinds and length-prefixed `String` / `Bytes`.
|
|
|
|
use alktype::*;
|
|
use serde_json::json;
|
|
|
|
fn mixed_fixed_struct_schema() -> serde_json::Value {
|
|
json!({
|
|
"AlkType:Struct": true,
|
|
"endian": "little",
|
|
"properties": {
|
|
"flag": { "AlkType:Uint8": true },
|
|
"id": { "AlkType:Uint32": true },
|
|
"score": { "AlkType:Float32": true },
|
|
"tag": { "AlkType:String": true }
|
|
}
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn compile_aligned_builds_engine_with_offset_map() -> Result<(), AlkTypeError> {
|
|
let mut schema = mixed_fixed_struct_schema();
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
assert_eq!(engine.mode(), LayoutMode::Aligned);
|
|
assert!(engine.offset_map().is_some());
|
|
assert!(engine.layout_builder().is_none());
|
|
assert!(engine.sequential_reader().is_none());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn compile_packed_builds_engine_with_builder_and_reader() -> Result<(), AlkTypeError> {
|
|
let mut schema = mixed_fixed_struct_schema();
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
assert_eq!(engine.mode(), LayoutMode::Packed);
|
|
assert!(engine.offset_map().is_none());
|
|
assert!(engine.layout_builder().is_some());
|
|
assert!(engine.sequential_reader().is_some());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn compile_normalizes_bare_name_refs() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"child": { "$ref": "Child" }
|
|
},
|
|
"$defs": {
|
|
"Child": {
|
|
"AlkType:Struct": true,
|
|
"properties": { "x": { "AlkType:Uint8": true } }
|
|
}
|
|
}
|
|
});
|
|
let _engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
assert_eq!(
|
|
schema["properties"]["child"]["$ref"],
|
|
json!("#/$defs/Child")
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn compile_leaves_full_pointer_refs_unchanged() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"child": { "$ref": "#/$defs/Child" }
|
|
},
|
|
"$defs": {
|
|
"Child": {
|
|
"AlkType:Struct": true,
|
|
"properties": { "x": { "AlkType:Uint8": true } }
|
|
}
|
|
}
|
|
});
|
|
let _engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
assert_eq!(
|
|
schema["properties"]["child"]["$ref"],
|
|
json!("#/$defs/Child")
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn compile_returns_schema_error_when_no_alktype_kind() {
|
|
let mut schema = json!({ "type": "object", "properties": {} });
|
|
let err = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned).unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Schema(_)), "got {err:?}");
|
|
}
|
|
|
|
#[test]
|
|
fn endian_parsed_from_schema_big() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"endian": "big",
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
assert_eq!(engine.endian(), Endian::Big);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn endian_defaults_to_little() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
assert_eq!(engine.endian(), Endian::Little);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn validate_json_accepts_valid_instance() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "AlkType:Uint32": true, "type": "integer" }
|
|
},
|
|
"required": ["id"]
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
assert!(engine.validate_json(&json!({"id": 42})).is_ok());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn validate_json_rejects_invalid_instance() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "AlkType:Uint32": true, "type": "integer" }
|
|
},
|
|
"required": ["id"]
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let err = engine.validate_json(&json!({"id": -1})).unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Validation(_)), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn is_valid_json_returns_bool() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"type": "object",
|
|
"properties": {
|
|
"id": { "AlkType:Uint32": true, "type": "integer" }
|
|
},
|
|
"required": ["id"]
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
assert!(engine.is_valid_json(&json!({"id": 42})));
|
|
assert!(!engine.is_valid_json(&json!({"id": -1})));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_write_aligned_round_trips_all_fixed_size_kinds() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"endian": "little",
|
|
"properties": {
|
|
"i8": { "AlkType:Int8": true },
|
|
"u8": { "AlkType:Uint8": true },
|
|
"i16": { "AlkType:Int16": true },
|
|
"u16": { "AlkType:Uint16": true },
|
|
"i32": { "AlkType:Int32": true },
|
|
"u32": { "AlkType:Uint32": true },
|
|
"i64": { "AlkType:Int64": true },
|
|
"u64": { "AlkType:Uint64": true },
|
|
"f32": { "AlkType:Float32": true },
|
|
"f64": { "AlkType:Float64": true },
|
|
"b": { "AlkType:Boolean": true },
|
|
"e": { "AlkType:Enum": true }
|
|
}
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let offset_map = engine.offset_map().expect("aligned mode has offset_map");
|
|
let mut buffer = vec![0u8; offset_map.total_size()];
|
|
|
|
engine.write_field(&mut buffer, "i8", &FieldValue::I8(-127))?;
|
|
engine.write_field(&mut buffer, "u8", &FieldValue::U8(0xAB))?;
|
|
engine.write_field(&mut buffer, "i16", &FieldValue::I16(-32000))?;
|
|
engine.write_field(&mut buffer, "u16", &FieldValue::U16(0xBEEF))?;
|
|
engine.write_field(&mut buffer, "i32", &FieldValue::I32(-2_000_000_007))?;
|
|
engine.write_field(&mut buffer, "u32", &FieldValue::U32(0xDEADBEEF))?;
|
|
engine.write_field(&mut buffer, "i64", &FieldValue::I64(-9_000_000_000_000_000_000))?;
|
|
engine.write_field(&mut buffer, "u64", &FieldValue::U64(0x0102030405060708))?;
|
|
engine.write_field(&mut buffer, "f32", &FieldValue::F32(1.5))?;
|
|
engine.write_field(&mut buffer, "f64", &FieldValue::F64(2.5))?;
|
|
engine.write_field(&mut buffer, "b", &FieldValue::Bool(true))?;
|
|
engine.write_field(&mut buffer, "e", &FieldValue::Enum(7))?;
|
|
|
|
assert_eq!(engine.read_field(&buffer, "i8")?, FieldValue::I8(-127));
|
|
assert_eq!(engine.read_field(&buffer, "u8")?, FieldValue::U8(0xAB));
|
|
assert_eq!(engine.read_field(&buffer, "i16")?, FieldValue::I16(-32000));
|
|
assert_eq!(engine.read_field(&buffer, "u16")?, FieldValue::U16(0xBEEF));
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "i32")?,
|
|
FieldValue::I32(-2_000_000_007)
|
|
);
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "u32")?,
|
|
FieldValue::U32(0xDEADBEEF)
|
|
);
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "i64")?,
|
|
FieldValue::I64(-9_000_000_000_000_000_000)
|
|
);
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "u64")?,
|
|
FieldValue::U64(0x0102030405060708)
|
|
);
|
|
assert_eq!(engine.read_field(&buffer, "f32")?, FieldValue::F32(1.5));
|
|
assert_eq!(engine.read_field(&buffer, "f64")?, FieldValue::F64(2.5));
|
|
assert_eq!(engine.read_field(&buffer, "b")?, FieldValue::Bool(true));
|
|
assert_eq!(engine.read_field(&buffer, "e")?, FieldValue::Enum(7));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_write_aligned_round_trips_string() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"name": { "AlkType:String": true }
|
|
}
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let offset_map = engine.offset_map().expect("aligned mode has offset_map");
|
|
let mut buffer = vec![0u8; offset_map.total_size() + 64];
|
|
engine.write_field(&mut buffer, "name", &FieldValue::String("hello world"))?;
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "name")?,
|
|
FieldValue::String("hello world")
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_write_aligned_round_trips_bytes() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"blob": { "AlkType:Bytes": true }
|
|
}
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let offset_map = engine.offset_map().expect("aligned mode has offset_map");
|
|
let payload = b"the quick brown fox".to_vec();
|
|
let mut buffer = vec![0u8; offset_map.total_size() + payload.len()];
|
|
engine.write_field(&mut buffer, "blob", &FieldValue::Bytes(&payload))?;
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "blob")?,
|
|
FieldValue::Bytes(&payload)
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_field_returns_access_error_in_packed_mode() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
let buffer = [0u8; 4];
|
|
let err = engine.read_field(&buffer, "id").unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn write_field_returns_access_error_in_packed_mode() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Packed)?;
|
|
let mut buffer = [0u8; 4];
|
|
let err = engine
|
|
.write_field(&mut buffer, "id", &FieldValue::U32(1))
|
|
.unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_field_returns_offset_error_for_missing_path() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let buffer = [0u8; 8];
|
|
let err = engine.read_field(&buffer, "missing").unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn write_field_returns_offset_error_for_missing_path() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let mut buffer = [0u8; 8];
|
|
let err = engine
|
|
.write_field(&mut buffer, "missing", &FieldValue::U32(1))
|
|
.unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Offset { .. }), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_field_returns_access_error_for_composite_types() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"vals": {
|
|
"AlkType:Array": true,
|
|
"items": { "AlkType:Uint32": true }
|
|
}
|
|
}
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let buffer = [0u8; 8];
|
|
let err = engine.read_field(&buffer, "vals").unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn write_field_returns_access_error_for_composite_value() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": { "id": { "AlkType:Uint32": true } }
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let mut buffer = [0u8; 8];
|
|
let err = engine
|
|
.write_field(&mut buffer, "id", &FieldValue::Struct { start: 0, end: 4 })
|
|
.unwrap_err();
|
|
assert!(matches!(err, AlkTypeError::Access { .. }), "got {err:?}");
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_field_aligned_reads_nested_struct_byte_range() -> Result<(), AlkTypeError> {
|
|
let mut schema = json!({
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"header": {
|
|
"AlkType:Struct": true,
|
|
"properties": {
|
|
"version": { "AlkType:Uint8": true },
|
|
"magic": { "AlkType:Uint32": true }
|
|
}
|
|
}
|
|
}
|
|
});
|
|
let engine = AlkTypeEngine::compile(&mut schema, LayoutMode::Aligned)?;
|
|
let offset_map = engine.offset_map().expect("aligned mode");
|
|
let mut buffer = vec![0u8; offset_map.total_size()];
|
|
|
|
engine.write_field(&mut buffer, "header.version", &FieldValue::U8(3))?;
|
|
engine.write_field(&mut buffer, "header.magic", &FieldValue::U32(0xCAFEBABE))?;
|
|
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "header.version")?,
|
|
FieldValue::U8(3)
|
|
);
|
|
assert_eq!(
|
|
engine.read_field(&buffer, "header.magic")?,
|
|
FieldValue::U32(0xCAFEBABE)
|
|
);
|
|
Ok(())
|
|
}
|