ic_http_certification/tree/
certification_tree_entry.rs

1use super::certification_tree_path::{HttpCertificationPath, InnerTreePath};
2use crate::HttpCertification;
3use std::borrow::Cow;
4
5/// An entry in an [HttpCertificationTree](crate::HttpCertificationTree).
6///
7/// It requires two properties:
8///
9/// - [path](HttpCertificationTreeEntry::path) specifies the path of an
10/// [HttpCertification] definition within the tree. This path will define
11/// what [HttpRequest](crate::HttpRequest) URLs the
12/// [certification](HttpCertificationTreeEntry::certification) will be valid for.
13///
14/// - [certification](HttpCertificationTreeEntry::certification) that specifies the
15/// [HttpCertification] definition itself.
16///
17/// Use the [new](HttpCertificationTreeEntry::new) associated function to create a new `HttpCertificationTreeEntry`.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct HttpCertificationTreeEntry<'a> {
20    /// The path of an [HttpCertification] definition within the tree.
21    /// This path will define what [HttpRequest](crate::HttpRequest) URLs the
22    /// [certification](HttpCertificationTreeEntry::certification) will be valid for.
23    pub path: Cow<'a, HttpCertificationPath<'a>>,
24
25    /// The [HttpCertification] definition itself.
26    pub certification: Cow<'a, HttpCertification>,
27}
28
29impl<'a> HttpCertificationTreeEntry<'a> {
30    /// Creates a new [HttpCertificationTreeEntry] with the given `path` and `certification`.
31    /// This is a convenience method for creating a [HttpCertificationTreeEntry]
32    /// without having to directly deal with the [Cow] type.
33    pub fn new(
34        path: impl Into<Cow<'a, HttpCertificationPath<'a>>>,
35        certification: impl Into<Cow<'a, HttpCertification>>,
36    ) -> Self {
37        Self {
38            path: path.into(),
39            certification: certification.into(),
40        }
41    }
42
43    pub(super) fn to_tree_path(&self) -> InnerTreePath {
44        let mut tree_path = vec![];
45        tree_path.append(&mut self.path.to_tree_path());
46        tree_path.append(&mut self.certification.to_tree_path());
47
48        tree_path
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::{
56        request_hash, response_hash, DefaultCelBuilder, DefaultResponseCertification, HttpRequest,
57        HttpResponse, StatusCode, CERTIFICATE_EXPRESSION_HEADER_NAME,
58    };
59    use ic_representation_independent_hash::hash;
60    use rstest::*;
61    use rstest_reuse::*;
62
63    #[template]
64    #[rstest]
65    #[case(HttpCertificationPath::exact("/foo/bar"), vec!["foo", "bar", "<$>"])]
66    #[case(HttpCertificationPath::wildcard("/foo/bar"), vec!["foo", "bar", "<*>"])]
67    fn certification_paths(
68        #[case] path: HttpCertificationPath<'static>,
69        #[case] expected: Vec<&str>,
70    ) {
71    }
72
73    #[apply(certification_paths)]
74    fn skip_certification_path(
75        #[case] path: HttpCertificationPath<'static>,
76        #[case] expected: Vec<&str>,
77    ) {
78        let cel_expr = DefaultCelBuilder::skip_certification().to_string();
79        let cel_expr_hash = hash(cel_expr.as_bytes());
80
81        let certification = HttpCertification::skip();
82        let entry = HttpCertificationTreeEntry::new(&path, certification);
83
84        let result = entry.to_tree_path();
85
86        let path_segments: Vec<_> = expected
87            .into_iter()
88            .map(|segment| segment.as_bytes().to_vec())
89            .collect();
90        let expected = vec![cel_expr_hash.to_vec().to_owned()];
91
92        let expected: Vec<_> = path_segments
93            .into_iter()
94            .chain(expected.into_iter())
95            .collect();
96
97        assert_eq!(result, expected);
98    }
99
100    #[apply(certification_paths)]
101    fn response_only_certification_path(
102        #[case] path: HttpCertificationPath<'static>,
103        #[case] expected: Vec<&str>,
104    ) {
105        let cel_expr = DefaultCelBuilder::response_only_certification()
106            .with_response_certification(DefaultResponseCertification::certified_response_headers(
107                vec![],
108            ))
109            .build();
110        let cel_expr_hash = hash(cel_expr.to_string().as_bytes());
111        let response = HttpResponse::builder()
112            .with_status_code(StatusCode::OK)
113            .with_headers(vec![(
114                CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(),
115                cel_expr.to_string(),
116            )])
117            .build();
118        let expected_response_hash = response_hash(&response, &cel_expr.response, None);
119
120        let certification = HttpCertification::response_only(&cel_expr, &response, None).unwrap();
121        let entry = HttpCertificationTreeEntry::new(&path, certification);
122
123        let result = entry.to_tree_path();
124
125        let path_segments: Vec<_> = expected
126            .into_iter()
127            .map(|segment| segment.as_bytes().to_vec())
128            .collect();
129        let expected = vec![
130            cel_expr_hash.to_vec().to_owned(),
131            "".as_bytes().to_vec(),
132            expected_response_hash.to_vec(),
133        ];
134
135        let expected: Vec<_> = path_segments
136            .into_iter()
137            .chain(expected.into_iter())
138            .collect();
139
140        assert_eq!(result, expected);
141    }
142
143    #[apply(certification_paths)]
144    fn full_certification_path(
145        #[case] path: HttpCertificationPath<'static>,
146        #[case] expected: Vec<&str>,
147    ) {
148        let cel_expr = DefaultCelBuilder::full_certification().build();
149        let cel_expr_hash = hash(cel_expr.to_string().as_bytes());
150
151        let request = HttpRequest::get("/index.html").build();
152        let expected_request_hash = request_hash(&request, &cel_expr.request).unwrap();
153
154        let response = HttpResponse::builder()
155            .with_status_code(StatusCode::OK)
156            .with_headers(vec![(
157                CERTIFICATE_EXPRESSION_HEADER_NAME.to_string(),
158                cel_expr.to_string(),
159            )])
160            .build();
161        let expected_response_hash = response_hash(&response, &cel_expr.response, None);
162
163        let certification = HttpCertification::full(&cel_expr, &request, &response, None).unwrap();
164        let entry = HttpCertificationTreeEntry::new(&path, certification);
165
166        let result = entry.to_tree_path();
167
168        let path_segments: Vec<_> = expected
169            .into_iter()
170            .map(|segment| segment.as_bytes().to_vec())
171            .collect();
172        let expected = vec![
173            cel_expr_hash.to_vec().to_owned(),
174            expected_request_hash.to_vec(),
175            expected_response_hash.to_vec(),
176        ];
177
178        let expected: Vec<_> = path_segments
179            .into_iter()
180            .chain(expected.into_iter())
181            .collect();
182
183        assert_eq!(result, expected);
184    }
185}