use serde::Deserialize;
use wiremock::matchers::any;
use wiremock::{Mock, MockServer, ResponseTemplate};
use crate::transport::Transport;
use crate::{entities, DimeSchedulerClient};
#[derive(Deserialize)]
struct Contract {
common: Common,
scenarios: Vec<Scenario>,
}
#[derive(Deserialize)]
struct Common {
#[serde(rename = "apiKey")]
api_key: String,
headers: Headers,
}
#[derive(Deserialize)]
struct Headers {
#[serde(rename = "x-api-key")]
x_api_key: String,
#[serde(rename = "user-agent-pattern")]
user_agent_pattern: String,
}
#[derive(Deserialize)]
struct Scenario {
name: String,
input: Option<serde_json::Value>,
expect: Expect,
}
#[derive(Deserialize)]
struct Expect {
method: String,
path: String,
body: Option<serde_json::Value>,
}
async fn dispatch(c: &DimeSchedulerClient, name: &str, input: Option<serde_json::Value>) {
let input = input.unwrap_or(serde_json::Value::Null);
match name {
"categories.getAll" => {
c.categories.get_all().await.unwrap();
}
"categories.create" => {
let cat: entities::Category = serde_json::from_value(input).unwrap();
c.categories.create(&cat).await.unwrap();
}
"resources.getAll" => {
c.resources.get_all().await.unwrap();
}
"jobs.getAll" => {
c.jobs.get_all().await.unwrap();
}
"tasks.getAll" => {
c.tasks.get_all().await.unwrap();
}
"tags.getAll" => {
c.tags.get_all().await.unwrap();
}
"tags.create" => {
let tag: entities::Tag = serde_json::from_value(input).unwrap();
c.tags.create(&tag).await.unwrap();
}
other => panic!("unknown scenario: {other}. Add a case in dispatch()."),
}
}
#[tokio::test]
async fn cross_sdk_contract() {
let raw =
std::fs::read_to_string("../../tests/contract/scenarios.json").expect("read scenarios");
let contract: Contract = serde_json::from_str(&raw).expect("parse scenarios");
assert!(
contract
.common
.headers
.user_agent_pattern
.starts_with("^dimescheduler-"),
"contract pattern changed shape: {}",
contract.common.headers.user_agent_pattern
);
for scenario in contract.scenarios {
let server = MockServer::start().await;
Mock::given(any())
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([])))
.mount(&server)
.await;
let c = DimeSchedulerClient::from_transport(Transport::new(
server.uri(),
contract.common.api_key.clone(),
));
dispatch(&c, &scenario.name, scenario.input).await;
let reqs = server.received_requests().await.unwrap();
assert_eq!(
reqs.len(),
1,
"{}: expected exactly one request",
scenario.name
);
let req = &reqs[0];
assert_eq!(req.url.path(), scenario.expect.path, "{}", scenario.name);
assert_eq!(
req.method.as_str(),
scenario.expect.method,
"{}",
scenario.name
);
assert_eq!(
req.headers.get("x-api-key").unwrap(),
&contract.common.headers.x_api_key,
"{}",
scenario.name
);
let ua = req.headers.get("user-agent").unwrap().to_str().unwrap();
assert!(
ua.starts_with("dimescheduler-rust/"),
"{}: {ua}",
scenario.name
);
if let Some(expected) = scenario.expect.body {
let sent: serde_json::Value = serde_json::from_slice(&req.body).unwrap();
assert_eq!(sent, expected, "{}", scenario.name);
}
}
}