#![forbid(unsafe_code)]
use std::error::Error;
use haematite_cli::serve::contracts::{Contract, MutationRequest, Receipt, ResolvedMutation};
use haematite_cli::serve::protocol::Version;
use serde_json::{Value, json};
type TestResult = Result<(), Box<dyn Error>>;
const fn version(sequence: u64) -> Version {
Version {
generation: 7,
sequence,
}
}
fn request_value() -> Value {
json!({
"request_id": "write-1", "branch": "main",
"caller": {"issuer": "local", "subject": "1001"},
"contract": {"kind": "exact_version", "expected": version(2)},
"change": {"kind": "document.put", "value": {
"collection": "charts", "document_id": "chart-1", "document": {"value": 1}
}}
})
}
fn resolve(value: &Value) -> Result<ResolvedMutation, Box<dyn Error>> {
let request = MutationRequest::decode(&serde_json::to_vec(value)?)?;
Ok(request.resolve("1001")?)
}
#[test]
fn exact_contract_keeps_the_declared_expected_version() -> TestResult {
let request = resolve(&request_value())?;
assert!(
matches!(request.contract(), Contract::ExactVersion { expected } if *expected == version(2))
);
assert_eq!(request.origin(), version(2));
Ok(())
}
#[test]
fn dependencies_keep_origin_and_absence_and_empty_interval_reads() -> TestResult {
let mut value = request_value();
value["contract"] = json!({"kind": "dependencies", "origin": version(1), "reads": [
{"kind": "document", "collection": "charts", "document_id": "absent"},
{"kind": "collection", "collection": "charts"},
{"kind": "source_interval", "collection": "charts", "lower": "a", "upper": "a"}
]});
let request = resolve(&value)?;
let Contract::Dependencies { origin, reads } = request.contract() else {
return Err("dependency contract was lost".into());
};
assert_eq!(*origin, version(1));
assert_eq!(reads.len(), 3);
assert_eq!(request.origin(), version(1));
Ok(())
}
#[test]
fn missing_caller_resolves_to_the_socket_owner_without_changing_explicit_callers() -> TestResult {
let mut value = request_value();
let object = value.as_object_mut().ok_or("request is not an object")?;
object.remove("caller");
let request = MutationRequest::decode(&serde_json::to_vec(&value)?)?;
let resolved = request.resolve("2048")?;
assert_eq!(resolved.caller().issuer(), "local");
assert_eq!(resolved.caller().subject(), "2048");
let explicit =
MutationRequest::decode(&serde_json::to_vec(&request_value())?)?.resolve("2048")?;
assert_eq!(explicit.caller().subject(), "1001");
Ok(())
}
#[test]
fn explicit_null_caller_never_becomes_the_socket_owner() -> TestResult {
let mut value = request_value();
value["caller"] = Value::Null;
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_err());
Ok(())
}
#[test]
fn omitted_and_explicit_owner_have_the_same_resolved_identity() -> TestResult {
let explicit = request_value();
let mut omitted = explicit.clone();
omitted
.as_object_mut()
.ok_or("request is not an object")?
.remove("caller");
assert_eq!(
resolve(&omitted)?.fingerprint()?,
resolve(&explicit)?.fingerprint()?
);
Ok(())
}
#[test]
fn an_invalid_owner_is_never_assigned_to_an_anonymous_request() -> TestResult {
let mut value = request_value();
value
.as_object_mut()
.ok_or("request is not an object")?
.remove("caller");
let request = MutationRequest::decode(&serde_json::to_vec(&value)?)?;
let error = request.resolve("").err().ok_or("invalid owner accepted")?;
assert_eq!(error.code, "invalid_identifier");
Ok(())
}
#[test]
fn responsible_person_survives_receipt_and_changes_request_identity() -> TestResult {
let mut value = request_value();
value["caller"]["responsible"] = json!("person-1");
let first = resolve(&value)?;
let receipt = Receipt::for_request(&first, version(3))?;
let decoded = Receipt::decode(&serde_json::to_vec(&receipt)?)?;
assert_eq!(decoded.caller().responsible(), Some("person-1"));
value["caller"]["responsible"] = json!("person-2");
assert_ne!(first.fingerprint()?, resolve(&value)?.fingerprint()?);
Ok(())
}
#[test]
fn identity_limits_count_utf8_bytes_and_refuse_oversized_request_names() -> TestResult {
let mut value = request_value();
value["caller"]["subject"] = json!("é".repeat(256));
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_ok());
value["caller"]["subject"] = json!("é".repeat(257));
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_err());
let mut value = request_value();
value["request_id"] = json!("x".repeat(513));
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_err());
Ok(())
}
#[test]
fn malformed_identity_and_credentials_are_refused_at_decode() -> TestResult {
for caller in [
json!({"issuer": "", "subject": "1001"}),
json!({"issuer": "local", "subject": ""}),
json!({"issuer": "local", "subject": "a\u{0000}b"}),
json!({"issuer": "local", "subject": "1001", "responsible": ""}),
json!({"issuer": "local", "subject": "1001", "token": "credential"}),
] {
let mut value = request_value();
value["caller"] = caller;
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_err());
}
Ok(())
}
#[test]
fn old_top_level_expected_and_unknown_contract_fields_are_refused() -> TestResult {
let mut old = request_value();
let object = old.as_object_mut().ok_or("request is not an object")?;
object.remove("contract");
object.insert("expected".to_owned(), json!(version(2)));
assert!(MutationRequest::decode(&serde_json::to_vec(&old)?).is_err());
let mut unknown = request_value();
unknown["contract"]["origin"] = json!(version(1));
assert!(MutationRequest::decode(&serde_json::to_vec(&unknown)?).is_err());
Ok(())
}
#[test]
fn unsupported_predicates_and_physical_root_dependencies_are_refused() -> TestResult {
for read in [
json!({"kind": "predicate", "expression": "value > 0"}),
json!({"kind": "unique_index", "index": "identity", "key": "x"}),
json!({"kind": "physical_root", "root": "00"}),
] {
let mut value = request_value();
value["contract"] = json!({"kind": "dependencies", "origin": version(1), "reads": [read]});
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_err());
}
Ok(())
}
#[test]
fn reversed_intervals_and_invalid_dependency_names_are_refused() -> TestResult {
for read in [
json!({"kind": "source_interval", "collection": "charts", "lower": "z", "upper": "a"}),
json!({"kind": "document", "collection": "charts", "document_id": ""}),
json!({"kind": "collection", "collection": ""}),
] {
let mut value = request_value();
value["contract"] = json!({"kind": "dependencies", "origin": version(1), "reads": [read]});
assert!(MutationRequest::decode(&serde_json::to_vec(&value)?).is_err());
}
Ok(())
}
#[test]
fn different_callers_reusing_a_request_id_never_receive_the_first_receipt() -> TestResult {
let first = resolve(&request_value())?;
let receipt = Receipt::for_request(&first, version(3))?;
let mut value = request_value();
value["caller"]["subject"] = json!("1002");
let second = resolve(&value)?;
assert_ne!(first.fingerprint()?, second.fingerprint()?);
let error = receipt
.replay(&second)
.err()
.ok_or("another caller received a receipt")?;
assert_eq!(error.code, "request_id_reused");
Ok(())
}
#[test]
fn unchanged_replay_preserves_original_origin_and_installed_version() -> TestResult {
let mut value = request_value();
value["contract"] = json!({"kind": "dependencies", "origin": version(1), "reads": []});
let request = resolve(&value)?;
let receipt = Receipt::for_request(&request, version(8))?;
let encoded = serde_json::to_vec(&receipt)?;
let decoded = Receipt::decode(&encoded)?;
let replay = decoded.replay(&resolve(&value)?)?;
assert_eq!(replay.request_origin(), version(1));
assert_eq!(replay.version(), version(8));
assert_eq!(serde_json::to_vec(replay)?, encoded);
Ok(())
}
#[test]
fn branch_id_origin_contract_reads_and_change_all_bind_the_fingerprint() -> TestResult {
let original = request_value();
let fingerprint = resolve(&original)?.fingerprint()?;
let changes = [
("request_id", json!("write-2")),
("branch", json!("other")),
(
"contract",
json!({"kind": "exact_version", "expected": version(1)}),
),
(
"contract",
json!({"kind": "dependencies", "origin": version(2), "reads": []}),
),
(
"change",
json!({"kind": "document.delete", "value": {"collection": "charts", "document_id": "chart-1"}}),
),
];
for (field, replacement) in changes {
let mut value = original.clone();
value[field] = replacement;
assert_ne!(fingerprint, resolve(&value)?.fingerprint()?);
}
let mut dependencies = original;
dependencies["contract"] = json!({"kind": "dependencies", "origin": version(1), "reads": []});
let empty = resolve(&dependencies)?.fingerprint()?;
dependencies["contract"]["reads"] = json!([{"kind": "collection", "collection": "charts"}]);
assert_ne!(empty, resolve(&dependencies)?.fingerprint()?);
Ok(())
}
#[test]
fn receipt_has_format_two_and_never_infers_a_missing_stored_author() -> TestResult {
let request = resolve(&request_value())?;
let receipt = Receipt::for_request(&request, version(3))?;
let mut value = serde_json::to_value(&receipt)?;
assert_eq!(value["format"], 2);
assert_eq!(value["fingerprint_format"], 1);
assert_eq!(value["contract_kind"], "exact_version");
assert_eq!(value["request_origin"], json!(version(2)));
value
.as_object_mut()
.ok_or("receipt is not an object")?
.remove("caller");
assert!(Receipt::decode(&serde_json::to_vec(&value)?).is_err());
Ok(())
}
#[test]
fn old_and_future_receipt_formats_have_a_named_refusal() -> TestResult {
let old = json!({"request_id": "write-1", "fingerprint": "00", "branch": "main", "version": version(3)});
let error = Receipt::decode(&serde_json::to_vec(&old)?)
.err()
.ok_or("old receipt accepted")?;
assert_eq!(error.code, "stored_receipt_format_unsupported");
let request = resolve(&request_value())?;
let mut future = serde_json::to_value(Receipt::for_request(&request, version(3))?)?;
future["format"] = json!(3);
let error = Receipt::decode(&serde_json::to_vec(&future)?)
.err()
.ok_or("future receipt accepted")?;
assert_eq!(error.code, "stored_receipt_format_unsupported");
Ok(())
}
#[test]
fn unsupported_fingerprint_format_and_noncanonical_digest_are_refused() -> TestResult {
let request = resolve(&request_value())?;
let original = serde_json::to_value(Receipt::for_request(&request, version(3))?)?;
let mut future = original.clone();
future["fingerprint_format"] = json!(2);
let error = Receipt::decode(&serde_json::to_vec(&future)?)
.err()
.ok_or("future fingerprint accepted")?;
assert_eq!(error.code, "stored_fingerprint_format_unsupported");
for digest in ["00".to_owned(), "A".repeat(64), "g".repeat(64)] {
let mut value = original.clone();
value["fingerprint"] = json!(digest);
assert!(Receipt::decode(&serde_json::to_vec(&value)?).is_err());
}
Ok(())
}
#[test]
fn a_receipt_from_another_key_is_corruption_not_a_replayed_answer() -> TestResult {
let request = resolve(&request_value())?;
let mut value = serde_json::to_value(Receipt::for_request(&request, version(3))?)?;
value["request_id"] = json!("other-request");
let receipt = Receipt::decode(&serde_json::to_vec(&value)?)?;
let error = receipt.replay(&request).err().ok_or("wrong key replayed")?;
assert_eq!(error.code, "receipt_corrupt");
Ok(())
}
#[test]
fn tampering_with_the_stored_caller_never_returns_an_answer() -> TestResult {
let request = resolve(&request_value())?;
let mut value = serde_json::to_value(Receipt::for_request(&request, version(3))?)?;
value["caller"]["subject"] = json!("1002");
let receipt = Receipt::decode(&serde_json::to_vec(&value)?)?;
let error = receipt
.replay(&request)
.err()
.ok_or("altered caller replayed")?;
assert_eq!(error.code, "request_id_reused");
Ok(())
}
#[test]
fn fork_receipt_names_the_destination_and_keeps_the_source_origin() -> TestResult {
let mut value = request_value();
value["change"] = json!({"kind": "branch.fork", "value": {"name": "work", "from": version(1)}});
let request = resolve(&value)?;
let installed = Version {
generation: 9,
sequence: 0,
};
let receipt = Receipt::for_request(&request, installed)?;
assert_eq!(receipt.branch(), "work");
assert_eq!(receipt.request_origin(), version(2));
assert_eq!(receipt.version(), installed);
assert!(receipt.replay(&request).is_ok());
Ok(())
}
#[test]
fn document_payload_bytes_are_preserved_and_part_of_identity() -> TestResult {
let first = br#"{"request_id":"write-1","branch":"main","contract":{"kind":"exact_version","expected":{"generation":"7","sequence":"2"}},"change":{"kind":"document.put","value":{"collection":"charts","document_id":"chart-1","document":{ "z": 1.00, "a": 18446744073709551616001 }}}}"#;
let second = br#"{"request_id":"write-1","branch":"main","contract":{"kind":"exact_version","expected":{"generation":"7","sequence":"2"}},"change":{"kind":"document.put","value":{"collection":"charts","document_id":"chart-1","document":{"z":1.00,"a":18446744073709551616001}}}}"#;
let left = MutationRequest::decode(first)?.resolve("1001")?;
let right = MutationRequest::decode(second)?.resolve("1001")?;
let encoded = serde_json::to_string(&left)?;
assert!(encoded.contains(r#"{ "z": 1.00, "a": 18446744073709551616001 }"#));
assert_ne!(left.fingerprint()?, right.fingerprint()?);
Ok(())
}