AZ-513: POST/PATCH/DELETE /classes for detection-class CRUD; new DetectionClass entity, schema, DTOs, IDetectionClassService. Unblocks ui/AZ-512. AZ-196: POST /devices auto-assigns sequential azj-NNNN serial+email +password and inserts a CompanionPC user. Returns plaintext credentials for the provisioning script. AZ-183: Resources table + POST /get-update + POST /resources/publish for fleet OTA. Per-resource encryption_key column AES-256-CBC encrypted at rest with ResourcesConfig.EncryptionMasterKey; ICache wraps the per-(arch,stage) latest-versions lookup and is invalidated on publish. Adds IDbFactory.RunAdmin<T> overload for write-and-return. Backfills _docs/02_document/module-layout.md to satisfy the implement skill's File Ownership prerequisite (the _docs/ artifact set predates the Step 1.5 module-layout addition). Code review: PASS_WITH_WARNINGS — see _docs/03_implementation/reviews/batch_05_review.md. Co-authored-by: Cursor <cursoragent@cursor.com>
Azaion Admin API — black-box E2E tests
Run (Docker)
From the repository root:
docker compose -f docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from e2e-consumer
Reports are written to e2e/test-results/ on the host (results.trx, results.xunit.xml).
Database bootstrap
The stock Postgres entrypoint runs every file in /docker-entrypoint-initdb.d/ against POSTGRES_DB only. The scripts under env/db/ expect different databases (postgres vs azaion), so e2e/db-init/00_run_all.sh runs 01_permissions.sql on postgres, then 02_structure.sql, 03_add_timestamp_columns.sql, and 99_test_seed.sql on azaion. The compose file uses POSTGRES_USER=postgres so 01_permissions.sql can create roles and the azaion database as written.
99_test_seed.sql sets azaion_admin / azaion_reader passwords to test_password (matching the API connection strings) and updates seed user password hashes for Admin1234 and Upload1234.
Local dotnet test (without Docker)
appsettings.test.json targets http://system-under-test:8080. Running tests on the host will fail fixture setup unless you override ApiBaseUrl (for example via environment variables) and run the API plus Postgres yourself.