# Tests and reproduction

The authoritative run outputs are under `test-results/`. The final summary states exact passed/skipped counts; fixture-backed contract tests are not live-provider certification.

## Unit, feature and contract suite

Run from the project root with PHP 8.3+ and Composer dependencies installed:

```bash
php vendor/bin/phpunit --log-junit test-results/phpunit-sqlite.xml
```

`phpunit.xml` uses an in-memory SQLite DB and array cache/sessions/mail with test credentials. It covers input validation, real private storage, consent/quotas, authorization, auth and signed links, Filament key/SMTP settings, engine mappings/errors, worker transitions, CSV injection, expiry, and Stripe raw-body signature/replay/canonical-state behavior. Remote HTTP fixtures live only in tests; the normal application has no simulated result mode.

For MySQL 8, create an isolated database such as `multiscan_test`, then supply its credentials via environment variables:

```bash
APP_ENV=testing DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=multiscan_test DB_USERNAME=TEST_USER DB_PASSWORD='YOUR_TEST_PASSWORD' php vendor/bin/phpunit --log-junit test-results/phpunit-mysql8.xml
```

The suite uses `RefreshDatabase`; do not use a production database. Keep test cache/session/mail/queue drivers from phpunit.xml, rather than overriding them with production environment values. A running MySQL process and the test processes must be in the same network namespace/container network.

## Actual scanner binaries

```bash
python3 tests/scanner/test_scanner.py
```

Install `clamscan`, `yara`, `yarac` first. `CLAMSCAN_BIN`, `YARA_BIN`, `YARAC_BIN` can select approved paths. New ClamAV versions may require `CVD_CERTS_DIR` to point to the package's installed certificate directory. The suite creates a deterministic EICAR HDB fixture and compiled YARA rules in a temporary directory, tests real binary matches/no-matches and path attacks, and performs an actual Unix-socket request when the environment allows Unix sockets. It skips that socket test only when the OS forbids socket creation, with the explicit reason recorded. That skip remains a launch blocker for the actual scanner integration.

The EICAR test string is a harmless industry test signature. The test fixture verifies binary wiring; it is not a substitute for fresh official production signatures or broad coverage testing. No live malware is needed or downloaded.

## MySQL concurrency, Redis worker and actual HTTP tests

Use `tests/integration/run.py` only with a deliberately isolated MySQL test database and dedicated Redis test instance. It requires a database name ending `_test`, `DB_CONNECTION=mysql`, and an explicit reset opt-in. It drops/recreates tables, seeds configuration, launches its own local PHP HTTP server, races quota consumers and runs actual Redis workers. It uses standard-library Python, PHP and the real Redis service.

```bash
MULTISCAN_TEST_ALLOW_DATABASE_RESET=yes APP_ENV=local APP_KEY='YOUR_ISOLATED_TEST_KEY' DB_CONNECTION=mysql DB_DATABASE=multiscan_test DB_HOST=127.0.0.1 DB_PORT=3306 DB_USERNAME=TEST_USER DB_PASSWORD='YOUR_TEST_PASSWORD' REDIS_CLIENT=predis REDIS_HOST=127.0.0.1 REDIS_PORT=6379 REDIS_PASSWORD='YOUR_REDIS_PASSWORD' python3 tests/integration/run.py
```

Use a fresh isolated APP_KEY from a local testing `.env` or a separately generated key, not your production key. Do not put actual credentials into shared shell history. In real operations, load a protected environment file rather than typing secrets into command arguments.

The default integration run deliberately points at an unavailable scanner socket to verify that actual Redis workers produce visible failures and no invented detections. The final local run used actual MySQL 8.0.46 and Redis 7.0.15. It checks CSRF rejection using a real HTTP request (the PHPUnit test framework normally bypasses CSRF), private guest-session ownership, public-root isolation and headers.

For a full real local-engine pipeline on staging, start the deployed restricted scanner and run the same isolated integration script with `MULTISCAN_LIVE_SCANNER=1`, `SCANNER_SOCKET` set to the real socket and `QUARANTINE_PATH` set to the service's actual quarantine. The account running the test must have its intended group/ACL access. It uploads benign text and EICAR through real HTTP, processes Redis jobs and asserts actual ClamAV/YARA verdicts. This mode was NOT run successfully on the local build host because Unix sockets are forbidden there.

## Dependency, syntax and build checks

```bash
composer validate --strict
composer audit --format=json
npm audit --json
npm ci
npm run build
find app bootstrap config database routes tests -name '*.php' -print0 | xargs -0 -n1 php -l
python3 -m py_compile scanner/daemon.py tests/scanner/test_scanner.py tests/integration/run.py
bash -n deploy/provision-almalinux.sh deploy/install-services.sh deploy/release.sh
```

These checks do not certify the live service, commercial provider entitlements or browser UI. The included GitHub Actions workflow exercises the MySQL/scanner suite in a normal Linux environment when the project is placed in a repository. The workflow has not itself been run here.

## Browser installer tests

The standard PHPUnit suite includes 11 installer safety/serialization tests. For an actual HTTP install, use `tests/integration/installer-http.py` with `MULTISCAN_TEST_ALLOW_DATABASE_RESET=yes`, a **separate empty** MySQL database named with the `_test` suffix, and its database/Redis connection environment. It starts a temporary PHP server and checkout, uses only test credentials, refuses a nonempty database, and leaves the installation database populated for inspection. It never drops the caller's existing tables. Set `PHP_BIN` if needed. HTTPS enforcement on the live cPanel domain remains a staging gate.
