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
storytellerorartcraft(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
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().
#[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:
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.