//! 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 = 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); }