#![forbid(unsafe_code)]
use haematite_cli::serve::protocol::{Change, Operation, Request, Version};
#[test]
fn document_wire_preserves_payload_bytes_and_large_version_ids() -> Result<(), serde_json::Error> {
let json = r#"{"id":"connection-1","operation":{"method":"mutation.apply","params":{
"request_id":"durable-1","branch":"main",
"expected":{"generation":"18446744073709551615","sequence":"1"},
"change":{"kind":"document.put","value":{"collection":"events","document_id":"a",
"document":{ "z": 1.00, "a": 18446744073709551616001 }}}
}}}"#;
let request: Request = serde_json::from_str(json)?;
let Operation::Mutate(mutation) = request.operation else {
return Err(serde::de::Error::custom("expected mutation"));
};
assert_eq!(mutation.expected.generation, u64::MAX);
let Change::Put(document) = mutation.change else {
return Err(serde::de::Error::custom("expected put"));
};
assert_eq!(
document.document.get(),
r#"{ "z": 1.00, "a": 18446744073709551616001 }"#
);
Ok(())
}
#[test]
fn versions_round_trip_without_browser_number_precision_loss() -> Result<(), serde_json::Error> {
let version = Version {
generation: u64::MAX,
sequence: u64::MAX,
};
let encoded = serde_json::to_string(&version)?;
assert_eq!(
encoded,
r#"{"generation":"18446744073709551615","sequence":"18446744073709551615"}"#
);
assert_eq!(serde_json::from_str::<Version>(&encoded)?, version);
for invalid in [r#""01""#, r#""+1""#, "1", r#""18446744073709551616""#] {
assert!(
serde_json::from_str::<Version>(&format!(
r#"{{"generation":{invalid},"sequence":"0"}}"#
))
.is_err()
);
}
Ok(())
}
#[test]
fn requests_refuse_unknown_fields_methods_and_zero_pages() {
for invalid in [
r#"{"id":"a","extra":true,"operation":{"method":"hello"}}"#,
r#"{"id":"a","operation":{"method":"execute"}}"#,
r#"{"id":"a","operation":{"method":"documents.list","params":{"selection":{"branch":"main"},"collection":"c","limit":0}}}"#,
r#"{"id":"a","operation":{"method":"collections.list","params":{"branch":"main","anything":true}}}"#,
] {
assert!(
serde_json::from_str::<Request>(invalid).is_err(),
"accepted {invalid}"
);
}
}
#[test]
fn collection_schema_removal_requires_explicit_null() -> Result<(), serde_json::Error> {
use haematite_cli::serve::protocol::CollectionWrite;
assert!(serde_json::from_str::<CollectionWrite>(r#"{"collection":"events"}"#).is_err());
let removed: CollectionWrite =
serde_json::from_str(r#"{"collection":"events","schema":null}"#)?;
assert!(removed.schema.is_none());
let refused: CollectionWrite =
serde_json::from_str(r#"{"collection":"events","schema":false}"#)?;
assert_eq!(refused.schema, Some(serde_json::Value::Bool(false)));
Ok(())
}
#[test]
fn vector_batch_wire_is_declared_and_strict() -> Result<(), serde_json::Error> {
assert!(Change::WIRE_NAMES.contains(&"vectors.put_many"));
let value = r#"{"kind":"vectors.put_many","value":{"vectors":[{"collection":"notes","document_id":"a","field":"supplied","vector":[1.0,2.0]}]}}"#;
let change: Change = serde_json::from_str(value)?;
let Change::VectorPutMany(import) = change else {
return Err(serde::de::Error::custom("expected vector batch"));
};
assert_eq!(import.vectors.len(), 1);
assert_eq!(import.vectors[0].document_id, "a");
for invalid in [
r#"{"kind":"vectors.put_many","value":{"vectors":[],"extra":true}}"#,
r#"{"kind":"vectors.put_many","value":{"vectors":[{"collection":"notes","document_id":"a","field":"supplied","vector":[1.0],"extra":true}]}}"#,
] {
assert!(serde_json::from_str::<Change>(invalid).is_err());
}
Ok(())
}
#[test]
fn delete_batch_wire_is_declared_and_strict() -> Result<(), serde_json::Error> {
assert!(Change::WIRE_NAMES.contains(&"documents.delete_many"));
let value = r#"{"kind":"documents.delete_many","value":{"collection":"notes","document_ids":["b","a"]}}"#;
let change: Change = serde_json::from_str(value)?;
let Change::DeleteMany(delete) = change else {
return Err(serde::de::Error::custom("expected delete batch"));
};
assert_eq!(delete.collection, "notes");
assert_eq!(delete.document_ids, ["b", "a"]);
assert!(
serde_json::from_str::<Change>(
r#"{"kind":"documents.delete_many","value":{"collection":"notes","document_ids":[],"extra":true}}"#,
)
.is_err()
);
Ok(())
}