1use super::{
2 certification_tree_entry::HttpCertificationTreeEntry,
3 certification_tree_path::CertificationTreePathSegment,
4};
5use crate::{
6 tree::HttpCertificationPathType,
7 utils::{more_specific_wildcards_for, PATH_PREFIX_BYTES},
8 HttpCertificationError, HttpCertificationPath, HttpCertificationResult,
9};
10use ic_certification::{labeled, labeled_hash, merge_hash_trees, AsHashTree, HashTree, NestedTree};
11use ic_representation_independent_hash::Sha256Digest;
12use std::fmt::{Debug, Formatter};
13
14type CertificationTree = NestedTree<CertificationTreePathSegment, Vec<u8>>;
15
16#[derive(Clone)]
18pub struct HttpCertificationTree {
19 tree: CertificationTree,
20}
21
22impl Debug for HttpCertificationTree {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 write!(f, "tree: {:#?}", self.tree)
25 }
26}
27
28impl Default for HttpCertificationTree {
29 fn default() -> Self {
30 Self::new(CertificationTree::default())
31 }
32}
33
34impl HttpCertificationTree {
35 pub fn new(tree: CertificationTree) -> Self {
38 Self { tree }
39 }
40
41 pub fn root_hash(&self) -> Sha256Digest {
44 labeled_hash(PATH_PREFIX_BYTES, &self.tree.root_hash())
45 }
46
47 pub fn insert(&mut self, entry: &HttpCertificationTreeEntry) {
51 let tree_path = entry.to_tree_path();
52 self.tree.insert(&tree_path, vec![]);
53 }
54
55 pub fn delete(&mut self, entry: &HttpCertificationTreeEntry) {
59 let tree_path = entry.to_tree_path();
60 self.tree.delete(&tree_path);
61 }
62
63 pub fn delete_by_path(&mut self, path: &HttpCertificationPath) {
67 let tree_path = path.to_tree_path();
68 self.tree.delete(&tree_path);
69 }
70
71 pub fn clear(&mut self) {
75 self.tree.clear();
76 }
77
78 pub fn witness(
83 &self,
84 entry: &HttpCertificationTreeEntry,
85 request_url: &str,
86 ) -> HttpCertificationResult<HashTree> {
87 let witness = match entry.path.get_type() {
88 HttpCertificationPathType::Exact(_) => self.tree.witness(&entry.to_tree_path()),
89
90 HttpCertificationPathType::Wildcard(wildcard_path) => {
91 let requested_tree_path = HttpCertificationPath::exact(request_url).to_tree_path();
92 let responding_tree_path = entry.path.to_tree_path();
93
94 if responding_tree_path.len() > requested_tree_path.len() {
95 return Err(HttpCertificationError::WildcardPathNotValidForRequestPath {
96 wildcard_path: wildcard_path.to_string(),
97 request_path: request_url.to_string(),
98 });
99 }
100
101 let witness = self.tree.witness(&requested_tree_path);
104
105 let witness = merge_hash_trees(witness, self.tree.witness(&responding_tree_path));
107
108 more_specific_wildcards_for(&requested_tree_path, &responding_tree_path)
111 .iter()
112 .fold(witness, |acc, path| {
113 merge_hash_trees(acc, self.tree.witness(path))
114 })
115 }
116 };
117
118 Ok(labeled(PATH_PREFIX_BYTES, witness))
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::{
126 DefaultCelBuilder, DefaultResponseCertification, HttpCertification, HttpRequest,
127 HttpResponse, StatusCode, CERTIFICATE_EXPRESSION_HEADER_NAME,
128 };
129 use assert_matches::assert_matches;
130 use ic_certification::SubtreeLookupResult;
131 use rstest::*;
132
133 #[rstest]
134 fn test_witness() {
135 let mut tree = HttpCertificationTree::default();
136
137 let cel_expr = DefaultCelBuilder::full_certification()
138 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
139 vec![],
140 ))
141 .build();
142
143 let not_found_request = HttpRequest::get("/assets/js/not-found.js").build();
144 let not_found_response = HttpResponse::not_found(
145 br#"404 Not Found"#,
146 vec![(
147 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
148 cel_expr.to_string(),
149 )],
150 )
151 .build();
152
153 let hello_world_request = HttpRequest::get("/assets/js/hello-world.js").build();
154 let hello_world_response = HttpResponse::not_found(
155 br#"console.log("Hello, World!")"#,
156 vec![(
157 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
158 cel_expr.to_string(),
159 )],
160 )
161 .build();
162
163 let not_found_entry = HttpCertificationTreeEntry::new(
164 HttpCertificationPath::wildcard("/assets/js"),
165 HttpCertification::full(&cel_expr, ¬_found_request, ¬_found_response, None)
166 .unwrap(),
167 );
168 tree.insert(¬_found_entry);
169
170 let hello_world_entry = HttpCertificationTreeEntry::new(
171 HttpCertificationPath::exact(hello_world_request.url()),
172 HttpCertification::full(&cel_expr, &hello_world_request, &hello_world_response, None)
173 .unwrap(),
174 );
175 tree.insert(&hello_world_entry);
176
177 let not_found_witness = tree.witness(¬_found_entry, "/assets/js/0.js").unwrap();
178
179 assert_eq!(
180 not_found_witness.lookup_subtree(["http_expr", "<*>"]),
181 SubtreeLookupResult::Absent
182 );
183 assert_eq!(
184 not_found_witness.lookup_subtree(["http_expr", "", "<*>"]),
185 SubtreeLookupResult::Absent
186 );
187 assert_eq!(
188 not_found_witness.lookup_subtree(["http_expr", "assets", "<*>"]),
189 SubtreeLookupResult::Absent
190 );
191 assert_eq!(
192 not_found_witness.lookup_subtree(["http_expr", "assets", "", "<*>"]),
193 SubtreeLookupResult::Absent
194 );
195 assert_matches!(
196 not_found_witness.lookup_subtree(["http_expr", "assets", "js", "<*>"]),
197 SubtreeLookupResult::Found(_)
198 );
199 assert_eq!(
200 not_found_witness.lookup_subtree(["http_expr", "assets", "js", "0.js", "<*>"]),
201 SubtreeLookupResult::Absent
202 );
203
204 let hello_world_witness = tree
205 .witness(&hello_world_entry, "/assets/js/hello-world.js")
206 .unwrap();
207
208 assert_matches!(
209 hello_world_witness.lookup_subtree([
210 "http_expr",
211 "assets",
212 "js",
213 "hello-world.js",
214 "<$>"
215 ]),
216 SubtreeLookupResult::Found(_)
217 );
218 }
219
220 #[rstest]
221 fn test_delete_by_path() {
222 let mut http_tree = HttpCertificationTree::default();
223 let cel_expr = DefaultCelBuilder::full_certification()
224 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
225 vec![],
226 ))
227 .build();
228 let req_one_url = "/assets/js/hello.js";
229 let req_two_url = "/assets/js/world.js";
230
231 let req_one = HttpRequest::get(req_one_url).build();
253 let req_two = HttpRequest::get(req_two_url).build();
254
255 let res = HttpResponse::builder()
256 .with_status_code(StatusCode::OK)
257 .with_body(br#"console.log("Hello, World!")"#)
258 .with_headers(vec![(
259 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
260 cel_expr.to_string(),
261 )])
262 .build();
263 let alt_res = HttpResponse::builder()
264 .with_status_code(StatusCode::OK)
265 .with_body(br#"console.log("Hello, ALT World!")"#)
266 .with_headers(vec![(
267 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
268 cel_expr.to_string(),
269 )])
270 .build();
271
272 let req_one_entry = HttpCertificationTreeEntry::new(
273 HttpCertificationPath::exact(req_one.url()),
274 HttpCertification::full(&cel_expr, &req_one, &res, None).unwrap(),
275 );
276 http_tree.insert(&req_one_entry);
277
278 let req_one_alt_entry = HttpCertificationTreeEntry::new(
279 HttpCertificationPath::exact(req_one.url()),
280 HttpCertification::full(&cel_expr, &req_one, &alt_res, None).unwrap(),
281 );
282 http_tree.insert(&req_one_alt_entry);
283
284 let req_two_entry = HttpCertificationTreeEntry::new(
285 HttpCertificationPath::exact(req_two.url()),
286 HttpCertification::full(&cel_expr, &req_two, &res, None).unwrap(),
287 );
288 http_tree.insert(&req_two_entry);
289
290 let req_two_alt_entry = HttpCertificationTreeEntry::new(
291 HttpCertificationPath::exact(req_two.url()),
292 HttpCertification::full(&cel_expr, &req_two, &alt_res, None).unwrap(),
293 );
294 http_tree.insert(&req_two_alt_entry);
295
296 assert_matches!(
297 http_tree
298 .witness(&req_one_entry, req_one_url)
299 .unwrap()
300 .lookup_subtree(&lookup_path_from_entry(&req_one_entry)),
301 SubtreeLookupResult::Found(_)
302 );
303 assert_matches!(
304 http_tree
305 .witness(&req_one_alt_entry, req_one_url)
306 .unwrap()
307 .lookup_subtree(&lookup_path_from_entry(&req_one_alt_entry)),
308 SubtreeLookupResult::Found(_)
309 );
310
311 assert_matches!(
312 http_tree
313 .witness(&req_two_entry, req_two_url)
314 .unwrap()
315 .lookup_subtree(&lookup_path_from_entry(&req_two_entry)),
316 SubtreeLookupResult::Found(_)
317 );
318 assert_matches!(
319 http_tree
320 .witness(&req_two_alt_entry, req_two_url)
321 .unwrap()
322 .lookup_subtree(&lookup_path_from_entry(&req_two_alt_entry)),
323 SubtreeLookupResult::Found(_)
324 );
325
326 assert!(http_tree
327 .tree
328 .contains_path(&HttpCertificationPath::exact(req_one_url).to_tree_path()));
329 assert!(http_tree.tree.contains_path(&req_one_entry.to_tree_path()));
330 assert!(http_tree
331 .tree
332 .contains_path(&req_one_alt_entry.to_tree_path()));
333
334 assert!(http_tree
335 .tree
336 .contains_path(&HttpCertificationPath::exact(req_two_url).to_tree_path()));
337 assert!(http_tree.tree.contains_path(&req_two_entry.to_tree_path()));
338 assert!(http_tree
339 .tree
340 .contains_path(&req_two_alt_entry.to_tree_path()));
341
342 http_tree.delete_by_path(&HttpCertificationPath::exact(req_one.url()));
344
345 assert_matches!(
346 http_tree
347 .witness(&req_one_entry, req_one_url)
348 .unwrap()
349 .lookup_subtree(&lookup_path_from_entry(&req_one_entry)),
350 SubtreeLookupResult::Absent
351 );
352 assert_matches!(
353 http_tree
354 .witness(&req_one_alt_entry, req_one_url)
355 .unwrap()
356 .lookup_subtree(&lookup_path_from_entry(&req_one_alt_entry)),
357 SubtreeLookupResult::Absent
358 );
359
360 assert_matches!(
361 http_tree
362 .witness(&req_two_entry, req_two_url)
363 .unwrap()
364 .lookup_subtree(&lookup_path_from_entry(&req_two_entry)),
365 SubtreeLookupResult::Found(_)
366 );
367 assert_matches!(
368 http_tree
369 .witness(&req_two_alt_entry, req_two_url)
370 .unwrap()
371 .lookup_subtree(&lookup_path_from_entry(&req_two_alt_entry)),
372 SubtreeLookupResult::Found(_)
373 );
374
375 assert!(!http_tree
376 .tree
377 .contains_path(&HttpCertificationPath::exact(req_one_url).to_tree_path()));
378 assert!(!http_tree.tree.contains_path(&req_one_entry.to_tree_path()));
379 assert!(!http_tree
380 .tree
381 .contains_path(&req_one_alt_entry.to_tree_path()));
382
383 assert!(http_tree
384 .tree
385 .contains_path(&HttpCertificationPath::exact(req_two_url).to_tree_path()));
386 assert!(http_tree.tree.contains_path(&req_two_entry.to_tree_path()));
387 assert!(http_tree
388 .tree
389 .contains_path(&req_two_alt_entry.to_tree_path()));
390
391 http_tree.delete_by_path(&HttpCertificationPath::exact(req_two.url()));
393
394 assert_matches!(
395 http_tree
396 .witness(&req_one_entry, req_one_url)
397 .unwrap()
398 .lookup_subtree(&lookup_path_from_entry(&req_one_entry)),
399 SubtreeLookupResult::Absent
400 );
401 assert_matches!(
402 http_tree
403 .witness(&req_one_alt_entry, req_one_url)
404 .unwrap()
405 .lookup_subtree(&lookup_path_from_entry(&req_one_alt_entry)),
406 SubtreeLookupResult::Absent
407 );
408
409 assert_matches!(
410 http_tree
411 .witness(&req_two_entry, req_two_url)
412 .unwrap()
413 .lookup_subtree(&lookup_path_from_entry(&req_two_entry)),
414 SubtreeLookupResult::Absent
415 );
416 assert_matches!(
417 http_tree
418 .witness(&req_two_alt_entry, req_two_url)
419 .unwrap()
420 .lookup_subtree(&lookup_path_from_entry(&req_two_alt_entry)),
421 SubtreeLookupResult::Absent
422 );
423
424 assert!(!http_tree
425 .tree
426 .contains_path(&HttpCertificationPath::exact(req_one_url).to_tree_path()));
427 assert!(!http_tree.tree.contains_path(&req_one_entry.to_tree_path()));
428 assert!(!http_tree
429 .tree
430 .contains_path(&req_one_alt_entry.to_tree_path()));
431
432 assert!(!http_tree
433 .tree
434 .contains_path(&HttpCertificationPath::exact(req_two_url).to_tree_path()));
435 assert!(!http_tree.tree.contains_path(&req_two_entry.to_tree_path()));
436 assert!(!http_tree
437 .tree
438 .contains_path(&req_two_alt_entry.to_tree_path()));
439 }
440
441 #[rstest]
442 fn delete_removes_empty_subpaths() {
443 let mut http_tree = HttpCertificationTree::default();
444 let cel_expr = DefaultCelBuilder::full_certification()
445 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
446 vec![],
447 ))
448 .build();
449 let req_url = "/assets/js/hello-world.js";
450
451 let get_request = HttpRequest::get(req_url).build();
471 let post_request = HttpRequest::post(req_url).build();
472
473 let response = HttpResponse::ok(
474 br#"console.log("Hello, World!")"#,
475 vec![(
476 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
477 cel_expr.to_string(),
478 )],
479 )
480 .build();
481 let alt_response = HttpResponse::ok(
482 br#"console.log("Hello, ALT World!")"#,
483 vec![(
484 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
485 cel_expr.to_string(),
486 )],
487 )
488 .build();
489
490 let get_entry = HttpCertificationTreeEntry::new(
491 HttpCertificationPath::exact(get_request.url()),
492 HttpCertification::full(&cel_expr, &get_request, &response, None).unwrap(),
493 );
494 http_tree.insert(&get_entry);
495
496 let post_entry = HttpCertificationTreeEntry::new(
497 HttpCertificationPath::exact(post_request.url()),
498 HttpCertification::full(&cel_expr, &post_request, &response, None).unwrap(),
499 );
500 http_tree.insert(&post_entry);
501
502 let alt_get_entry = HttpCertificationTreeEntry::new(
503 HttpCertificationPath::exact(get_request.url()),
504 HttpCertification::full(&cel_expr, &get_request, &alt_response, None).unwrap(),
505 );
506 http_tree.insert(&alt_get_entry);
507
508 let alt_post_entry = HttpCertificationTreeEntry::new(
509 HttpCertificationPath::exact(post_request.url()),
510 HttpCertification::full(&cel_expr, &post_request, &alt_response, None).unwrap(),
511 );
512 http_tree.insert(&alt_post_entry);
513
514 assert_matches!(
515 http_tree
516 .witness(&get_entry, req_url)
517 .unwrap()
518 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
519 SubtreeLookupResult::Found(_)
520 );
521 assert_matches!(
522 http_tree
523 .witness(&post_entry, req_url)
524 .unwrap()
525 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
526 SubtreeLookupResult::Found(_)
527 );
528 assert_matches!(
529 http_tree
530 .witness(&alt_get_entry, req_url)
531 .unwrap()
532 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
533 SubtreeLookupResult::Found(_)
534 );
535 assert_matches!(
536 http_tree
537 .witness(&alt_post_entry, req_url)
538 .unwrap()
539 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
540 SubtreeLookupResult::Found(_)
541 );
542
543 assert!(http_tree
544 .tree
545 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
546 assert!(http_tree.tree.contains_path(&get_entry.to_tree_path()));
547 assert!(http_tree.tree.contains_path(&post_entry.to_tree_path()));
548 assert!(http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
549 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
550
551 http_tree.delete(&get_entry);
553
554 assert_matches!(
555 http_tree
556 .witness(&get_entry, req_url)
557 .unwrap()
558 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
559 SubtreeLookupResult::Absent
560 );
561 assert_matches!(
562 http_tree
563 .witness(&post_entry, req_url)
564 .unwrap()
565 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
566 SubtreeLookupResult::Found(_)
567 );
568 assert_matches!(
569 http_tree
570 .witness(&alt_get_entry, req_url)
571 .unwrap()
572 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
573 SubtreeLookupResult::Found(_)
574 );
575 assert_matches!(
576 http_tree
577 .witness(&alt_post_entry, req_url)
578 .unwrap()
579 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
580 SubtreeLookupResult::Found(_)
581 );
582
583 assert!(http_tree
584 .tree
585 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
586 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
587 assert!(http_tree.tree.contains_path(&post_entry.to_tree_path()));
588 assert!(http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
589 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
590
591 http_tree.delete(&post_entry);
593
594 assert_matches!(
595 http_tree
596 .witness(&get_entry, req_url)
597 .unwrap()
598 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
599 SubtreeLookupResult::Absent
600 );
601 assert_matches!(
602 http_tree
603 .witness(&post_entry, req_url)
604 .unwrap()
605 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
606 SubtreeLookupResult::Absent
607 );
608 assert_matches!(
609 http_tree
610 .witness(&alt_get_entry, req_url)
611 .unwrap()
612 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
613 SubtreeLookupResult::Found(_)
614 );
615 assert_matches!(
616 http_tree
617 .witness(&alt_post_entry, req_url)
618 .unwrap()
619 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
620 SubtreeLookupResult::Found(_)
621 );
622
623 assert!(http_tree
624 .tree
625 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
626 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
627 assert!(!http_tree.tree.contains_path(&post_entry.to_tree_path()));
628 assert!(http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
629 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
630
631 http_tree.delete(&alt_get_entry);
633
634 assert_matches!(
635 http_tree
636 .witness(&get_entry, req_url)
637 .unwrap()
638 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
639 SubtreeLookupResult::Absent
640 );
641 assert_matches!(
642 http_tree
643 .witness(&post_entry, req_url)
644 .unwrap()
645 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
646 SubtreeLookupResult::Absent
647 );
648 assert_matches!(
649 http_tree
650 .witness(&alt_get_entry, req_url)
651 .unwrap()
652 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
653 SubtreeLookupResult::Absent
654 );
655 assert_matches!(
656 http_tree
657 .witness(&alt_post_entry, req_url)
658 .unwrap()
659 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
660 SubtreeLookupResult::Found(_)
661 );
662
663 assert!(http_tree
664 .tree
665 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
666 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
667 assert!(!http_tree.tree.contains_path(&post_entry.to_tree_path()));
668 assert!(!http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
669 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
670
671 http_tree.delete(&alt_post_entry);
673
674 assert_matches!(
675 http_tree
676 .witness(&get_entry, req_url)
677 .unwrap()
678 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
679 SubtreeLookupResult::Absent
680 );
681 assert_matches!(
682 http_tree
683 .witness(&post_entry, req_url)
684 .unwrap()
685 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
686 SubtreeLookupResult::Absent
687 );
688 assert_matches!(
689 http_tree
690 .witness(&alt_get_entry, req_url)
691 .unwrap()
692 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
693 SubtreeLookupResult::Absent
694 );
695 assert_matches!(
696 http_tree
697 .witness(&alt_post_entry, req_url)
698 .unwrap()
699 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
700 SubtreeLookupResult::Absent
701 );
702
703 assert!(!http_tree
704 .tree
705 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
706 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
707 assert!(!http_tree.tree.contains_path(&post_entry.to_tree_path()));
708 assert!(!http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
709 assert!(!http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
710 }
711
712 #[rstest]
713 fn test_witness_wildcard_too_long() {
714 let mut tree = HttpCertificationTree::default();
715
716 let cel_expr = DefaultCelBuilder::full_certification()
717 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
718 vec![],
719 ))
720 .build();
721
722 let not_found_request = HttpRequest::get("/assets/js/not-found.js").build();
723 let not_found_response = HttpResponse::not_found(
724 br#"404 Not Found"#,
725 vec![(
726 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
727 cel_expr.to_string(),
728 )],
729 )
730 .build();
731
732 let not_found_entry = HttpCertificationTreeEntry::new(
733 HttpCertificationPath::wildcard("/assets/js"),
734 HttpCertification::full(&cel_expr, ¬_found_request, ¬_found_response, None)
735 .unwrap(),
736 );
737 tree.insert(¬_found_entry);
738
739 let witness = tree.witness(¬_found_entry, "/assets");
740
741 assert_matches!(
742 witness,
743 Err(HttpCertificationError::WildcardPathNotValidForRequestPath { .. })
744 );
745 }
746
747 #[rstest]
748 fn test_witness_wildcard_matches_asset() {
749 let mut tree = HttpCertificationTree::default();
750
751 let cel_expr = DefaultCelBuilder::full_certification()
752 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
753 vec![],
754 ))
755 .build();
756
757 let index_html_request = HttpRequest::get("/").build();
758
759 let index_html_body = b"<html><body><h1>Hello World!</h1></body></html>".to_vec();
760 let index_html_response = HttpResponse::not_found(
761 index_html_body,
762 vec![(
763 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
764 cel_expr.to_string(),
765 )],
766 )
767 .build();
768
769 let certification =
770 HttpCertification::full(&cel_expr, &index_html_request, &index_html_response, None)
771 .unwrap();
772 let index_html_entry =
773 HttpCertificationTreeEntry::new(HttpCertificationPath::wildcard("/"), certification);
774 tree.insert(&index_html_entry);
775
776 let witness = tree.witness(&index_html_entry, "/").unwrap();
777
778 let mut path = index_html_entry.to_tree_path();
779 path.insert(0, b"http_expr".to_vec());
780
781 assert_matches!(witness.lookup_subtree(&path), SubtreeLookupResult::Found(_));
782 }
783
784 fn lookup_path_from_entry(entry: &HttpCertificationTreeEntry) -> Vec<Vec<u8>> {
785 let mut lookup_path = entry.to_tree_path();
786 lookup_path.insert(0, b"http_expr".to_vec());
787 lookup_path
788 }
789}