Streaming Tree ARchive format
1#[cfg(test)]
2mod tests {
3 use crate::parser::StarParser;
4 use crate::ser::{StarEncoder, StarSerializer};
5 use crate::types::{
6 RepoMstEntry, RepoMstNode, StarCommit, StarItem, StarMstEntry, StarMstNode,
7 };
8 use cid::Cid;
9 use serde_bytes::ByteBuf;
10 use sha2::{Digest, Sha256};
11
12 fn create_test_cid(data: &[u8]) -> Cid {
13 let hash = Sha256::digest(data);
14 Cid::new_v1(0x71, cid::multihash::Multihash::wrap(0x12, &hash).unwrap())
15 }
16
17 #[test]
18 fn test_roundtrip_basic() {
19 // 1. Create a dummy record
20 // Use "bar" (sha256 starts with fc... -> 0 leading zeros -> height 0)
21 let record_data = b"hello world";
22 let record_cid = create_test_cid(record_data);
23 let key = b"bar".to_vec();
24
25 // 2. Create a dummy MST Node (Layer 0, implicit record)
26 let node_entry = StarMstEntry {
27 p: 0,
28 k: ByteBuf::from(key.clone()),
29 v: None,
30 v_archived: Some(true),
31 t: None,
32 t_archived: None,
33 };
34
35 let star_node = StarMstNode {
36 l: None,
37 l_archived: None,
38 e: vec![node_entry],
39 };
40
41 // 3. Manually compute the expected Node CID for verification
42 let repo_entry = RepoMstEntry {
43 p: 0,
44 k: ByteBuf::from(key.clone()),
45 v: record_cid,
46 t: None,
47 };
48 let repo_node = RepoMstNode {
49 l: None,
50 e: vec![repo_entry],
51 };
52 let repo_bytes = serde_ipld_dagcbor::to_vec(&repo_node).unwrap();
53 let node_cid = create_test_cid(&repo_bytes);
54
55 // 4. Create Commit
56 let commit = StarCommit {
57 did: "did:example:test".into(),
58 version: 3,
59 data: Some(node_cid),
60 rev: "rev1".into(),
61 prev: None,
62 sig: None,
63 };
64
65 // 5. Serialize to Buffer
66 let mut buf = Vec::new();
67 StarEncoder::write_header(&commit, &mut buf).unwrap();
68 StarEncoder::write_node(&star_node, &mut buf).unwrap();
69 StarEncoder::write_record(record_data, &mut buf).unwrap();
70
71 // 6. Deserialize and Verify
72 let mut parser = StarParser::new();
73
74 // Helper to mimic Decoder loop for tests
75 fn parse_helper(
76 parser: &mut StarParser,
77 buf: &mut Vec<u8>,
78 offset: &mut usize,
79 ) -> Option<StarItem> {
80 let (consumed, item) = parser.parse(&buf[*offset..]).unwrap();
81 *offset += consumed;
82 item
83 }
84
85 let mut offset = 0;
86
87 // Header
88 let item1 = parse_helper(&mut parser, &mut buf, &mut offset).unwrap();
89 match item1 {
90 StarItem::Commit(c) => assert_eq!(c, commit),
91 _ => panic!("Expected commit"),
92 }
93
94 // Node
95 let item2 = parse_helper(&mut parser, &mut buf, &mut offset).unwrap();
96 match item2 {
97 StarItem::Node(n) => {
98 assert_eq!(n.e[0].v, None);
99 assert_eq!(n.e[0].v_archived, Some(true));
100 }
101 _ => panic!("Expected node"),
102 }
103
104 // Record
105 let item3 = parse_helper(&mut parser, &mut buf, &mut offset).unwrap();
106 match item3 {
107 StarItem::Record {
108 key: k,
109 cid,
110 content,
111 } => {
112 assert_eq!(k, key);
113 assert_eq!(cid, record_cid);
114 assert_eq!(content.unwrap(), record_data);
115 }
116 _ => panic!("Expected record"),
117 }
118
119 // Done
120 assert!(parse_helper(&mut parser, &mut buf, &mut offset).is_none());
121 }
122
123 #[test]
124 fn test_verification_failure() {
125 // 1. Create a dummy record
126 let record_data = b"hello world";
127 let key = b"bar".to_vec(); // Height 0
128
129 // 2. Create STAR Node
130 let node_entry = StarMstEntry {
131 p: 0,
132 k: ByteBuf::from(key.clone()),
133 v: None,
134 v_archived: Some(true),
135 t: None,
136 t_archived: None,
137 };
138 let star_node = StarMstNode {
139 l: None,
140 l_archived: None,
141 e: vec![node_entry],
142 };
143
144 // 3. Create Commit with WRONG CID
145 let wrong_cid = create_test_cid(b"garbage");
146 let commit = StarCommit {
147 did: "did:example:test".into(),
148 version: 3,
149 data: Some(wrong_cid),
150 rev: "rev1".into(),
151 prev: None,
152 sig: None,
153 };
154
155 // 4. Serialize
156 let mut buf = Vec::new();
157 StarEncoder::write_header(&commit, &mut buf).unwrap();
158 StarEncoder::write_node(&star_node, &mut buf).unwrap();
159 StarEncoder::write_record(record_data, &mut buf).unwrap();
160
161 // 5. Parse
162 let mut parser = StarParser::new();
163 let mut offset = 0;
164
165 fn parse_helper(
166 parser: &mut StarParser,
167 buf: &mut Vec<u8>,
168 offset: &mut usize,
169 ) -> Result<Option<StarItem>, crate::error::StarError> {
170 let (consumed, item) = parser.parse(&buf[*offset..])?;
171 *offset += consumed;
172 Ok(item)
173 }
174
175 parse_helper(&mut parser, &mut buf, &mut offset).unwrap(); // Header OK
176 parse_helper(&mut parser, &mut buf, &mut offset).unwrap(); // Node OK
177 parse_helper(&mut parser, &mut buf, &mut offset).unwrap(); // Record OK
178
179 // 6. Trigger verification
180 let result = parse_helper(&mut parser, &mut buf, &mut offset);
181
182 assert!(result.is_err());
183 match result.unwrap_err() {
184 crate::error::StarError::VerificationFailed { .. } => {}
185 e => panic!("Expected VerificationFailed, got {:?}", e),
186 }
187 }
188
189 #[test]
190 fn test_strict_serializer() {
191 // Test that strict serializer enforces constraints
192 let mut buf = Vec::new();
193 let mut serializer = StarSerializer::new(&mut buf);
194
195 // 1. Create invalid node (root empty)
196 let invalid_node = StarMstNode {
197 l: None,
198 l_archived: None,
199 e: vec![],
200 };
201
202 // 2. Commit pointing to root
203 let cid = create_test_cid(b"foo");
204 let commit = StarCommit {
205 did: "did".into(),
206 version: 3,
207 data: Some(cid),
208 rev: "1".into(),
209 prev: None,
210 sig: None,
211 };
212
213 // Header OK
214 serializer.write_header(&commit).unwrap();
215
216 // Node Fail
217 let err = serializer.write_node(&invalid_node).unwrap_err();
218 match err {
219 crate::error::StarError::InvalidStructure(msg) => {
220 assert!(msg.contains("Root cannot be empty"), "Got message: {}", msg);
221 }
222 e => panic!("Expected InvalidStructure, got {:?}", e),
223 }
224 }
225}