use axum::{ routing::{delete, get, patch, post}, Router, }; use axum_test::TestServer; use serde_json::{json, Value}; use sqlx::SqlitePool; async fn setup_server() -> TestServer { let pool = SqlitePool::connect("sqlite::memory:").await.unwrap(); sqlx::query( r#" CREATE TABLE IF NOT EXISTS tasks ( id TEXT PRIMARY KEY NOT NULL, title TEXT NOT NULL, description TEXT, completed BOOLEAN NOT NULL DEFAULT 0, created_at INTEGER NOT NULL ) "#, ) .execute(&pool) .await .unwrap(); let app = Router::new() .route("/api/tasks", get(taskpile_backend::routes::tasks::list_tasks)) .route("/api/tasks", post(taskpile_backend::routes::tasks::create_task)) .route("/api/tasks/:id", patch(taskpile_backend::routes::tasks::update_task)) .route("/api/tasks/:id", delete(taskpile_backend::routes::tasks::delete_task)) .route("/api/graph", get(taskpile_backend::routes::graph::get_graph)) .with_state(pool); TestServer::new(app).unwrap() } #[tokio::test] async fn test_list_tasks_empty() { let server = setup_server().await; let resp = server.get("/api/tasks").await; resp.assert_status_ok(); let body: Value = resp.json(); assert_eq!(body, json!([])); } #[tokio::test] async fn test_create_task() { let server = setup_server().await; let resp = server .post("/api/tasks") .json(&json!({"title": "Buy milk"})) .await; resp.assert_status(axum::http::StatusCode::CREATED); let body: Value = resp.json(); assert_eq!(body["title"], "Buy milk"); assert_eq!(body["completed"], false); assert!(body["id"].is_string()); } #[tokio::test] async fn test_crud_flow() { let server = setup_server().await; // Create let resp = server .post("/api/tasks") .json(&json!({"title": "Write tests", "description": "Important"})) .await; resp.assert_status(axum::http::StatusCode::CREATED); let created: Value = resp.json(); let id = created["id"].as_str().unwrap().to_string(); // List let resp = server.get("/api/tasks").await; resp.assert_status_ok(); let tasks: Value = resp.json(); assert_eq!(tasks.as_array().unwrap().len(), 1); // Update let resp = server .patch(&format!("/api/tasks/{id}")) .json(&json!({"completed": true})) .await; resp.assert_status_ok(); let updated: Value = resp.json(); assert_eq!(updated["completed"], true); assert_eq!(updated["title"], "Write tests"); // Graph let resp = server.get("/api/graph").await; resp.assert_status_ok(); let graph: Value = resp.json(); assert_eq!(graph["nodes"].as_array().unwrap().len(), 1); assert!(graph["edges"].is_array()); // Delete let resp = server.delete(&format!("/api/tasks/{id}")).await; resp.assert_status(axum::http::StatusCode::NO_CONTENT); // List again - empty let resp = server.get("/api/tasks").await; resp.assert_status_ok(); let tasks: Value = resp.json(); assert_eq!(tasks.as_array().unwrap().len(), 0); } #[tokio::test] async fn test_delete_nonexistent_returns_404() { let server = setup_server().await; let resp = server.delete("/api/tasks/does-not-exist").await; resp.assert_status(axum::http::StatusCode::NOT_FOUND); } #[tokio::test] async fn test_graph_endpoint_structure() { let server = setup_server().await; let resp = server.get("/api/graph").await; resp.assert_status_ok(); let graph: Value = resp.json(); assert!(graph["nodes"].is_array()); assert!(graph["edges"].is_array()); } #[tokio::test] async fn test_create_task_with_description() { let server = setup_server().await; let resp = server .post("/api/tasks") .json(&json!({"title": "Task with desc", "description": "Some details"})) .await; resp.assert_status(axum::http::StatusCode::CREATED); let body: Value = resp.json(); assert_eq!(body["description"], "Some details"); } #[tokio::test] async fn test_graph_with_multiple_tasks_has_edges() { let server = setup_server().await; // Create enough tasks to statistically guarantee edges (~30% of pairs) for i in 0..10 { server .post("/api/tasks") .json(&json!({"title": format!("Task {i}")})) .await; } let resp = server.get("/api/graph").await; resp.assert_status_ok(); let graph: Value = resp.json(); assert_eq!(graph["nodes"].as_array().unwrap().len(), 10); // 45 possible pairs at 30% => highly likely to have at least 1 let edge_count = graph["edges"].as_array().unwrap().len(); assert!(edge_count <= 45); }