#![cfg(feature = "api")]
mod common;
use serde_json::{json, Value};
#[tokio::test]
async fn create_then_get_then_list() {
let pool = common::fresh_pool().await;
let server = common::spawn_api(pool).await;
let client = reqwest::Client::new();
let res = client
.post(format!("{}/jobs", server.base_url))
.json(&json!({
"kind": "send_email",
"payload": {"to": "a@b.c", "subject": "hi", "body": "hello"},
"max_attempts": 3,
}))
.send()
.await
.expect("post");
assert_eq!(res.status(), 201, "POST should return 201 Created");
let body: Value = res.json().await.expect("json");
let id = body["id"].as_str().expect("id is str").to_string();
assert_eq!(body["status"], "queued");
assert_eq!(body["kind"], "send_email");
assert_eq!(body["attempts"], 0);
assert_eq!(body["max_attempts"], 3);
let res = client
.get(format!("{}/jobs/{id}", server.base_url))
.send()
.await
.expect("get by id");
assert_eq!(res.status(), 200);
let got: Value = res.json().await.expect("json");
assert_eq!(got["id"].as_str(), Some(id.as_str()));
let res = client
.get(format!("{}/jobs", server.base_url))
.send()
.await
.expect("list");
assert_eq!(res.status(), 200);
let list: Vec<Value> = res.json().await.expect("json");
assert_eq!(list.len(), 1);
assert_eq!(list[0]["id"].as_str(), Some(id.as_str()));
server.shutdown().await;
}
#[tokio::test]
async fn get_unknown_id_returns_404() {
let pool = common::fresh_pool().await;
let server = common::spawn_api(pool).await;
let client = reqwest::Client::new();
let missing = uuid::Uuid::now_v7();
let res = client
.get(format!("{}/jobs/{missing}", server.base_url))
.send()
.await
.expect("get");
assert_eq!(res.status(), 404);
let body: Value = res.json().await.expect("json");
assert_eq!(body["error"], "not_found");
server.shutdown().await;
}
#[tokio::test]
async fn invalid_payload_returns_422() {
let pool = common::fresh_pool().await;
let server = common::spawn_api(pool).await;
let client = reqwest::Client::new();
let res = client
.post(format!("{}/jobs", server.base_url))
.json(&json!({
"kind": "send_email",
"payload": {"to": "a@b.c"}, }))
.send()
.await
.expect("post");
assert_eq!(res.status(), 422);
let body: Value = res.json().await.expect("json");
assert_eq!(body["error"], "validation");
server.shutdown().await;
}
#[tokio::test]
async fn health_endpoint_returns_ok() {
let pool = common::fresh_pool().await;
let server = common::spawn_api(pool).await;
let client = reqwest::Client::new();
let res = client
.get(format!("{}/health", server.base_url))
.send()
.await
.expect("health");
assert_eq!(res.status(), 200);
let body: Value = res.json().await.expect("json");
assert_eq!(body["status"], "ok");
server.shutdown().await;
}
#[tokio::test]
async fn openapi_spec_renders() {
let pool = common::fresh_pool().await;
let server = common::spawn_api(pool).await;
let client = reqwest::Client::new();
let res = client
.get(format!("{}/api-docs/openapi.json", server.base_url))
.send()
.await
.expect("openapi");
assert_eq!(res.status(), 200);
let doc: Value = res.json().await.expect("json");
assert!(doc["paths"]["/jobs"].is_object());
assert!(doc["paths"]["/jobs/{id}"].is_object());
assert!(doc["paths"]["/jobs/{id}/cancel"].is_object());
assert!(doc["paths"]["/health"].is_object());
server.shutdown().await;
}