this repo has no description
1use crate::sync::verify::{CarVerifier, VerifyError};
2use bytes::Bytes;
3use cid::Cid;
4use sha2::{Digest, Sha256};
5use std::collections::HashMap;
6
7fn make_cid(data: &[u8]) -> Cid {
8 let mut hasher = Sha256::new();
9 hasher.update(data);
10 let hash = hasher.finalize();
11 let multihash = multihash::Multihash::wrap(0x12, &hash).unwrap();
12 Cid::new_v1(0x71, multihash)
13}
14
15#[test]
16fn test_verifier_creation() {
17 let _verifier = CarVerifier::new();
18}
19
20#[test]
21fn test_verify_error_display() {
22 let err = VerifyError::DidMismatch {
23 commit_did: "did:plc:abc".to_string(),
24 expected_did: "did:plc:xyz".to_string(),
25 };
26 assert!(err.to_string().contains("did:plc:abc"));
27 assert!(err.to_string().contains("did:plc:xyz"));
28 let err = VerifyError::InvalidSignature;
29 assert!(err.to_string().contains("signature"));
30 let err = VerifyError::NoSigningKey;
31 assert!(err.to_string().contains("signing key"));
32 let err = VerifyError::MstValidationFailed("test error".to_string());
33 assert!(err.to_string().contains("test error"));
34}
35
36#[test]
37fn test_mst_validation_missing_root_block() {
38 let verifier = CarVerifier::new();
39 let blocks: HashMap<Cid, Bytes> = HashMap::new();
40 let fake_cid = make_cid(b"fake data");
41 let result = verifier.verify_mst_structure(&fake_cid, &blocks);
42 assert!(result.is_err());
43 let err = result.unwrap_err();
44 assert!(matches!(err, VerifyError::BlockNotFound(_)));
45}
46
47#[test]
48fn test_mst_validation_invalid_cbor() {
49 let verifier = CarVerifier::new();
50 let bad_cbor = Bytes::from(vec![0xFF, 0xFF, 0xFF]);
51 let cid = make_cid(&bad_cbor);
52 let mut blocks = HashMap::new();
53 blocks.insert(cid, bad_cbor);
54 let result = verifier.verify_mst_structure(&cid, &blocks);
55 assert!(result.is_err());
56 let err = result.unwrap_err();
57 assert!(matches!(err, VerifyError::InvalidCbor(_)));
58}
59
60#[test]
61fn test_mst_validation_empty_node() {
62 let verifier = CarVerifier::new();
63 let empty_node = serde_ipld_dagcbor::to_vec(&serde_json::json!({
64 "e": []
65 }))
66 .unwrap();
67 let cid = make_cid(&empty_node);
68 let mut blocks = HashMap::new();
69 blocks.insert(cid, Bytes::from(empty_node));
70 let result = verifier.verify_mst_structure(&cid, &blocks);
71 assert!(result.is_ok());
72}
73
74#[test]
75fn test_mst_validation_missing_left_pointer() {
76 use ipld_core::ipld::Ipld;
77
78 let verifier = CarVerifier::new();
79 let missing_left_cid = make_cid(b"missing left");
80 let node = Ipld::Map(std::collections::BTreeMap::from([
81 ("l".to_string(), Ipld::Link(missing_left_cid)),
82 ("e".to_string(), Ipld::List(vec![])),
83 ]));
84 let node_bytes = serde_ipld_dagcbor::to_vec(&node).unwrap();
85 let cid = make_cid(&node_bytes);
86 let mut blocks = HashMap::new();
87 blocks.insert(cid, Bytes::from(node_bytes));
88 let result = verifier.verify_mst_structure(&cid, &blocks);
89 assert!(result.is_err());
90 let err = result.unwrap_err();
91 assert!(matches!(err, VerifyError::BlockNotFound(_)));
92 assert!(err.to_string().contains("left pointer"));
93}
94
95#[test]
96fn test_mst_validation_missing_subtree() {
97 use ipld_core::ipld::Ipld;
98
99 let verifier = CarVerifier::new();
100 let missing_subtree_cid = make_cid(b"missing subtree");
101 let record_cid = make_cid(b"record");
102 let entry = Ipld::Map(std::collections::BTreeMap::from([
103 ("k".to_string(), Ipld::Bytes(b"key1".to_vec())),
104 ("v".to_string(), Ipld::Link(record_cid)),
105 ("p".to_string(), Ipld::Integer(0)),
106 ("t".to_string(), Ipld::Link(missing_subtree_cid)),
107 ]));
108 let node = Ipld::Map(std::collections::BTreeMap::from([(
109 "e".to_string(),
110 Ipld::List(vec![entry]),
111 )]));
112 let node_bytes = serde_ipld_dagcbor::to_vec(&node).unwrap();
113 let cid = make_cid(&node_bytes);
114 let mut blocks = HashMap::new();
115 blocks.insert(cid, Bytes::from(node_bytes));
116 let result = verifier.verify_mst_structure(&cid, &blocks);
117 assert!(result.is_err());
118 let err = result.unwrap_err();
119 assert!(matches!(err, VerifyError::BlockNotFound(_)));
120 assert!(err.to_string().contains("subtree"));
121}
122
123#[test]
124fn test_mst_validation_unsorted_keys() {
125 use ipld_core::ipld::Ipld;
126
127 let verifier = CarVerifier::new();
128 let record_cid = make_cid(b"record");
129 let entry1 = Ipld::Map(std::collections::BTreeMap::from([
130 ("k".to_string(), Ipld::Bytes(b"zzz".to_vec())),
131 ("v".to_string(), Ipld::Link(record_cid)),
132 ("p".to_string(), Ipld::Integer(0)),
133 ]));
134 let entry2 = Ipld::Map(std::collections::BTreeMap::from([
135 ("k".to_string(), Ipld::Bytes(b"aaa".to_vec())),
136 ("v".to_string(), Ipld::Link(record_cid)),
137 ("p".to_string(), Ipld::Integer(0)),
138 ]));
139 let node = Ipld::Map(std::collections::BTreeMap::from([(
140 "e".to_string(),
141 Ipld::List(vec![entry1, entry2]),
142 )]));
143 let node_bytes = serde_ipld_dagcbor::to_vec(&node).unwrap();
144 let cid = make_cid(&node_bytes);
145 let mut blocks = HashMap::new();
146 blocks.insert(cid, Bytes::from(node_bytes));
147 let result = verifier.verify_mst_structure(&cid, &blocks);
148 assert!(result.is_err());
149 let err = result.unwrap_err();
150 assert!(matches!(err, VerifyError::MstValidationFailed(_)));
151 assert!(err.to_string().contains("sorted"));
152}
153
154#[test]
155fn test_mst_validation_sorted_keys_ok() {
156 use ipld_core::ipld::Ipld;
157
158 let verifier = CarVerifier::new();
159 let record_cid = make_cid(b"record");
160 let entry1 = Ipld::Map(std::collections::BTreeMap::from([
161 ("k".to_string(), Ipld::Bytes(b"aaa".to_vec())),
162 ("v".to_string(), Ipld::Link(record_cid)),
163 ("p".to_string(), Ipld::Integer(0)),
164 ]));
165 let entry2 = Ipld::Map(std::collections::BTreeMap::from([
166 ("k".to_string(), Ipld::Bytes(b"bbb".to_vec())),
167 ("v".to_string(), Ipld::Link(record_cid)),
168 ("p".to_string(), Ipld::Integer(0)),
169 ]));
170 let entry3 = Ipld::Map(std::collections::BTreeMap::from([
171 ("k".to_string(), Ipld::Bytes(b"zzz".to_vec())),
172 ("v".to_string(), Ipld::Link(record_cid)),
173 ("p".to_string(), Ipld::Integer(0)),
174 ]));
175 let node = Ipld::Map(std::collections::BTreeMap::from([(
176 "e".to_string(),
177 Ipld::List(vec![entry1, entry2, entry3]),
178 )]));
179 let node_bytes = serde_ipld_dagcbor::to_vec(&node).unwrap();
180 let cid = make_cid(&node_bytes);
181 let mut blocks = HashMap::new();
182 blocks.insert(cid, Bytes::from(node_bytes));
183 let result = verifier.verify_mst_structure(&cid, &blocks);
184 assert!(result.is_ok());
185}
186
187#[test]
188fn test_mst_validation_with_valid_left_pointer() {
189 use ipld_core::ipld::Ipld;
190
191 let verifier = CarVerifier::new();
192 let left_node = Ipld::Map(std::collections::BTreeMap::from([(
193 "e".to_string(),
194 Ipld::List(vec![]),
195 )]));
196 let left_node_bytes = serde_ipld_dagcbor::to_vec(&left_node).unwrap();
197 let left_cid = make_cid(&left_node_bytes);
198 let root_node = Ipld::Map(std::collections::BTreeMap::from([
199 ("l".to_string(), Ipld::Link(left_cid)),
200 ("e".to_string(), Ipld::List(vec![])),
201 ]));
202 let root_node_bytes = serde_ipld_dagcbor::to_vec(&root_node).unwrap();
203 let root_cid = make_cid(&root_node_bytes);
204 let mut blocks = HashMap::new();
205 blocks.insert(root_cid, Bytes::from(root_node_bytes));
206 blocks.insert(left_cid, Bytes::from(left_node_bytes));
207 let result = verifier.verify_mst_structure(&root_cid, &blocks);
208 assert!(result.is_ok());
209}
210
211#[test]
212fn test_mst_validation_cycle_detection() {
213 let verifier = CarVerifier::new();
214 let node = serde_ipld_dagcbor::to_vec(&serde_json::json!({
215 "e": []
216 }))
217 .unwrap();
218 let cid = make_cid(&node);
219 let mut blocks = HashMap::new();
220 blocks.insert(cid, Bytes::from(node));
221 let result = verifier.verify_mst_structure(&cid, &blocks);
222 assert!(result.is_ok());
223}
224
225#[tokio::test]
226async fn test_unsupported_did_method() {
227 let verifier = CarVerifier::new();
228 let result = verifier.resolve_did_document("did:unknown:test").await;
229 assert!(result.is_err());
230 let err = result.unwrap_err();
231 assert!(matches!(err, VerifyError::DidResolutionFailed(_)));
232 assert!(err.to_string().contains("Unsupported"));
233}
234
235#[test]
236fn test_mst_validation_with_prefix_compression() {
237 use ipld_core::ipld::Ipld;
238
239 let verifier = CarVerifier::new();
240 let record_cid = make_cid(b"record");
241 let entry1 = Ipld::Map(std::collections::BTreeMap::from([
242 (
243 "k".to_string(),
244 Ipld::Bytes(b"app.bsky.feed.post/abc".to_vec()),
245 ),
246 ("v".to_string(), Ipld::Link(record_cid)),
247 ("p".to_string(), Ipld::Integer(0)),
248 ]));
249 let entry2 = Ipld::Map(std::collections::BTreeMap::from([
250 ("k".to_string(), Ipld::Bytes(b"def".to_vec())),
251 ("v".to_string(), Ipld::Link(record_cid)),
252 ("p".to_string(), Ipld::Integer(19)),
253 ]));
254 let entry3 = Ipld::Map(std::collections::BTreeMap::from([
255 ("k".to_string(), Ipld::Bytes(b"xyz".to_vec())),
256 ("v".to_string(), Ipld::Link(record_cid)),
257 ("p".to_string(), Ipld::Integer(19)),
258 ]));
259 let node = Ipld::Map(std::collections::BTreeMap::from([(
260 "e".to_string(),
261 Ipld::List(vec![entry1, entry2, entry3]),
262 )]));
263 let node_bytes = serde_ipld_dagcbor::to_vec(&node).unwrap();
264 let cid = make_cid(&node_bytes);
265 let mut blocks = HashMap::new();
266 blocks.insert(cid, Bytes::from(node_bytes));
267 let result = verifier.verify_mst_structure(&cid, &blocks);
268 assert!(
269 result.is_ok(),
270 "Prefix-compressed keys should be validated correctly"
271 );
272}
273
274#[test]
275fn test_mst_validation_prefix_compression_unsorted() {
276 use ipld_core::ipld::Ipld;
277
278 let verifier = CarVerifier::new();
279 let record_cid = make_cid(b"record");
280 let entry1 = Ipld::Map(std::collections::BTreeMap::from([
281 (
282 "k".to_string(),
283 Ipld::Bytes(b"app.bsky.feed.post/xyz".to_vec()),
284 ),
285 ("v".to_string(), Ipld::Link(record_cid)),
286 ("p".to_string(), Ipld::Integer(0)),
287 ]));
288 let entry2 = Ipld::Map(std::collections::BTreeMap::from([
289 ("k".to_string(), Ipld::Bytes(b"abc".to_vec())),
290 ("v".to_string(), Ipld::Link(record_cid)),
291 ("p".to_string(), Ipld::Integer(19)),
292 ]));
293 let node = Ipld::Map(std::collections::BTreeMap::from([(
294 "e".to_string(),
295 Ipld::List(vec![entry1, entry2]),
296 )]));
297 let node_bytes = serde_ipld_dagcbor::to_vec(&node).unwrap();
298 let cid = make_cid(&node_bytes);
299 let mut blocks = HashMap::new();
300 blocks.insert(cid, Bytes::from(node_bytes));
301 let result = verifier.verify_mst_structure(&cid, &blocks);
302 assert!(
303 result.is_err(),
304 "Unsorted prefix-compressed keys should fail validation"
305 );
306 let err = result.unwrap_err();
307 assert!(matches!(err, VerifyError::MstValidationFailed(_)));
308}