# Testing

## First-time setup

The backend suite runs against an isolated MySQL database `stepup_test_db`, never
against dev or prod. Create it once:

```sql
CREATE DATABASE stepup_test_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```

Then run all migrations against it. The `phpunit.xml` config pins
`DB_DATABASE=stepup_test_db`, so this overrides your local `.env` for tests only:

```bash
DB_DATABASE=stepup_test_db php artisan migrate --force
```

**You must re-run this any time you pull migrations.** There is no CI to do it for
you (see below), so `stepup_test_db` drifts silently until a test blows up with
`Column not found` or `Table doesn't exist`.

## Running tests

The suite has hundreds of tests and each spins up a fresh Laravel container.
On Windows dev boxes the default 128 MB PHP heap exhausts long before the run
finishes. **The full suite does not currently complete in one process.** Run
filtered:

```bash
# Single class
php artisan test --filter=ProgramCloneAndCopyTest

# Directory / partial name
php artisan test --filter=Workflow

# All that reliably runs in one process — bump memory
php -d memory_limit=2G artisan test --filter=Program
```

If you have to run the whole suite:

```bash
php -d memory_limit=4G artisan test 2>&1 | tail -200
```

Even at 4 GB the run may OOM mid-suite on some machines. Splitting by directory
is the current workaround. Fixing the per-test container leak is queued as
post-demo tech debt.

## No CI

There is no `.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`, or docker
compose test config in this repo. Tests only ever run when a developer runs
them locally. Two implications:

1. **A green suite locally is the only signal you have.** Nobody else is
   running it after you push.
2. **Migrations to `stepup_test_db` are your responsibility per developer.**
   Nothing automates it.

## Known-broken guards to know about

- The transaction-guarantee helper `engineOf()` in
  `AttendanceCancellationTransactionTest`, `ClassScheduleTransactionTest`, and
  `ModuleTransactionTest` was silently reading `NULL` for every table's engine
  on MySQL 8 (case-sensitive property access against uppercase column names).
  Fixed in Batch 7 follow-up. If you see the same `$row->engine ?? ''` pattern
  in any new transaction test, use the portable key-iteration pattern instead:

  ```php
  foreach ((array) $row as $k => $v) {
      if (strcasecmp((string) $k, 'engine') === 0) return (string) $v;
  }
  return '';
  ```

- `RolePermissionTransactionTest` and (any future addition) may still have the
  broken pattern.
