Skip to main content

mockforge_bench/conformance/
spec.rs

1//! Conformance feature definitions and bundled reference spec
2
3/// OpenAPI 3.0.0 feature categories for conformance testing
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum ConformanceFeature {
6    // Parameters
7    PathParamString,
8    PathParamInteger,
9    QueryParamString,
10    QueryParamInteger,
11    QueryParamArray,
12    HeaderParam,
13    CookieParam,
14    // Request Bodies
15    BodyJson,
16    BodyFormUrlencoded,
17    BodyMultipart,
18    // Schema Types
19    SchemaString,
20    SchemaInteger,
21    SchemaNumber,
22    SchemaBoolean,
23    SchemaArray,
24    SchemaObject,
25    // Composition
26    CompositionOneOf,
27    CompositionAnyOf,
28    CompositionAllOf,
29    // String Formats
30    FormatDate,
31    FormatDateTime,
32    FormatEmail,
33    FormatUuid,
34    FormatUri,
35    FormatIpv4,
36    FormatIpv6,
37    // Constraints
38    ConstraintRequired,
39    ConstraintOptional,
40    ConstraintMinMax,
41    ConstraintPattern,
42    ConstraintEnum,
43    // Response Codes
44    Response200,
45    Response201,
46    Response204,
47    Response400,
48    Response404,
49    // HTTP Methods
50    MethodGet,
51    MethodPost,
52    MethodPut,
53    MethodPatch,
54    MethodDelete,
55    MethodHead,
56    MethodOptions,
57    // Content Negotiation
58    ContentNegotiation,
59    // Security
60    SecurityBearer,
61    SecurityApiKey,
62    SecurityBasic,
63}
64
65impl ConformanceFeature {
66    /// Get the category name for this feature
67    pub fn category(&self) -> &'static str {
68        match self {
69            Self::PathParamString
70            | Self::PathParamInteger
71            | Self::QueryParamString
72            | Self::QueryParamInteger
73            | Self::QueryParamArray
74            | Self::HeaderParam
75            | Self::CookieParam => "Parameters",
76            Self::BodyJson | Self::BodyFormUrlencoded | Self::BodyMultipart => "Request Bodies",
77            Self::SchemaString
78            | Self::SchemaInteger
79            | Self::SchemaNumber
80            | Self::SchemaBoolean
81            | Self::SchemaArray
82            | Self::SchemaObject => "Schema Types",
83            Self::CompositionOneOf | Self::CompositionAnyOf | Self::CompositionAllOf => {
84                "Composition"
85            }
86            Self::FormatDate
87            | Self::FormatDateTime
88            | Self::FormatEmail
89            | Self::FormatUuid
90            | Self::FormatUri
91            | Self::FormatIpv4
92            | Self::FormatIpv6 => "String Formats",
93            Self::ConstraintRequired
94            | Self::ConstraintOptional
95            | Self::ConstraintMinMax
96            | Self::ConstraintPattern
97            | Self::ConstraintEnum => "Constraints",
98            Self::Response200
99            | Self::Response201
100            | Self::Response204
101            | Self::Response400
102            | Self::Response404 => "Response Codes",
103            Self::MethodGet
104            | Self::MethodPost
105            | Self::MethodPut
106            | Self::MethodPatch
107            | Self::MethodDelete
108            | Self::MethodHead
109            | Self::MethodOptions => "HTTP Methods",
110            Self::ContentNegotiation => "Content Types",
111            Self::SecurityBearer | Self::SecurityApiKey | Self::SecurityBasic => "Security",
112        }
113    }
114
115    /// Get the check name used in k6 scripts (maps back from k6 output)
116    pub fn check_name(&self) -> &'static str {
117        match self {
118            Self::PathParamString => "param:path:string",
119            Self::PathParamInteger => "param:path:integer",
120            Self::QueryParamString => "param:query:string",
121            Self::QueryParamInteger => "param:query:integer",
122            Self::QueryParamArray => "param:query:array",
123            Self::HeaderParam => "param:header",
124            Self::CookieParam => "param:cookie",
125            Self::BodyJson => "body:json",
126            Self::BodyFormUrlencoded => "body:form-urlencoded",
127            Self::BodyMultipart => "body:multipart",
128            Self::SchemaString => "schema:string",
129            Self::SchemaInteger => "schema:integer",
130            Self::SchemaNumber => "schema:number",
131            Self::SchemaBoolean => "schema:boolean",
132            Self::SchemaArray => "schema:array",
133            Self::SchemaObject => "schema:object",
134            Self::CompositionOneOf => "composition:oneOf",
135            Self::CompositionAnyOf => "composition:anyOf",
136            Self::CompositionAllOf => "composition:allOf",
137            Self::FormatDate => "format:date",
138            Self::FormatDateTime => "format:date-time",
139            Self::FormatEmail => "format:email",
140            Self::FormatUuid => "format:uuid",
141            Self::FormatUri => "format:uri",
142            Self::FormatIpv4 => "format:ipv4",
143            Self::FormatIpv6 => "format:ipv6",
144            Self::ConstraintRequired => "constraint:required",
145            Self::ConstraintOptional => "constraint:optional",
146            Self::ConstraintMinMax => "constraint:minmax",
147            Self::ConstraintPattern => "constraint:pattern",
148            Self::ConstraintEnum => "constraint:enum",
149            Self::Response200 => "response:200",
150            Self::Response201 => "response:201",
151            Self::Response204 => "response:204",
152            Self::Response400 => "response:400",
153            Self::Response404 => "response:404",
154            Self::MethodGet => "method:GET",
155            Self::MethodPost => "method:POST",
156            Self::MethodPut => "method:PUT",
157            Self::MethodPatch => "method:PATCH",
158            Self::MethodDelete => "method:DELETE",
159            Self::MethodHead => "method:HEAD",
160            Self::MethodOptions => "method:OPTIONS",
161            Self::ContentNegotiation => "content:negotiation",
162            Self::SecurityBearer => "security:bearer",
163            Self::SecurityApiKey => "security:apikey",
164            Self::SecurityBasic => "security:basic",
165        }
166    }
167
168    /// All feature variants
169    pub fn all() -> &'static [ConformanceFeature] {
170        &[
171            Self::PathParamString,
172            Self::PathParamInteger,
173            Self::QueryParamString,
174            Self::QueryParamInteger,
175            Self::QueryParamArray,
176            Self::HeaderParam,
177            Self::CookieParam,
178            Self::BodyJson,
179            Self::BodyFormUrlencoded,
180            Self::BodyMultipart,
181            Self::SchemaString,
182            Self::SchemaInteger,
183            Self::SchemaNumber,
184            Self::SchemaBoolean,
185            Self::SchemaArray,
186            Self::SchemaObject,
187            Self::CompositionOneOf,
188            Self::CompositionAnyOf,
189            Self::CompositionAllOf,
190            Self::FormatDate,
191            Self::FormatDateTime,
192            Self::FormatEmail,
193            Self::FormatUuid,
194            Self::FormatUri,
195            Self::FormatIpv4,
196            Self::FormatIpv6,
197            Self::ConstraintRequired,
198            Self::ConstraintOptional,
199            Self::ConstraintMinMax,
200            Self::ConstraintPattern,
201            Self::ConstraintEnum,
202            Self::Response200,
203            Self::Response201,
204            Self::Response204,
205            Self::Response400,
206            Self::Response404,
207            Self::MethodGet,
208            Self::MethodPost,
209            Self::MethodPut,
210            Self::MethodPatch,
211            Self::MethodDelete,
212            Self::MethodHead,
213            Self::MethodOptions,
214            Self::ContentNegotiation,
215            Self::SecurityBearer,
216            Self::SecurityApiKey,
217            Self::SecurityBasic,
218        ]
219    }
220
221    /// All unique categories
222    pub fn categories() -> &'static [&'static str] {
223        &[
224            "Parameters",
225            "Request Bodies",
226            "Schema Types",
227            "Composition",
228            "String Formats",
229            "Constraints",
230            "Response Codes",
231            "HTTP Methods",
232            "Content Types",
233            "Security",
234        ]
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn test_all_features_have_categories() {
244        for feature in ConformanceFeature::all() {
245            assert!(!feature.category().is_empty());
246            assert!(!feature.check_name().is_empty());
247        }
248    }
249
250    #[test]
251    fn test_all_categories_covered() {
252        let categories: std::collections::HashSet<&str> =
253            ConformanceFeature::all().iter().map(|f| f.category()).collect();
254        for cat in ConformanceFeature::categories() {
255            assert!(categories.contains(cat), "Category '{}' has no features", cat);
256        }
257    }
258}