use std::collections::HashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TestSuite {
#[serde(default)]
pub variables: HashMap<String, String>,
#[serde(default)]
pub timeout_ms: Option<u64>,
pub requests: Vec<RequestDef>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RequestDef {
pub name: String,
pub method: String,
pub url: String,
#[serde(default)]
pub headers: HashMap<String, String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub expect: Option<ExpectDef>,
#[serde(default)]
pub no_auth: bool,
#[serde(default)]
pub timeout_ms: Option<u64>,
#[serde(default)]
pub skip: bool,
#[serde(default)]
pub tags: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ExpectDef {
#[serde(default)]
pub status: Option<StatusExpect>,
#[serde(default)]
pub headers: HashMap<String, String>,
#[serde(default)]
pub body_contains: Vec<String>,
#[serde(default)]
pub body_not_contains: Vec<String>,
#[serde(default)]
pub body_matches: Vec<String>,
#[serde(default)]
pub body_not_matches: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum StatusExpect {
Single(u16),
Multiple(Vec<u16>),
}
impl StatusExpect {
pub fn matches(&self, actual: u16) -> bool {
match self {
Self::Single(s) => *s == actual,
Self::Multiple(v) => v.contains(&actual),
}
}
}
impl std::fmt::Display for StatusExpect {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Single(s) => write!(f, "{s}"),
Self::Multiple(v) => {
let s: Vec<String> = v.iter().map(|n| n.to_string()).collect();
write!(f, "{}", s.join(" or "))
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum VarSource {
Runtime,
SuiteFile,
DotEnv,
OsEnv,
}
impl std::fmt::Display for VarSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Runtime => write!(f, "runtime"),
Self::SuiteFile => write!(f, "suite"),
Self::DotEnv => write!(f, ".env"),
Self::OsEnv => write!(f, "OS"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VarTrace {
pub name: String,
pub value: String,
pub source: VarSource,
pub found_in: Vec<(VarSource, String)>,
}
#[derive(Debug, Clone, Default)]
pub struct ResolvedVars {
pub vars: HashMap<String, String>,
pub traces: Vec<VarTrace>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HttpResponse {
pub status: u16,
pub http_version: String,
pub headers: HashMap<String, String>,
pub actual_request_headers: HashMap<String, String>,
pub body: String,
pub charset: Option<String>,
pub elapsed_ms: u64,
pub size_bytes: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Failure {
pub check: String,
pub expected: String,
pub actual: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TestResult {
pub name: String,
pub status: u16,
pub passed: bool,
pub elapsed_ms: u64,
pub failures: Vec<Failure>,
pub response_preview: String,
pub request_method: String,
pub request_url: String,
pub request_headers: HashMap<String, String>,
pub request_body: Option<String>,
pub response_headers: HashMap<String, String>,
#[serde(default)]
pub response_http_version: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub response_charset: Option<String>,
#[serde(default)]
pub response_size_bytes: usize,
pub skipped: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuiteResult {
pub total: usize,
pub passed: usize,
pub failed: usize,
pub skipped: usize,
pub elapsed_ms: u64,
pub results: Vec<TestResult>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub warnings: Vec<String>,
}