• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 19740735823

27 Nov 2025 03:12PM UTC coverage: 67.771% (+0.03%) from 67.745%
19740735823

push

github

web-flow
feat(rust/signed-doc): Update the signed doc specification to the 0.2.0 version (#660)

* Update the signed doc specification to the 0.2.0 version

* Use attribute instead of commenting the tests

* Add missing space

* Apply review comments

13 of 17 new or added lines in 1 file covered. (76.47%)

9 existing lines in 3 files now uncovered.

13927 of 20550 relevant lines covered (67.77%)

2912.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.46
/rust/signed_doc/src/validator/rules/content_encoding.rs
1
//! `content-encoding` rule type impl.
2

3
use std::string::ToString;
4

5
use catalyst_signed_doc_spec::is_required::IsRequired;
6

7
use crate::{CatalystSignedDocument, metadata::ContentEncoding};
8

9
/// `content-encoding` field validation rule.
10
#[derive(Debug)]
11
pub(crate) enum ContentEncodingRule {
12
    /// Content Encoding field is optionally present in the document.
13
    Specified {
14
        /// expected `content-encoding` field.
15
        exp: Vec<ContentEncoding>,
16
        /// optional flag for the `content-encoding` field.
17
        optional: bool,
18
    },
19
    /// Content Encoding field must not be present in the document.
20
    NotSpecified,
21
}
22

23
impl ContentEncodingRule {
24
    /// Create a new rule from specs.
25
    pub(crate) fn new(
1,911✔
26
        spec: &catalyst_signed_doc_spec::headers::content_encoding::ContentEncoding
1,911✔
27
    ) -> anyhow::Result<Self> {
1,911✔
28
        if let IsRequired::Excluded = spec.required {
1,911✔
UNCOV
29
            anyhow::ensure!(
×
UNCOV
30
                spec.value.is_none(),
×
31
                "'content type' field must not exist when 'required' is 'excluded'"
×
32
            );
UNCOV
33
            return Ok(Self::NotSpecified);
×
34
        }
1,911✔
35

36
        let optional = IsRequired::Optional == spec.required;
1,911✔
37

38
        let exp = spec
1,911✔
39
            .value
1,911✔
40
            .as_ref()
1,911✔
41
            .ok_or(anyhow::anyhow!("'content-encoding' field must have value "))?
1,911✔
42
            .iter()
1,911✔
43
            .flat_map(|encoding| encoding.parse())
1,911✔
44
            .collect();
1,911✔
45

46
        Ok(Self::Specified { exp, optional })
1,911✔
47
    }
1,911✔
48

49
    /// Field validation rule
50
    #[allow(clippy::unused_async)]
51
    pub(crate) async fn check(
95✔
52
        &self,
95✔
53
        doc: &CatalystSignedDocument,
95✔
54
    ) -> anyhow::Result<bool> {
95✔
55
        let context = "Content Encoding Rule check";
95✔
56
        match self {
95✔
57
            Self::NotSpecified => {
58
                if let Some(content_encoding) = doc.doc_content_encoding() {
2✔
59
                    doc.report().unknown_field(
1✔
60
                        "content-encoding",
1✔
61
                        &content_encoding.to_string(),
1✔
62
                        &format!(
1✔
63
                            "{context}, document does not expect to have a content-encoding field"
1✔
64
                        ),
1✔
65
                    );
66
                    return Ok(false);
1✔
67
                }
1✔
68
            },
69
            Self::Specified { exp, optional } => {
93✔
70
                if let Some(content_encoding) = doc.doc_content_encoding() {
93✔
71
                    if !exp.contains(&content_encoding) {
91✔
72
                        doc.report().invalid_value(
×
73
                            "content-encoding",
×
74
                            content_encoding.to_string().as_str(),
×
75
                            &exp.iter()
×
76
                                .map(ToString::to_string)
×
77
                                .collect::<Vec<_>>()
×
78
                                .join(", "),
×
79
                            "Invalid document content-encoding value",
×
80
                        );
81
                        return Ok(false);
×
82
                    }
91✔
83
                    if content_encoding.decode(doc.encoded_content()).is_err() {
91✔
84
                        doc.report().invalid_value(
16✔
85
                            "payload",
16✔
86
                            &hex::encode(doc.encoded_content()),
16✔
87
                            content_encoding.to_string().as_str(),
16✔
88
                            "Document content is not decodable with the expected content-encoding",
16✔
89
                        );
90
                        return Ok(false);
16✔
91
                    }
75✔
92
                } else if !optional {
2✔
93
                    doc.report().missing_field(
1✔
94
                        "content-encoding",
1✔
95
                        "Document must have a content-encoding field",
1✔
96
                    );
97
                    return Ok(false);
1✔
98
                }
1✔
99
            },
100
        }
101
        Ok(true)
77✔
102
    }
95✔
103
}
104

105
#[cfg(test)]
106
mod tests {
107
    use super::*;
108
    use crate::{builder::tests::Builder, metadata::SupportedField};
109

110
    #[tokio::test]
111
    async fn content_encoding_is_specified_rule_test() {
1✔
112
        let content_encoding = ContentEncoding::Brotli;
1✔
113

114
        let rule = ContentEncodingRule::Specified {
1✔
115
            exp: vec![content_encoding],
1✔
116
            optional: true,
1✔
117
        };
1✔
118

119
        let doc = Builder::new()
1✔
120
            .with_metadata_field(SupportedField::ContentEncoding(content_encoding))
1✔
121
            .with_content(content_encoding.encode(&[1, 2, 3]).unwrap())
1✔
122
            .build();
1✔
123
        assert!(rule.check(&doc).await.unwrap());
1✔
124

125
        // empty content (empty bytes) could not be brotli decoded
126
        let doc = Builder::new()
1✔
127
            .with_metadata_field(SupportedField::ContentEncoding(content_encoding))
1✔
128
            .build();
1✔
129
        assert!(!rule.check(&doc).await.unwrap());
1✔
130

131
        let doc = Builder::new().build();
1✔
132
        assert!(rule.check(&doc).await.unwrap());
1✔
133

134
        let rule = ContentEncodingRule::Specified {
1✔
135
            exp: vec![content_encoding],
1✔
136
            optional: false,
1✔
137
        };
1✔
138
        assert!(!rule.check(&doc).await.unwrap());
1✔
139
    }
1✔
140

141
    #[tokio::test]
142
    async fn content_encoding_is_not_specified_rule_test() {
1✔
143
        let content_encoding = ContentEncoding::Brotli;
1✔
144

145
        let rule = ContentEncodingRule::NotSpecified;
1✔
146

147
        // With Brotli content encoding
148
        let doc = Builder::new()
1✔
149
            .with_metadata_field(SupportedField::ContentEncoding(content_encoding))
1✔
150
            .build();
1✔
151
        assert!(!rule.check(&doc).await.unwrap());
1✔
152

153
        // No content encoding
154
        let doc = Builder::new().build();
1✔
155
        assert!(rule.check(&doc).await.unwrap());
1✔
156
    }
1✔
157
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc