artcraft/.claude/rules/testing.md

3.5 KiB

Testing

Database Fixture Tests

  • Live in-crate (e.g. storyteller-web's omni_gen video pricing tests) and use the mysql_testing crate (crates/schema/database/mysql_testing) for guarded pools, schema setup, and account/session/wallet fixtures
  • They RUN BY DEFAULT with cargo test -p storyteller-web and from the IDE (requires a local MySQL with an artcraft_test database). Machines/CI without MySQL skip them via the off switch: SQLX_OFFLINE=true cargo test -p storyteller-web --features skip_database_tests
  • They connect ONLY via ARTCRAFT_TEST_DATABASE_URL (default mysql://root:@localhost:3306/artcraft_test); the guard panics on any database whose name lacks "test", on storyteller/artcraft, and on cloud hosts — see mysql_testing/README.md
  • Database tests run in PARALLEL: each test creates its own users, wallets, and rows; schema setup single-flights via a MySQL named lock. Only tests mutating shared/global state take mysql_testing::serial::acquire_serial_test_lock().await
  • External providers must never be called: the harness stubs Kinovi with an in-process HTTP server via KINOVI_CUSTOM_API_HOST/KINOVI_CUSTOM_CDN_HOST

Running Tests

  • Most crates: cargo test -p {crate_name}
  • Crates using SQLx offline mode: SQLX_OFFLINE=true cargo test -p {crate_name}
  • The storyteller-web and mysql_queries crates require SQLX_OFFLINE=true for compilation
  • Use cargo check -p {crate_name} for fast compilation checks without running tests

Test Patterns

  • Tests live in #[cfg(test)] mod tests { ... } at the bottom of each file
  • Group related tests into sub-modules by category (e.g. mod pricing_tests, mod resolution_tests)
  • Only create a sub-module when it has 2+ tests — don't wrap a single test in its own module
  • Keep nesting shallow: mod tests { mod category { ... } } is fine; a third level is rarely needed
  • Shared helpers used by multiple sub-modules belong in the parent mod tests, not duplicated in each sub-module
  • Enum tests should cover: serialization, deserialization, to_str/from_str round-trip, variant count
  • Password/crypto tests: be mindful of computation cost, don't add too many bcrypt test cases

Test File Layout

Put test cases first, helpers last. Engineers should see the actual tests before scrolling to understand helper plumbing.

Within a mod tests { ... } block:

  1. Imports at the top
  2. Constants (test data IDs, URLs, etc.) — small context that tests reference
  3. Test functions (#[test] / #[tokio::test]) grouped by category
  4. Helper functions (builders, pipeline runners, etc.) at the very bottom

If tests are grouped into sub-modules, the same rule applies within each sub-module: constants first, then tests, then helpers at the end of that sub-module.

What to Test

  • New error types and their mappings
  • Enum variant serialization stability (these are stored in the database)
  • Hash/deduplication functions with known inputs
  • Edge cases in parsers (empty input, missing fields, malformed data)

Imports in Tests

  • Import test fixtures and constants with use, not inline fully-qualified paths. Write use test_data::web::image_urls::JUNO_AT_LAKE_IMAGE_URL; at the top, then reference JUNO_AT_LAKE_IMAGE_URL in test bodies.
  • Same rule applies to enum variants used in match arms — import them, don't write crate::some::deep::module::MyEnum::Variant inline.

What Not to Test

  • Don't add integration tests that require a running database unless explicitly asked
  • Don't add tests for simple struct construction or trivial getters