Files
artcraft-services/crates/schema/database/mysql_testing/README.md
T
2026-08-09 22:54:25 -04:00

68 lines
2.5 KiB
Markdown

# mysql_testing
Test-only MySQL harness: guarded connections to a **test** database, schema
setup from the checked-in migrations, and shared fixtures (accounts,
sessions, wallets, media files). Use it as a `dev-dependency` only.
## Safety model
Every connection goes through `guard::guard_test_database_url`, which PANICS
unless:
- the database name contains `"test"`,
- the database name is not `storyteller` or `artcraft` (the real databases),
- the host does not look like a managed/cloud database (`digitalocean`,
`aws`, `amazonaws`, `rds`, `gcp`, `googleapis`, `cloudsql`, `azure`, ...).
Test code reads its URL from exactly one env var —
`ARTCRAFT_TEST_DATABASE_URL` — never `MYSQL_URL` or `DATABASE_URL`, so test
runs can't inherit a real database by accident. The default is
`mysql://root:@localhost:3306/artcraft_test`.
## One-time local setup
```bash
mysql -u root -e "CREATE DATABASE IF NOT EXISTS artcraft_test;"
```
The harness creates the database itself when the connecting user has the
privilege, applies all `_database/sql/migrations/*/up.sql` in order (tracked
in `mysql_testing_applied_migrations`, so repeat runs are fast), and seeds
`user_roles`. If the schema ever wedges: `DROP DATABASE artcraft_test;` and
rerun.
## Writing a database test
Database tests run by default; mark them with the `skip_database_tests`
off switch so database-less machines/CI can exclude them. Tests run in
PARALLEL — create your own users/wallets/rows and never touch another
test's data (schema setup single-flights via a MySQL named lock). Only a
test that must mutate shared/global state takes
`mysql_testing::serial::acquire_serial_test_lock()`.
```rust
#[tokio::test]
#[cfg_attr(feature = "skip_database_tests", ignore)]
async fn my_database_test() {
let pool = mysql_testing::pool::create_test_pool().await;
let user = mysql_testing::fixtures::users::create_test_user(&pool).await.unwrap();
mysql_testing::fixtures::wallets::fund_wallet_banked(&pool, &user.user_token, 10_000)
.await
.unwrap();
// ...
}
```
Skip them (machines/CI without MySQL) with:
```bash
SQLX_OFFLINE=true cargo test -p storyteller-web --features skip_database_tests
```
The first end-to-end consumers are the omni_gen video pricing tests in
`crates/service/web/storyteller_web/src/http_server/endpoints/omni_gen/generate/video/tests/`,
which drive the real generate handler with dummy Actix requests, stub the
provider with an in-process HTTP server, and assert the exact credits
debited from the wallet.