Files
2026-08-09 22:54:25 -04:00
..
2026-08-09 22:54:25 -04:00
2026-08-09 22:54:25 -04:00
2026-08-09 22:54:25 -04:00

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

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.