You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.0 KiB
4.0 KiB
+++ title = "Implement test server harness — spawn quotesdb-api with temp SQLite DB, return base URL" priority = 8 status = "done" ticket_type = "task" dependencies = ["5f5ba0", "2ab7a8", "fba598"] +++
Integration tests live in `tests/` and exercise the API binary against a temporary SQLite database. They run with `cargo test` and must not require a running Cloudflare environment.Architecture decided in triage:
- 2ab7a8: Server is spawned as a tokio task using the native Axum path (cfg-split, no workers-rs on host)
- fba598: Isolation strategy is per-test temp SQLite file via
tempfilecrate (transaction rollback cannot intercept server-side pool commits; in-memory SQLite is incompatible with multi-connection SQLx pools) - 0d84fa: HTTP client for tests is
reqwestwithtokio::test
pub struct TestContext { _db_dir: TempDir, // deleted on drop pub base_url: String, _server: tokio::task::JoinHandle<()>, }
pub async fn spawn_test_server() -> TestContext { let db_dir = TempDir::new().expect("temp dir"); let db_path = db_dir.path().join("test.sqlite"); let db_url = format!("sqlite:{}?mode=rwc", db_path.display());
let pool = SqlitePool::connect(&db_url).await.expect("pool");
sqlx::migrate!("./migrations").run(&pool).await.expect("migrations");
let repo = Arc::new(NativeRepository::new(pool));
let app = build_router(repo);
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let port = listener.local_addr().unwrap().port();
let server = tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});
TestContext {
_db_dir: db_dir,
base_url: format!("http://127.0.0.1:{port}"),
_server: server,
}
}
Usage in a test:
```rust
#[tokio::test]
async fn test_create_quote() {
let ctx = spawn_test_server().await;
let client = reqwest::Client::new();
let res = client
.put(format!("{}/api/quotes", ctx.base_url))
.json(&serde_json::json!({"text": "hello", "author": "world"}))
.send()
.await
.unwrap();
assert_eq!(res.status(), 201);
}
- `build_router` and `NativeRepository` must be pub-accessible from the `quotesdb` crate (may require re-exports in `src/lib.rs`).
- `sqlx::migrate!()` macro path is relative to the crate root — migrations must be in `migrations/` at the crate root.
- Each test gets a unique `TempDir`, so parallel test execution (`cargo test`) is safe.
- Do not set `--test-threads=1`; parallel execution must work.
- The `_server` handle is intentionally leaked (tokio runtime drops it when the test ends).
In `[dev-dependencies]` (ticket 5f5ba0):
- `tempfile = "3"`
- `reqwest = { version = "0.12", features = ["json"] }`
- `tokio = { version = "1", features = ["full"] }`
- `serde_json = "1"`
Use `superpowers:test-driven-development` — the harness is itself tested by running `cargo test`.
Use `superpowers:verification-before-completion` before closing.
Run in order from the `quotesdb/` directory:
cargo fmt
cargo check
cargo clippy
cargo test
`test(quotesdb): implement test server harness with per-test temp SQLite DB`