1use std::collections::HashMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
12pub struct TestSuite {
13 #[serde(default)]
15 pub variables: HashMap<String, String>,
16
17 #[serde(default)]
19 pub timeout_ms: Option<u64>,
20
21 pub requests: Vec<RequestDef>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
27pub struct RequestDef {
28 pub name: String,
30
31 pub method: String,
33
34 pub url: String,
36
37 #[serde(default)]
39 pub headers: HashMap<String, String>,
40
41 #[serde(default)]
43 pub body: Option<String>,
44
45 #[serde(default)]
47 pub expect: Option<ExpectDef>,
48
49 #[serde(default)]
51 pub no_auth: bool,
52
53 #[serde(default)]
55 pub timeout_ms: Option<u64>,
56
57 #[serde(default)]
59 pub skip: bool,
60
61 #[serde(default)]
63 pub tags: Vec<String>,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
68pub struct ExpectDef {
69 #[serde(default)]
71 pub status: Option<StatusExpect>,
72
73 #[serde(default)]
75 pub headers: HashMap<String, String>,
76
77 #[serde(default)]
79 pub body_contains: Vec<String>,
80
81 #[serde(default)]
83 pub body_not_contains: Vec<String>,
84
85 #[serde(default)]
87 pub body_matches: Vec<String>,
88
89 #[serde(default)]
91 pub body_not_matches: Vec<String>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
96#[serde(untagged)]
97pub enum StatusExpect {
98 Single(u16),
99 Multiple(Vec<u16>),
100}
101
102impl StatusExpect {
103 pub fn matches(&self, actual: u16) -> bool {
104 match self {
105 Self::Single(s) => *s == actual,
106 Self::Multiple(v) => v.contains(&actual),
107 }
108 }
109}
110
111impl std::fmt::Display for StatusExpect {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113 match self {
114 Self::Single(s) => write!(f, "{s}"),
115 Self::Multiple(v) => {
116 let s: Vec<String> = v.iter().map(|n| n.to_string()).collect();
117 write!(f, "{}", s.join(" or "))
118 }
119 }
120 }
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129pub enum VarSource {
130 Runtime,
132 SuiteFile,
134 DotEnv,
136 OsEnv,
138}
139
140impl std::fmt::Display for VarSource {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 match self {
143 Self::Runtime => write!(f, "runtime"),
144 Self::SuiteFile => write!(f, "suite"),
145 Self::DotEnv => write!(f, ".env"),
146 Self::OsEnv => write!(f, "OS"),
147 }
148 }
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct VarTrace {
154 pub name: String,
155 pub value: String,
156 pub source: VarSource,
157 pub found_in: Vec<(VarSource, String)>,
159}
160
161#[derive(Debug, Clone, Default)]
163pub struct ResolvedVars {
164 pub vars: HashMap<String, String>,
165 pub traces: Vec<VarTrace>,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct HttpResponse {
175 pub status: u16,
176 pub http_version: String,
177 pub headers: HashMap<String, String>,
178 pub actual_request_headers: HashMap<String, String>,
180 pub body: String,
181 pub charset: Option<String>,
182 pub elapsed_ms: u64,
183 pub size_bytes: usize,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct Failure {
193 pub check: String,
194 pub expected: String,
195 pub actual: String,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct TestResult {
201 pub name: String,
202 pub status: u16,
203 pub passed: bool,
204 pub elapsed_ms: u64,
205 pub failures: Vec<Failure>,
206 pub response_preview: String,
207 pub request_method: String,
208 pub request_url: String,
209 pub request_headers: HashMap<String, String>,
210 pub request_body: Option<String>,
211 pub response_headers: HashMap<String, String>,
212 #[serde(default)]
214 pub response_http_version: String,
215 #[serde(default, skip_serializing_if = "Option::is_none")]
217 pub response_charset: Option<String>,
218 #[serde(default)]
220 pub response_size_bytes: usize,
221 pub skipped: bool,
222 #[serde(default, skip_serializing_if = "Option::is_none")]
224 pub error_type: Option<String>,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
229pub struct SuiteResult {
230 pub total: usize,
231 pub passed: usize,
232 pub failed: usize,
233 pub skipped: usize,
234 pub elapsed_ms: u64,
235 pub results: Vec<TestResult>,
236 #[serde(default, skip_serializing_if = "Vec::is_empty")]
238 pub warnings: Vec<String>,
239}