#![allow(clippy::expect_used, clippy::indexing_slicing)]
use prikk_object::{
CanonicalWriter, CreateFile, NodeId, ObjectId, Operation, OperationKind,
PATCH_PARENT_IDS_RETIRED_SCHEMA,
};
use crate::patch_replay::decode::{DecodedOperationKind, decode_patch_operations};
fn one_create_file_operation() -> Operation {
Operation {
op_seq: 1,
op_id: None,
preconditions: Vec::new(),
kind: OperationKind::CreateFile(CreateFile {
path: "schema2.txt".to_string(),
node_id: NodeId::from_bytes([0x9c; 32]),
blob_id: ObjectId::from_bytes([0x9d; 32]),
mode: 0o100_644,
}),
}
}
fn patch_bytes_with_raw_tag2(parent_patch_ids: &[ObjectId]) -> Vec<u8> {
let mut writer = CanonicalWriter::new();
writer
.repeated_record_list(1, &[one_create_file_operation()])
.expect("operations record list encodes");
writer
.repeated_object_id(2, parent_patch_ids)
.expect("parent_patch_ids record encodes");
writer.finish()
}
#[test]
fn schema_1_patch_with_field_2_present_decodes_unchanged() {
let with_field_2 = patch_bytes_with_raw_tag2(&[ObjectId::from_bytes([0x9e; 32])]);
let without_field_2 = patch_bytes_with_raw_tag2(&[]);
let decoded_with = decode_patch_operations(&with_field_2, 1)
.expect("schema 1 must still decode a non-empty tag 2 -- it is legal there");
let decoded_without = decode_patch_operations(&without_field_2, 1)
.expect("schema 1 decodes a patch with no tag 2 at all");
assert_eq!(
decoded_with, decoded_without,
"tag 2's value must have zero effect on the decoded operations at schema 1, exactly as \
before this schema existed"
);
assert_eq!(decoded_with.len(), 1);
assert!(matches!(
decoded_with[0].kind,
DecodedOperationKind::CreateFile { .. }
));
}
#[test]
fn schema_2_patch_with_field_2_present_is_refused() {
let bytes = patch_bytes_with_raw_tag2(&[ObjectId::from_bytes([0x9e; 32])]);
let error = decode_patch_operations(&bytes, PATCH_PARENT_IDS_RETIRED_SCHEMA)
.expect_err("schema 2 must refuse a patch carrying tag 2");
let message = error.to_string();
assert!(
message.contains("parent_patch_ids") && message.contains("retired"),
"expected a retired-tag-2 refusal naming parent_patch_ids, got: {message}"
);
}
#[test]
fn schema_2_patch_without_field_2_decodes_normally() {
let schema1_bytes = patch_bytes_with_raw_tag2(&[]);
let schema2_bytes = patch_bytes_with_raw_tag2(&[]);
assert_eq!(
schema1_bytes, schema2_bytes,
"bytes are schema-independent by construction here"
);
let decoded = decode_patch_operations(&schema2_bytes, PATCH_PARENT_IDS_RETIRED_SCHEMA)
.expect("schema 2 must accept a patch that never carries tag 2 at all");
assert_eq!(decoded.len(), 1);
assert!(matches!(
decoded[0].kind,
DecodedOperationKind::CreateFile { .. }
));
}