Golden example: todo-rs (Axum + SQLx + SQLite)
This commit is contained in:
262
kipina-codebench/golden-examples/todo-rs/tests/api_test.rs
Normal file
262
kipina-codebench/golden-examples/todo-rs/tests/api_test.rs
Normal file
@@ -0,0 +1,262 @@
|
||||
//! Integraatiotestit — muistinvarainen SQLite, uniikki data per testi.
|
||||
|
||||
use axum::http::StatusCode;
|
||||
use reqwest::Client;
|
||||
use sqlx::sqlite::SqlitePoolOptions;
|
||||
use todo_rs::{app, init_db};
|
||||
|
||||
/// Käynnistä testipalvelin satunnaisessa portissa.
|
||||
async fn spawn_server() -> (Client, String) {
|
||||
let pool = SqlitePoolOptions::new()
|
||||
.max_connections(1)
|
||||
.connect("sqlite::memory:")
|
||||
.await
|
||||
.expect("Testitietokanta epäonnistui");
|
||||
|
||||
init_db(&pool).await;
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
|
||||
.await
|
||||
.expect("Testiportin kuuntelu epäonnistui");
|
||||
let addr = listener.local_addr().unwrap();
|
||||
let base_url = format!("http://{addr}");
|
||||
|
||||
let router = app(pool);
|
||||
tokio::spawn(async move {
|
||||
axum::serve(listener, router).await.unwrap();
|
||||
});
|
||||
|
||||
(Client::new(), base_url)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_todo() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let res = client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({"title": "Osta maitoa", "priority": 2}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::CREATED);
|
||||
let body: serde_json::Value = res.json().await.unwrap();
|
||||
assert_eq!(body["title"], "Osta maitoa");
|
||||
assert_eq!(body["priority"], 2);
|
||||
assert!(body["id"].is_number());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_todo_defaults() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let res = client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({"title": "Oletusarvotesti"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::CREATED);
|
||||
let body: serde_json::Value = res.json().await.unwrap();
|
||||
assert_eq!(body["priority"], 1);
|
||||
assert_eq!(body["status"], "pending");
|
||||
assert!(body["description"].is_null());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_list_todos() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({"title": "Listattava tehtävä"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let res = client.get(format!("{url}/todos")).send().await.unwrap();
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
|
||||
let body: Vec<serde_json::Value> = res.json().await.unwrap();
|
||||
assert!(body.len() >= 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_todo_by_id() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let created: serde_json::Value = client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({"title": "Haettava tehtävä"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let id = created["id"].as_i64().unwrap();
|
||||
let res = client
|
||||
.get(format!("{url}/todos/{id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let body: serde_json::Value = res.json().await.unwrap();
|
||||
assert_eq!(body["id"], id);
|
||||
assert_eq!(body["title"], "Haettava tehtävä");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_todo_not_found() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let res = client
|
||||
.get(format!("{url}/todos/99999"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_todo() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let created: serde_json::Value = client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({"title": "Vanha otsikko"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let id = created["id"].as_i64().unwrap();
|
||||
let res = client
|
||||
.put(format!("{url}/todos/{id}"))
|
||||
.json(&serde_json::json!({"title": "Uusi otsikko"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::OK);
|
||||
let body: serde_json::Value = res.json().await.unwrap();
|
||||
assert_eq!(body["title"], "Uusi otsikko");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_update_todo_not_found() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let res = client
|
||||
.put(format!("{url}/todos/99999"))
|
||||
.json(&serde_json::json!({"title": "Ei löydy"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_todo() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let created: serde_json::Value = client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({"title": "Poistettava"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let id = created["id"].as_i64().unwrap();
|
||||
let res = client
|
||||
.delete(format!("{url}/todos/{id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::NO_CONTENT);
|
||||
|
||||
let res = client
|
||||
.get(format!("{url}/todos/{id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_todo_not_found() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
let res = client
|
||||
.delete(format!("{url}/todos/99999"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_full_lifecycle() {
|
||||
let (client, url) = spawn_server().await;
|
||||
|
||||
// Luo
|
||||
let created: serde_json::Value = client
|
||||
.post(format!("{url}/todos"))
|
||||
.json(&serde_json::json!({
|
||||
"title": "Elinkaaritesti",
|
||||
"description": "Testataan koko CRUD-kierto",
|
||||
"due_date": "2026-12-31",
|
||||
"priority": 3,
|
||||
"status": "in_progress"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let id = created["id"].as_i64().unwrap();
|
||||
assert_eq!(created["title"], "Elinkaaritesti");
|
||||
assert_eq!(created["description"], "Testataan koko CRUD-kierto");
|
||||
assert_eq!(created["due_date"], "2026-12-31");
|
||||
assert_eq!(created["priority"], 3);
|
||||
assert_eq!(created["status"], "in_progress");
|
||||
|
||||
// Päivitä
|
||||
let updated: serde_json::Value = client
|
||||
.put(format!("{url}/todos/{id}"))
|
||||
.json(&serde_json::json!({"status": "done"}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(updated["status"], "done");
|
||||
assert_eq!(updated["title"], "Elinkaaritesti");
|
||||
|
||||
// Poista
|
||||
let res = client
|
||||
.delete(format!("{url}/todos/{id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(res.status(), StatusCode::NO_CONTENT);
|
||||
}
|
||||
Reference in New Issue
Block a user