diff --git a/backend/src/services/mod.rs b/backend/src/services/mod.rs index d29b0fe..50b7492 100644 --- a/backend/src/services/mod.rs +++ b/backend/src/services/mod.rs @@ -13,7 +13,6 @@ pub enum AppError { InternalError(anyhow::Error), JsonExtractError(JsonRejection), PathError(PathRejection), - Unprocessable(String), NotFound, } @@ -37,11 +36,6 @@ impl IntoResponse for AppError { }), ) .into_response(), - Self::Unprocessable(msg) => ( - StatusCode::UNPROCESSABLE_ENTITY, - Json(ErrorJson { error: msg }), - ) - .into_response(), Self::PathError(rej) => ( StatusCode::BAD_REQUEST, Json(ErrorJson { diff --git a/backend/src/services/tasks.rs b/backend/src/services/tasks.rs index 3773f01..ebf7f3e 100644 --- a/backend/src/services/tasks.rs +++ b/backend/src/services/tasks.rs @@ -10,17 +10,14 @@ use serde::Deserialize; use sqlx::{Pool, Sqlite}; use uuid::Uuid; -use crate::models::{TaskModel, TaskStatus}; +use crate::models::TaskModel; use super::AppError; pub fn create_task_router() -> Router> { Router::new() - .route("/", post(create_task).get(list_tasks)) - .route( - "/{task_id}", - get(get_task).put(update_task).delete(delete_task), - ) + .route("/", post(create_task)) + .route("/{task_id}", get(get_task)) } #[derive(Deserialize)] @@ -40,14 +37,6 @@ pub async fn create_task( Ok((StatusCode::CREATED, Json(model))) } -pub async fn list_tasks( - State(pool): State>, -) -> Result<(StatusCode, Json>), AppError> { - let tasks = TaskModel::list_all(&pool).await?; - - Ok((StatusCode::OK, Json(tasks))) -} - pub async fn get_task( State(pool): State>, WithRejection(Path(task_id), _): WithRejection, AppError>, @@ -56,51 +45,3 @@ pub async fn get_task( Ok((StatusCode::OK, Json(model))) } - -#[derive(Deserialize)] -#[serde(deny_unknown_fields)] -pub struct UpdateTaskRequest { - title: Option, - description: Option, - status: Option, -} - -pub async fn update_task( - State(pool): State>, - WithRejection(Path(task_id), _): WithRejection, AppError>, - WithRejection(Json(input), _): WithRejection, AppError>, -) -> Result<(StatusCode, Json), AppError> { - let mut model = TaskModel::get_by_id(&pool, task_id).await?; - - if let Some(title) = input.title { - if title.len() == 0 { - return Err(AppError::Unprocessable( - "Title must not be empty".to_string(), - )); - } - model.title = title; - } - - if let Some(description) = input.description { - model.description = Some(description); - } - - if let Some(status) = input.status { - model.status = status; - } - - let model = model.update(&pool).await?; - - Ok((StatusCode::OK, Json(model))) -} - -pub async fn delete_task( - State(pool): State>, - WithRejection(Path(task_id), _): WithRejection, AppError>, -) -> Result { - // Ensure that the task exists. - TaskModel::get_by_id(&pool, task_id).await?; - TaskModel::delete(&pool, task_id).await?; - - Ok(StatusCode::NO_CONTENT) -} diff --git a/backend/tests/api/delete_tasks.hurl b/backend/tests/api/delete_tasks.hurl deleted file mode 100644 index cf8cc61..0000000 --- a/backend/tests/api/delete_tasks.hurl +++ /dev/null @@ -1,92 +0,0 @@ -# Task Delete API Tests - -# Setup: Create a task to delete -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Task to Delete", - "description": "This task will be deleted" -} - -HTTP 201 -[Captures] -delete_task_id: jsonpath "$.id" - -# Test: Delete task successfully -DELETE {{host}}/api/tasks/{{delete_task_id}} - -HTTP 204 - -# Test: Verify task is deleted (should return 404) -GET {{host}}/api/tasks/{{delete_task_id}} - -HTTP 404 -[Asserts] -jsonpath "$.error" exists - -# Setup: Create another task for additional tests -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Another Task to Delete" -} - -HTTP 201 -[Captures] -another_task_id: jsonpath "$.id" - -# Test: Verify task exists before deletion -GET {{host}}/api/tasks/{{another_task_id}} - -HTTP 200 -[Asserts] -jsonpath "$.title" == "Another Task to Delete" - -# Test: Delete the task -DELETE {{host}}/api/tasks/{{another_task_id}} - -HTTP 204 - -# Test: Confirm task no longer exists -GET {{host}}/api/tasks/{{another_task_id}} - -HTTP 404 -[Asserts] -jsonpath "$.error" exists - -# Test: Delete non-existent task -DELETE {{host}}/api/tasks/00000000-0000-0000-0000-000000000000 - -HTTP 404 -[Asserts] -jsonpath "$.error" exists - -# Test: Delete with invalid UUID format -DELETE {{host}}/api/tasks/invalid-uuid-format - -HTTP 400 -[Asserts] -jsonpath "$.error" exists - -# Test: Multiple deletions of same task (idempotency test) -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Task for Idempotency Test" -} - -HTTP 201 -[Captures] -idempotent_task_id: jsonpath "$.id" - -# First deletion should succeed -DELETE {{host}}/api/tasks/{{idempotent_task_id}} - -HTTP 204 - -# Second deletion should return 404 (task already gone) -DELETE {{host}}/api/tasks/{{idempotent_task_id}} - -HTTP 404 -[Asserts] -jsonpath "$.error" exists \ No newline at end of file diff --git a/backend/tests/api/list_tasks.hurl b/backend/tests/api/list_tasks.hurl deleted file mode 100644 index f1f1449..0000000 --- a/backend/tests/api/list_tasks.hurl +++ /dev/null @@ -1,143 +0,0 @@ -# Task List API Tests - -# Test: Get task list (may or may not be empty) -GET {{host}}/api/tasks - -HTTP 200 -[Captures] -initial_count: jsonpath "$" count -[Asserts] -jsonpath "$" isCollection - -# Setup: Create multiple tasks with different properties -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Test Task Alpha", - "description": "Alpha task description" -} - -HTTP 201 -[Captures] -alpha_task_id: jsonpath "$.id" -alpha_created_at: jsonpath "$.created_at" - -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Test Task Beta" -} - -HTTP 201 -[Captures] -beta_task_id: jsonpath "$.id" -beta_created_at: jsonpath "$.created_at" - -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Test Task Gamma", - "description": "Gamma task with description" -} - -HTTP 201 -[Captures] -gamma_task_id: jsonpath "$.id" -gamma_created_at: jsonpath "$.created_at" - -# Test: List includes our newly created tasks -GET {{host}}/api/tasks - -HTTP 200 -[Asserts] -jsonpath "$" isCollection -jsonpath "$" count >= {{initial_count}} -jsonpath "$[*].id" includes "{{alpha_task_id}}" -jsonpath "$[*].id" includes "{{beta_task_id}}" -jsonpath "$[*].id" includes "{{gamma_task_id}}" -jsonpath "$[*].title" includes "Test Task Alpha" -jsonpath "$[*].title" includes "Test Task Beta" -jsonpath "$[*].title" includes "Test Task Gamma" - -# Test: Verify all tasks have required fields -GET {{host}}/api/tasks - -HTTP 200 -[Asserts] -jsonpath "$[?(@.id=='{{alpha_task_id}}')].title" exists -jsonpath "$[?(@.id=='{{alpha_task_id}}')].status" exists -jsonpath "$[?(@.id=='{{alpha_task_id}}')].created_at" exists -jsonpath "$[?(@.id=='{{alpha_task_id}}')].updated_at" exists -jsonpath "$[?(@.id=='{{beta_task_id}}')].title" exists -jsonpath "$[?(@.id=='{{beta_task_id}}')].status" exists -jsonpath "$[?(@.id=='{{beta_task_id}}')].created_at" exists -jsonpath "$[?(@.id=='{{beta_task_id}}')].updated_at" exists - -# Test: Verify our tasks have correct initial values -GET {{host}}/api/tasks - -HTTP 200 -[Asserts] -jsonpath "$[?(@.id=='{{alpha_task_id}}')].title" nth 0 == "Test Task Alpha" -jsonpath "$[?(@.id=='{{alpha_task_id}}')].description" nth 0 == "Alpha task description" -jsonpath "$[?(@.id=='{{alpha_task_id}}')].status" nth 0 == "todo" -jsonpath "$[?(@.id=='{{beta_task_id}}')].title" nth 0 == "Test Task Beta" -jsonpath "$[?(@.id=='{{beta_task_id}}')].description" nth 0 == null -jsonpath "$[?(@.id=='{{beta_task_id}}')].status" nth 0 == "todo" - -# Setup: Update tasks to test mixed statuses -PUT {{host}}/api/tasks/{{beta_task_id}} -Content-Type: application/json -{ - "status": "done" -} - -HTTP 200 - -PUT {{host}}/api/tasks/{{gamma_task_id}} -Content-Type: application/json -{ - "status": "backlog" -} - -HTTP 200 - -# Test: List shows updated statuses -GET {{host}}/api/tasks - -HTTP 200 -[Asserts] -jsonpath "$[?(@.id=='{{alpha_task_id}}')].status" nth 0 == "todo" -jsonpath "$[?(@.id=='{{beta_task_id}}')].status" nth 0 == "done" -jsonpath "$[?(@.id=='{{gamma_task_id}}')].status" nth 0 == "backlog" - -# Setup: Delete one task -DELETE {{host}}/api/tasks/{{alpha_task_id}} - -HTTP 204 - -# Test: Deleted task no longer appears in list -GET {{host}}/api/tasks - -HTTP 200 -[Asserts] -jsonpath "$[*].id" not includes "{{alpha_task_id}}" -jsonpath "$[*].id" includes "{{beta_task_id}}" -jsonpath "$[*].id" includes "{{gamma_task_id}}" - -# Cleanup: Delete remaining test tasks -DELETE {{host}}/api/tasks/{{beta_task_id}} - -HTTP 204 - -DELETE {{host}}/api/tasks/{{gamma_task_id}} - -HTTP 204 - -# Test: Our test tasks are gone -GET {{host}}/api/tasks - -HTTP 200 -[Asserts] -jsonpath "$[*].id" not includes "{{beta_task_id}}" -jsonpath "$[*].id" not includes "{{gamma_task_id}}" diff --git a/backend/tests/api/update_tasks.hurl b/backend/tests/api/update_tasks.hurl deleted file mode 100644 index 550f0cd..0000000 --- a/backend/tests/api/update_tasks.hurl +++ /dev/null @@ -1,154 +0,0 @@ -# Task Update API Tests - -# Setup: Create a task to update -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Original Task", - "description": "Original description" -} - -HTTP 201 -[Captures] -task_id: jsonpath "$.id" -created_at: jsonpath "$.created_at" - -# Test: Update task title only -PUT {{host}}/api/tasks/{{task_id}} -Content-Type: application/json -{ - "title": "Updated Task Title" -} - -HTTP 200 -[Asserts] -jsonpath "$.id" == "{{task_id}}" -jsonpath "$.title" == "Updated Task Title" -jsonpath "$.description" == "Original description" -jsonpath "$.status" == "todo" -jsonpath "$.updated_at" != "{{created_at}}" - -# Test: Update description only -PUT {{host}}/api/tasks/{{task_id}} -Content-Type: application/json -{ - "description": "Updated description" -} - -HTTP 200 -[Asserts] -jsonpath "$.id" == "{{task_id}}" -jsonpath "$.title" == "Updated Task Title" -jsonpath "$.description" == "Updated description" -jsonpath "$.status" == "todo" - -# Test: Update status to done -PUT {{host}}/api/tasks/{{task_id}} -Content-Type: application/json -{ - "status": "done" -} - -HTTP 200 -[Asserts] -jsonpath "$.id" == "{{task_id}}" -jsonpath "$.status" == "done" -jsonpath "$.completed_at" exists - -# Test: Update status to backlog -PUT {{host}}/api/tasks/{{task_id}} -Content-Type: application/json -{ - "status": "backlog" -} - -HTTP 200 -[Asserts] -jsonpath "$.id" == "{{task_id}}" -jsonpath "$.status" == "backlog" -jsonpath "$.completed_at" == null - -# Test: Update multiple fields together -PUT {{host}}/api/tasks/{{task_id}} -Content-Type: application/json -{ - "title": "Completely Updated Task", - "description": "Completely updated description", - "status": "done" -} - -HTTP 200 -[Asserts] -jsonpath "$.id" == "{{task_id}}" -jsonpath "$.title" == "Completely Updated Task" -jsonpath "$.description" == "Completely updated description" -jsonpath "$.status" == "done" -jsonpath "$.completed_at" exists - -# Test: Clear description (set to null) -PUT {{host}}/api/tasks/{{task_id}} -Content-Type: application/json -{ - "description": "" -} - -HTTP 200 -[Asserts] -jsonpath "$.id" == "{{task_id}}" -jsonpath "$.description" == "" - -# Setup: Create another task for error tests -POST {{host}}/api/tasks -Content-Type: application/json -{ - "title": "Task for Error Tests" -} - -HTTP 201 -[Captures] -error_test_task_id: jsonpath "$.id" - -# Test: Update non-existent task -PUT {{host}}/api/tasks/00000000-0000-0000-0000-000000000000 -Content-Type: application/json -{ - "title": "This should fail" -} - -HTTP 404 -[Asserts] -jsonpath "$.error" exists - -# Test: Update with invalid UUID format -PUT {{host}}/api/tasks/invalid-uuid-format -Content-Type: application/json -{ - "title": "This should also fail" -} - -HTTP 400 -[Asserts] -jsonpath "$.error" exists - -# Test: Update with empty title -PUT {{host}}/api/tasks/{{error_test_task_id}} -Content-Type: application/json -{ - "title": "" -} - -HTTP 422 -[Asserts] -jsonpath "$.error" exists - -# Test: Update with invalid status -PUT {{host}}/api/tasks/{{error_test_task_id}} -Content-Type: application/json -{ - "status": "invalid_status" -} - -HTTP 422 -[Asserts] -jsonpath "$.error" exists -