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

input-output-hk / catalyst-libs / 17607529411

10 Sep 2025 08:13AM UTC coverage: 68.644%. First build
17607529411

Pull #547

github

web-flow
Merge 749f4d63d into 6e806ce0d
Pull Request #547: feat(rust/signed-doc): Catalyst Signed Documents validation initialisation fully during the runtime

142 of 155 new or added lines in 7 files covered. (91.61%)

13606 of 19821 relevant lines covered (68.64%)

2736.58 hits per line

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

89.58
/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::{metadata::ContentEncoding, CatalystSignedDocument};
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(
336✔
26
        spec: &catalyst_signed_doc_spec::headers::content_encoding::ContentEncoding
336✔
27
    ) -> anyhow::Result<Self> {
336✔
28
        if let IsRequired::Excluded = spec.required {
336✔
29
            anyhow::ensure!(
16✔
30
                spec.value.is_none(),
16✔
NEW
31
                "'content type' field must not exist when 'required' is 'excluded'"
×
32
            );
33
            return Ok(Self::NotSpecified);
16✔
34
        }
320✔
35

36
        let optional = IsRequired::Optional == spec.required;
320✔
37

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

46
        Ok(Self::Specified { exp, optional })
320✔
47
    }
336✔
48

49
    /// Field validation rule
50
    #[allow(clippy::unused_async)]
51
    pub(crate) async fn check(
20✔
52
        &self,
20✔
53
        doc: &CatalystSignedDocument,
20✔
54
    ) -> anyhow::Result<bool> {
20✔
55
        let context = "Content Encoding Rule check";
20✔
56
        match self {
20✔
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 } => {
18✔
70
                if let Some(content_encoding) = doc.doc_content_encoding() {
18✔
71
                    if !exp.contains(&content_encoding) {
16✔
72
                        doc.report().invalid_value(
×
73
                            "content-encoding",
×
74
                            content_encoding.to_string().as_str(),
×
NEW
75
                            &exp.iter()
×
NEW
76
                                .map(ToString::to_string)
×
NEW
77
                                .collect::<Vec<_>>()
×
NEW
78
                                .join(", "),
×
NEW
79
                            "Invalid document content-encoding value",
×
80
                        );
81
                        return Ok(false);
×
82
                    }
16✔
83
                    if content_encoding.decode(doc.encoded_content()).is_err() {
16✔
84
                        doc.report().invalid_value(
1✔
85
                            "payload",
1✔
86
                            &hex::encode(doc.encoded_content()),
1✔
87
                            content_encoding.to_string().as_str(),
1✔
88
                            "Document content is not decodable with the expected content-encoding",
1✔
89
                        );
90
                        return Ok(false);
1✔
91
                    }
15✔
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)
17✔
102
    }
20✔
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