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

input-output-hk / catalyst-libs / 19157489699

07 Nov 2025 03:44AM UTC coverage: 68.549% (+0.1%) from 68.442%
19157489699

Pull #545

github

web-flow
Merge 258367de4 into b703f0134
Pull Request #545: feat(docs): Formalize Voting Formats as signed documents.

1 of 1 new or added line in 1 file covered. (100.0%)

26 existing lines in 5 files now uncovered.

13923 of 20311 relevant lines covered (68.55%)

2906.42 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::{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(
147✔
26
        spec: &catalyst_signed_doc_spec::headers::content_encoding::ContentEncoding
147✔
27
    ) -> anyhow::Result<Self> {
147✔
28
        if let IsRequired::Excluded = spec.required {
147✔
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
        }
147✔
35

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

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

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

49
    /// Field validation rule
50
    #[allow(clippy::unused_async)]
51
    pub(crate) async fn check(
11✔
52
        &self,
11✔
53
        doc: &CatalystSignedDocument,
11✔
54
    ) -> anyhow::Result<bool> {
11✔
55
        let context = "Content Encoding Rule check";
11✔
56
        match self {
11✔
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 } => {
9✔
70
                if let Some(content_encoding) = doc.doc_content_encoding() {
9✔
71
                    if !exp.contains(&content_encoding) {
7✔
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
                    }
7✔
83
                    if content_encoding.decode(doc.encoded_content()).is_err() {
7✔
84
                        doc.report().invalid_value(
2✔
85
                            "payload",
2✔
86
                            &hex::encode(doc.encoded_content()),
2✔
87
                            content_encoding.to_string().as_str(),
2✔
88
                            "Document content is not decodable with the expected content-encoding",
2✔
89
                        );
90
                        return Ok(false);
2✔
91
                    }
5✔
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)
7✔
102
    }
11✔
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