use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Assertion {
AllOf(Vec<Assertion>),
AnyOf(Vec<Assertion>),
Not(Box<Assertion>),
Status(u16),
StatusIn(Vec<u16>),
Header {
name: String,
predicate: ValuePredicate,
},
BodyLength(BodyLengthPredicate),
JsonPath {
path: String,
predicate: JsonPredicate,
},
Schema {
schema: serde_json::Value,
},
ValidJson,
ContentType(String),
ResponseTime(u64),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ValuePredicate {
Eq(String),
Contains(String),
Regex(String),
Present,
Absent,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum JsonPredicate {
Exists,
NotExists,
Eq(serde_json::Value),
NotEq(serde_json::Value),
Cmp { op: CmpOp, value: serde_json::Value },
Length(LengthPredicate),
Every(Box<JsonPredicate>),
Some(Box<JsonPredicate>),
Count(CountPredicate),
Schema(serde_json::Value),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CmpOp {
Gt,
Lt,
Ge,
Le,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BodyLengthPredicate {
Eq(usize),
Min(usize),
Max(usize),
Range { min: usize, max: usize },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LengthPredicate {
Eq(usize),
Min(usize),
Max(usize),
Range { min: usize, max: usize },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CountPredicate {
Eq(usize),
Min(usize),
Max(usize),
Range { min: usize, max: usize },
}
impl Assertion {
pub fn status(code: u16) -> Self {
Assertion::Status(code)
}
pub fn status_in(codes: Vec<u16>) -> Self {
Assertion::StatusIn(codes)
}
pub fn json_path_exists(path: impl Into<String>) -> Self {
Assertion::JsonPath {
path: path.into(),
predicate: JsonPredicate::Exists,
}
}
pub fn json_path_eq(path: impl Into<String>, value: serde_json::Value) -> Self {
Assertion::JsonPath {
path: path.into(),
predicate: JsonPredicate::Eq(value),
}
}
pub fn header(name: impl Into<String>, predicate: ValuePredicate) -> Self {
Assertion::Header {
name: name.into(),
predicate,
}
}
pub fn content_type(ct: impl Into<String>) -> Self {
Assertion::ContentType(ct.into())
}
pub fn valid_json() -> Self {
Assertion::ValidJson
}
pub fn schema(schema: serde_json::Value) -> Self {
Assertion::Schema { schema }
}
pub fn response_time(max_millis: u64) -> Self {
Assertion::ResponseTime(max_millis)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssertionResult {
pub description: String,
pub passed: bool,
pub message: Option<String>,
#[serde(default)]
pub children: Vec<AssertionResult>,
}
impl AssertionResult {
pub fn pass(description: impl Into<String>) -> Self {
Self {
description: description.into(),
passed: true,
message: None,
children: vec![],
}
}
pub fn fail(description: impl Into<String>, message: impl Into<String>) -> Self {
Self {
description: description.into(),
passed: false,
message: Some(message.into()),
children: vec![],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn status_assertion_constructors() {
let a = Assertion::status(200);
assert_eq!(a, Assertion::Status(200));
let b = Assertion::status_in(vec![200, 304]);
assert_eq!(b, Assertion::StatusIn(vec![200, 304]));
}
#[test]
fn json_path_constructors() {
let a = Assertion::json_path_exists("$.resourceType");
assert_eq!(
a,
Assertion::JsonPath {
path: "$.resourceType".into(),
predicate: JsonPredicate::Exists,
}
);
let b = Assertion::json_path_eq("$.total", serde_json::json!(42));
assert_eq!(
b,
Assertion::JsonPath {
path: "$.total".into(),
predicate: JsonPredicate::Eq(serde_json::json!(42)),
}
);
}
#[test]
fn assertion_result_pass() {
let r = AssertionResult::pass("status is 200");
assert!(r.passed);
assert!(r.message.is_none());
}
#[test]
fn assertion_result_fail() {
let r = AssertionResult::fail("status is 200", "got 404");
assert!(!r.passed);
assert_eq!(r.message.unwrap(), "got 404");
}
}