Hurl tests for project api.
This commit is contained in:
parent
4895f7c4b7
commit
d8094c4812
3 changed files with 406 additions and 5 deletions
1
backend/.gitignore
vendored
1
backend/.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
target
|
target
|
||||||
coverage
|
coverage
|
||||||
|
reports
|
||||||
|
|
||||||
*.db
|
*.db
|
||||||
*.db-shm
|
*.db-shm
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use axum::{Router, routing::get};
|
use axum::{Router, routing::get};
|
||||||
use axum_test::{TestServer, TestServerConfig, Transport};
|
use axum_test::{TestServer, TestServerConfig, Transport};
|
||||||
|
use hurl::report::html::{Testcase, write_report};
|
||||||
use hurl::runner::{self, RunnerOptions, Value, VariableSet};
|
use hurl::runner::{self, RunnerOptions, Value, VariableSet};
|
||||||
use hurl::util::logger::LoggerOptionsBuilder;
|
use hurl::util::logger::LoggerOptionsBuilder;
|
||||||
|
use hurl_core::input::Input;
|
||||||
|
use tower_http::trace::TraceLayer;
|
||||||
|
|
||||||
async fn create_app() -> Router {
|
async fn create_app() -> Router {
|
||||||
use backend::database::{DatabaseConfig, create_pool};
|
use backend::database::{DatabaseConfig, create_pool};
|
||||||
|
|
@ -49,11 +54,14 @@ async fn run_hurl_test(hurl_file_path: &str) {
|
||||||
let logger_opts = LoggerOptionsBuilder::new().build();
|
let logger_opts = LoggerOptionsBuilder::new().build();
|
||||||
let result = runner::run(&content, None, &runner_opts, &variables, &logger_opts).unwrap();
|
let result = runner::run(&content, None, &runner_opts, &variables, &logger_opts).unwrap();
|
||||||
|
|
||||||
assert!(
|
let input = Input::new(hurl_file_path);
|
||||||
result.success,
|
let test_case = Testcase::from(&result, &input);
|
||||||
"Hurl test failed for {}: {:?}",
|
test_case
|
||||||
hurl_file_path, result
|
.write_html(&content, &result.entries, Path::new("reports/store"), &[])
|
||||||
);
|
.expect("Failed to write html files");
|
||||||
|
write_report(Path::new("reports/"), &vec![test_case]).expect("Failed to write report");
|
||||||
|
|
||||||
|
assert!(result.success, "Hurl test failed for {}", hurl_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
|
@ -75,3 +83,8 @@ async fn test_update_tasks_api() {
|
||||||
async fn test_delete_tasks_api() {
|
async fn test_delete_tasks_api() {
|
||||||
run_hurl_test("./tests/api/delete_tasks.hurl").await;
|
run_hurl_test("./tests/api/delete_tasks.hurl").await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_projects_api() {
|
||||||
|
run_hurl_test("./tests/api/projects.hurl").await;
|
||||||
|
}
|
||||||
|
|
|
||||||
387
backend/tests/api/projects.hurl
Normal file
387
backend/tests/api/projects.hurl
Normal file
|
|
@ -0,0 +1,387 @@
|
||||||
|
# Project API Tests
|
||||||
|
|
||||||
|
# Test: Create a new project with all fields (POST /api/projects)
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Test Project Alpha",
|
||||||
|
"color": "#1976d2"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 201
|
||||||
|
[Captures]
|
||||||
|
project_id: jsonpath "$.id"
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.title" == "Test Project Alpha"
|
||||||
|
jsonpath "$.color" == "#1976d2"
|
||||||
|
jsonpath "$.status" == "active"
|
||||||
|
jsonpath "$.folder_id" == null
|
||||||
|
jsonpath "$.sort_order" == 0
|
||||||
|
jsonpath "$.id" exists
|
||||||
|
jsonpath "$.created_at" exists
|
||||||
|
jsonpath "$.updated_at" exists
|
||||||
|
|
||||||
|
# Test: Create another new project
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Test Project Beta",
|
||||||
|
"color": "#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 201
|
||||||
|
[Captures]
|
||||||
|
project_id_beta: jsonpath "$.id"
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.title" == "Test Project Beta"
|
||||||
|
jsonpath "$.color" == "#ffffff"
|
||||||
|
jsonpath "$.status" == "active"
|
||||||
|
jsonpath "$.folder_id" == null
|
||||||
|
jsonpath "$.sort_order" == 1000
|
||||||
|
jsonpath "$.id" exists
|
||||||
|
jsonpath "$.created_at" exists
|
||||||
|
jsonpath "$.updated_at" exists
|
||||||
|
|
||||||
|
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Missing Color"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 422
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
|
||||||
|
# Test: Create project with invalid data (missing title)
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"color": "#ffffff"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 422
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Create project with invalid data (invalid color)
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Invalid Color",
|
||||||
|
"color": "#ffffffasdf"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 422
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
|
||||||
|
# Test: Get a specific project by ID (GET /api/projects/{id})
|
||||||
|
GET {{host}}/api/projects/{{project_id}}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id}}"
|
||||||
|
jsonpath "$.title" == "Test Project Alpha"
|
||||||
|
jsonpath "$.color" == "#1976d2"
|
||||||
|
jsonpath "$.status" == "active"
|
||||||
|
jsonpath "$.folder_id" == null
|
||||||
|
jsonpath "$.created_at" exists
|
||||||
|
jsonpath "$.updated_at" exists
|
||||||
|
|
||||||
|
# Test: Get a minimal project by ID
|
||||||
|
GET {{host}}/api/projects/{{project_id_beta}}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id_beta}}"
|
||||||
|
jsonpath "$.title" == "Test Project Beta"
|
||||||
|
jsonpath "$.color" == "#ffffff"
|
||||||
|
jsonpath "$.status" == "active"
|
||||||
|
|
||||||
|
# Test: Get non-existent project
|
||||||
|
GET {{host}}/api/projects/00000000-0000-0000-0000-000000000000
|
||||||
|
|
||||||
|
HTTP 404
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Get project with invalid UUID format
|
||||||
|
GET {{host}}/api/projects/invalid-uuid
|
||||||
|
|
||||||
|
HTTP 400
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: List all projects (GET /api/projects)
|
||||||
|
GET {{host}}/api/projects
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Captures]
|
||||||
|
initial_count: jsonpath "$" count
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$" isCollection
|
||||||
|
jsonpath "$[*].id" contains "{{project_id}}"
|
||||||
|
jsonpath "$[*].id" contains "{{project_id_beta}}"
|
||||||
|
jsonpath "$[*].title" contains "Test Project Alpha"
|
||||||
|
jsonpath "$[*].title" contains "Test Project Beta"
|
||||||
|
|
||||||
|
# Test: Verify all projects have required fields
|
||||||
|
GET {{host}}/api/projects
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$[?(@.id=='{{project_id}}')].title" exists
|
||||||
|
jsonpath "$[?(@.id=='{{project_id}}')].status" exists
|
||||||
|
jsonpath "$[?(@.id=='{{project_id}}')].created_at" exists
|
||||||
|
jsonpath "$[?(@.id=='{{project_id}}')].updated_at" exists
|
||||||
|
jsonpath "$[?(@.id=='{{project_id}}')].sort_order" exists
|
||||||
|
|
||||||
|
# Test: Update project title only (PUT /api/projects/{id})
|
||||||
|
PUT {{host}}/api/projects/{{project_id}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Updated Project Alpha"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Captures]
|
||||||
|
first_updated_at: jsonpath "$.updated_at"
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id}}"
|
||||||
|
jsonpath "$.title" == "Updated Project Alpha"
|
||||||
|
jsonpath "$.color" == "#1976d2"
|
||||||
|
jsonpath "$.status" == "active"
|
||||||
|
|
||||||
|
|
||||||
|
# Test: Update status to done
|
||||||
|
PUT {{host}}/api/projects/{{project_id}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"status": "done"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id}}"
|
||||||
|
jsonpath "$.status" == "done"
|
||||||
|
|
||||||
|
# Test: Update status to backlog
|
||||||
|
PUT {{host}}/api/projects/{{project_id}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"status": "backlog"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id}}"
|
||||||
|
jsonpath "$.status" == "backlog"
|
||||||
|
|
||||||
|
# Test: Update color
|
||||||
|
PUT {{host}}/api/projects/{{project_id}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"color": "#388e3c"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id}}"
|
||||||
|
jsonpath "$.color" == "#388e3c"
|
||||||
|
|
||||||
|
|
||||||
|
# Test: Update multiple fields together
|
||||||
|
PUT {{host}}/api/projects/{{project_id}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Completely Updated Project",
|
||||||
|
"color": "#d32f2f",
|
||||||
|
"status": "active"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.id" == "{{project_id}}"
|
||||||
|
jsonpath "$.title" == "Completely Updated Project"
|
||||||
|
jsonpath "$.color" == "#d32f2f"
|
||||||
|
jsonpath "$.status" == "active"
|
||||||
|
|
||||||
|
|
||||||
|
# Test: Update non-existent project
|
||||||
|
PUT {{host}}/api/projects/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/projects/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/projects/{{project_id_beta}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 422
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Update with invalid status
|
||||||
|
PUT {{host}}/api/projects/{{project_id_beta}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"status": "invalid_status"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 422
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Update with invalid color format
|
||||||
|
PUT {{host}}/api/projects/{{project_id_beta}}
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"color": "invalid-color"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 422
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Setup: Create a project to delete
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Project to Delete",
|
||||||
|
"color": "#222222"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 201
|
||||||
|
[Captures]
|
||||||
|
delete_project_id: jsonpath "$.id"
|
||||||
|
|
||||||
|
# Test: Delete project successfully (DELETE /api/projects/{id})
|
||||||
|
DELETE {{host}}/api/projects/{{delete_project_id}}
|
||||||
|
|
||||||
|
HTTP 204
|
||||||
|
|
||||||
|
# Test: Verify project is deleted (should return 404)
|
||||||
|
GET {{host}}/api/projects/{{delete_project_id}}
|
||||||
|
|
||||||
|
HTTP 404
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Setup: Create another project for additional delete tests
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Another Project to Delete",
|
||||||
|
"color": "#333333"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 201
|
||||||
|
[Captures]
|
||||||
|
another_delete_project_id: jsonpath "$.id"
|
||||||
|
|
||||||
|
# Test: Verify project exists before deletion
|
||||||
|
GET {{host}}/api/projects/{{another_delete_project_id}}
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.title" == "Another Project to Delete"
|
||||||
|
|
||||||
|
# Test: Delete the project
|
||||||
|
DELETE {{host}}/api/projects/{{another_delete_project_id}}
|
||||||
|
|
||||||
|
HTTP 204
|
||||||
|
|
||||||
|
# Test: Confirm project no longer exists
|
||||||
|
GET {{host}}/api/projects/{{another_delete_project_id}}
|
||||||
|
|
||||||
|
HTTP 404
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Delete non-existent project
|
||||||
|
DELETE {{host}}/api/projects/00000000-0000-0000-0000-000000000000
|
||||||
|
|
||||||
|
HTTP 404
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Delete with invalid UUID format
|
||||||
|
DELETE {{host}}/api/projects/invalid-uuid-format
|
||||||
|
|
||||||
|
HTTP 400
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: Multiple deletions of same project (idempotency test)
|
||||||
|
POST {{host}}/api/projects
|
||||||
|
Content-Type: application/json
|
||||||
|
{
|
||||||
|
"title": "Project for Idempotency Test",
|
||||||
|
"color": "#234567"
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP 201
|
||||||
|
[Captures]
|
||||||
|
idempotent_project_id: jsonpath "$.id"
|
||||||
|
|
||||||
|
# First deletion should succeed
|
||||||
|
DELETE {{host}}/api/projects/{{idempotent_project_id}}
|
||||||
|
|
||||||
|
HTTP 204
|
||||||
|
|
||||||
|
# Second deletion should return 404 (project already gone)
|
||||||
|
DELETE {{host}}/api/projects/{{idempotent_project_id}}
|
||||||
|
|
||||||
|
HTTP 404
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.error" exists
|
||||||
|
|
||||||
|
# Test: List shows remaining projects after deletions
|
||||||
|
GET {{host}}/api/projects
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$[*].id" contains "{{project_id}}"
|
||||||
|
jsonpath "$[*].id" contains "{{project_id_beta}}"
|
||||||
|
jsonpath "$[*].id" not contains "{{delete_project_id}}"
|
||||||
|
jsonpath "$[*].id" not contains "{{another_delete_project_id}}"
|
||||||
|
jsonpath "$[*].id" not contains "{{idempotent_project_id}}"
|
||||||
|
|
||||||
|
# Cleanup: Delete remaining test projects
|
||||||
|
DELETE {{host}}/api/projects/{{project_id}}
|
||||||
|
|
||||||
|
HTTP 204
|
||||||
|
|
||||||
|
DELETE {{host}}/api/projects/{{project_id_beta}}
|
||||||
|
|
||||||
|
HTTP 204
|
||||||
|
|
||||||
|
# Test: Verify all test projects are cleaned up
|
||||||
|
GET {{host}}/api/projects
|
||||||
|
|
||||||
|
HTTP 200
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$[*].id" not contains "{{project_id}}"
|
||||||
|
jsonpath "$[*].id" not contains "{{project_id_beta}}"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue