# Testing

## Automated application suite

```sh
php artisan test
```

The default `phpunit.xml` uses SQLite `:memory:`, an ephemeral test encryption key, array cache/session stores, low-cost test password hashing, and preview mail. Tests exercise:

- Section-specific demand generation, stock shortages, idempotent/overlapping imports, atomic rejection, semester separation, invalid ISBNs, and ambiguous/mismatched Kortext records.
- Enrollment drops before/after sending, restoration without duplicate allocation, missing Finance review, edition replacement generations, return confirmation, and refund ordering/history.
- XLSX leading-zero and string-formula roundtrips, formula/XXE rejection, encrypted codes, and safe audits.
- Login/logout, inactive accounts, self-demotion protection, viewer denials on every mutation endpoint, CSRF, throttling, output escaping, pagination/search, and every module/detail page.
- HTTP upload/mapping/review/commit, template/export downloads, invalid upload rejection, cross-semester drafts, and closed-semester protection.
- Preview sends nothing; mocked SMTP acceptance; uncertain delivery reconciliation; SMTP acceptance followed by failed database persistence.

The test base refuses a MySQL database without an `_test` suffix and refuses SQLite files. Even with that guard, use disposable databases only.

## Run the same suite on MySQL

Create an empty database such as `scholarbooks_test`, and set process environment variables before running PHPUnit. On Linux/macOS:

```sh
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 \
DB_DATABASE=scholarbooks_test DB_USERNAME=your_test_user DB_PASSWORD=your_test_password \
php vendor/phpunit/phpunit/phpunit
```

PowerShell:

```powershell
$env:DB_CONNECTION='mysql'
$env:DB_HOST='127.0.0.1'
$env:DB_PORT='3306'
$env:DB_DATABASE='scholarbooks_test'
$env:DB_USERNAME='your_test_user'
$env:DB_PASSWORD='your_test_password'
php vendor/phpunit/phpunit/phpunit
```

These variables override the non-forced PHPUnit defaults. The suite uses migrations and transactions and must never point at production.

## Legacy import and concurrent allocation

`tests/mysql-integration.php` is an additional standalone check. **It removes tables in its two named test databases.** Use distinct, disposable databases whose names both end in `_test`; for example `scholarbooks_integration_test` and `scholarbooks_legacy_test`.

Set `DB_CONNECTION=mysql`, normal `DB_*` variables for the target, `LEGACY_DB_*` variables for the source, and a temporary valid Laravel `APP_KEY` in your process environment. Then:

```sh
php tests/mysql-integration.php
```

It builds the source using the original DDL fixture, populates fictional data, converts encrypted values to the original AES-GCM format, and tests the importer. It checks rollback on the wrong key, all source-table row counts, passwords, code decryption, HMAC preservation, refusal to overwrite an existing target, and two genuinely separate worker processes competing for one available code. It passed **26 integration checks on MySQL 8.4.6**.

## Test-only accounts

| Context | Username | Password | Created in normal install? |
|---|---|---|---|
| HTTP tests | `admin` | `TestPassword123!` | No |
| HTTP tests | `viewer` | `ViewerPassword123!` | No |
| Isolated legacy integration fixture | `legacy-admin` | `LegacyPassword123!` | No |

The normal seed uses the username/password you explicitly supply through `.env`. The demo seeder creates fictional operational records only.

## Verification record

- PHP 8.2.29; Laravel 12.69.2; MySQL Community Server 8.4.6.
- Application suite: **13 tests, 183 assertions passed** on both SQLite and MySQL; zero failures, errors, or skipped tests. JUnit results are included in `docs/sqlite-tests.xml` and `docs/mysql-tests.xml`.
- MySQL legacy/concurrency check: 26 passed.
- Composer dependency validation/audit, Laravel route registration, compiled views, PHP syntax, and archive integrity checked during packaging.
- Browser: successful sign-in with the migrated fixture account; dashboard and import-center layouts verified against the preserved styles.
- External SMTP delivery, real publisher integrations, and production data were not used. Mail tests use mocks; safe preview sends no messages.
