Run hurl tests via cargo #12
6 changed files with 805 additions and 32 deletions
730
backend/Cargo.lock
generated
730
backend/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -17,4 +17,7 @@ tracing-subscriber = "0.3.19"
|
||||||
uuid = { version = "1.18.0", features = ["serde", "v4"] }
|
uuid = { version = "1.18.0", features = ["serde", "v4"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
axum-test = "18.0.0"
|
||||||
cargo-tarpaulin = "0.31"
|
cargo-tarpaulin = "0.31"
|
||||||
|
hurl = "7.0.0"
|
||||||
|
hurl_core = "7.0.0"
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ build-backend:
|
||||||
|
|
||||||
[working-directory: 'backend']
|
[working-directory: 'backend']
|
||||||
test-unit-backend:
|
test-unit-backend:
|
||||||
cargo test
|
cargo test -- --skip api
|
||||||
|
|
||||||
[working-directory: 'backend']
|
[working-directory: 'backend']
|
||||||
fmt-backend:
|
fmt-backend:
|
||||||
|
|
@ -46,19 +46,7 @@ migrate-revert:
|
||||||
|
|
||||||
[working-directory: 'backend']
|
[working-directory: 'backend']
|
||||||
test-integration:
|
test-integration:
|
||||||
#!/usr/bin/env bash
|
cargo test --test api
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo run &
|
|
||||||
SERVER_PID=$!
|
|
||||||
|
|
||||||
trap 'echo "Stopping server..."; kill -TERM $SERVER_PID 2>/dev/null || true; wait $SERVER_PID 2>/dev/null || true' EXIT
|
|
||||||
|
|
||||||
echo "Waiting for server to start..."
|
|
||||||
printf 'GET http://localhost:3000/health\nHTTP 200' | hurl --retry 30 > /dev/null
|
|
||||||
|
|
||||||
echo "Running integration tests..."
|
|
||||||
hurl --test --error-format long --variable host=http://localhost:3000 tests/api/*.hurl
|
|
||||||
|
|
||||||
[working-directory: 'backend']
|
[working-directory: 'backend']
|
||||||
test-coverage:
|
test-coverage:
|
||||||
|
|
|
||||||
3
backend/src/lib.rs
Normal file
3
backend/src/lib.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod database;
|
||||||
|
pub mod models;
|
||||||
|
pub mod services;
|
||||||
|
|
@ -8,9 +8,8 @@ mod services;
|
||||||
async fn main() {
|
async fn main() {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let binding = database::DatabaseConfig {
|
let database_url = std::env::var("DATABASE_URL").unwrap_or("sqlite:local.db".to_string());
|
||||||
database_url: "sqlite:local.db".to_string(),
|
let binding = database::DatabaseConfig { database_url };
|
||||||
};
|
|
||||||
let pool = database::create_pool(&binding)
|
let pool = database::create_pool(&binding)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create database pool");
|
.expect("Failed to create database pool");
|
||||||
|
|
@ -19,7 +18,8 @@ async fn main() {
|
||||||
.nest("/api/tasks", services::create_task_router())
|
.nest("/api/tasks", services::create_task_router())
|
||||||
.with_state(pool);
|
.with_state(pool);
|
||||||
|
|
||||||
let addr = "127.0.0.1:3000";
|
let port = std::env::var("PORT").unwrap_or("3000".to_string());
|
||||||
|
let addr = format!("127.0.0.1:{}", port);
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
77
backend/tests/api.rs
Normal file
77
backend/tests/api.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
use axum::{Router, routing::get};
|
||||||
|
use axum_test::{TestServer, TestServerConfig, Transport};
|
||||||
|
use hurl::runner::{self, RunnerOptions, Value, VariableSet};
|
||||||
|
use hurl::util::logger::LoggerOptionsBuilder;
|
||||||
|
|
||||||
|
async fn create_app() -> Router {
|
||||||
|
use backend::database::{DatabaseConfig, create_pool};
|
||||||
|
use backend::services;
|
||||||
|
|
||||||
|
// Use in-memory SQLite for tests
|
||||||
|
let config = DatabaseConfig {
|
||||||
|
database_url: "sqlite::memory:".to_string(),
|
||||||
|
};
|
||||||
|
let pool = create_pool(&config)
|
||||||
|
.await
|
||||||
|
.expect("Failed to create test database pool");
|
||||||
|
|
||||||
|
// Run migrations on in-memory database
|
||||||
|
sqlx::migrate!("./migrations")
|
||||||
|
.run(&pool)
|
||||||
|
.await
|
||||||
|
.expect("Failed to run migrations");
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.route("/health", get(health))
|
||||||
|
.nest("/api/tasks", services::create_task_router())
|
||||||
|
.with_state(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn health() -> &'static str {
|
||||||
|
"Ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_hurl_test(hurl_file_path: &str) {
|
||||||
|
let app = create_app().await;
|
||||||
|
let config = TestServerConfig {
|
||||||
|
transport: Some(Transport::HttpRandomPort),
|
||||||
|
..TestServerConfig::default()
|
||||||
|
};
|
||||||
|
let server = TestServer::new_with_config(app, config).unwrap();
|
||||||
|
let address = server.server_address().unwrap();
|
||||||
|
let host = address.as_str().strip_suffix("/").unwrap();
|
||||||
|
|
||||||
|
let content = std::fs::read_to_string(hurl_file_path).unwrap();
|
||||||
|
let mut variables = VariableSet::new();
|
||||||
|
variables.insert("host".to_string(), Value::String(host.to_string()));
|
||||||
|
|
||||||
|
let runner_opts = RunnerOptions::default();
|
||||||
|
let logger_opts = LoggerOptionsBuilder::new().build();
|
||||||
|
let result = runner::run(&content, None, &runner_opts, &variables, &logger_opts).unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
result.success,
|
||||||
|
"Hurl test failed for {}: {:?}",
|
||||||
|
hurl_file_path, result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_tasks_api() {
|
||||||
|
run_hurl_test("./tests/api/tasks.hurl").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_list_tasks_api() {
|
||||||
|
run_hurl_test("./tests/api/list_tasks.hurl").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_update_tasks_api() {
|
||||||
|
run_hurl_test("./tests/api/update_tasks.hurl").await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_delete_tasks_api() {
|
||||||
|
run_hurl_test("./tests/api/delete_tasks.hurl").await;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue