backbone-payroll 0.3.49

Payroll: salary structures, payroll runs and computed salary slips over effective-dated statutory tables, plus compensation changes
Documentation
//! HTTP integration-test harness.
//!
//! Generated by metaphor-schema. Do not edit manually.
#![allow(dead_code)]

use std::collections::HashMap;

use serde_json::Value;

/// Outcome of a single API test case.
#[derive(Debug, Clone)]
pub struct TestResult {
    pub test_name: String,
    pub success: bool,
    pub details: String,
}

impl TestResult {
    pub fn success(test_name: impl Into<String>, details: impl Into<String>) -> Self {
        Self { test_name: test_name.into(), success: true, details: details.into() }
    }

    pub fn failure(test_name: impl Into<String>, details: impl Into<String>) -> Self {
        Self { test_name: test_name.into(), success: false, details: details.into() }
    }
}

/// Captured HTTP response.
#[derive(Debug, Clone)]
pub struct ApiResponse {
    pub status_code: u16,
    pub body: String,
}

impl ApiResponse {
    pub fn is_success(&self) -> bool {
        (200..300).contains(&self.status_code)
    }

    pub fn json_value(&self) -> Result<Value, serde_json::Error> {
        serde_json::from_str(&self.body)
    }
}

/// Thin reqwest wrapper for exercising a running service over HTTP.
pub struct ApiTest {
    entity_name: String,
    base_url: String,
    client: reqwest::Client,
}

impl ApiTest {
    pub fn new(entity_name: &str, base_url: &str) -> Self {
        Self {
            entity_name: entity_name.to_string(),
            base_url: base_url.trim_end_matches('/').to_string(),
            client: reqwest::Client::new(),
        }
    }

    fn url(&self, path: &str) -> String {
        format!("{}{}", self.base_url, path)
    }

    async fn send(
        &self,
        req: reqwest::RequestBuilder,
        headers: Option<HashMap<String, String>>,
    ) -> Result<ApiResponse, reqwest::Error> {
        let mut req = req;
        if let Some(hs) = headers {
            for (k, v) in hs {
                req = req.header(k, v);
            }
        }
        let resp = req.send().await?;
        let status_code = resp.status().as_u16();
        let body = resp.text().await.unwrap_or_default();
        Ok(ApiResponse { status_code, body })
    }

    pub async fn get(&self, path: &str, headers: Option<HashMap<String, String>>) -> Result<ApiResponse, reqwest::Error> {
        self.send(self.client.get(self.url(path)), headers).await
    }

    pub async fn post(&self, path: &str, body: &Value, headers: Option<HashMap<String, String>>) -> Result<ApiResponse, reqwest::Error> {
        self.send(self.client.post(self.url(path)).json(body), headers).await
    }

    pub async fn put(&self, path: &str, body: &Value, headers: Option<HashMap<String, String>>) -> Result<ApiResponse, reqwest::Error> {
        self.send(self.client.put(self.url(path)).json(body), headers).await
    }

    pub async fn delete(&self, path: &str, headers: Option<HashMap<String, String>>) -> Result<ApiResponse, reqwest::Error> {
        self.send(self.client.delete(self.url(path)), headers).await
    }

    /// Build a `TestResult` by asserting the response status equals `expected_status`.
    pub fn create_result(&self, test_name: &str, response: &ApiResponse, expected_status: u16, success_msg: &str) -> TestResult {
        if response.status_code == expected_status {
            TestResult::success(test_name, success_msg)
        } else {
            let snippet: String = response.body.chars().take(200).collect();
            TestResult::failure(
                test_name,
                format!(
                    "[{}] expected HTTP {}, got {} (body: {})",
                    self.entity_name, expected_status, response.status_code, snippet
                ),
            )
        }
    }
}