mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 16:51:10 +00:00
3a925b9b0f
- Deleted the `POST /resources/get/{dataFolder?}` and `GET /resources/get-installer` endpoints as part of the architectural shift towards simplified resource management.
- Removed associated methods and configurations, including `ResourcesService.GetEncryptedResource`, `ResourcesService.GetInstaller`, and related properties in `ResourcesConfig`.
- Cleaned up environment variables and configuration files to reflect the removal of installer-related settings.
- Eliminated the `GetResourceRequest` DTO and its validator, along with the `WrongResourceName` error code.
- Updated documentation to clarify the changes in resource handling and the retirement of per-user file encryption.
Co-authored-by: Cursor <cursoragent@cursor.com>
814 lines
25 KiB
Markdown
814 lines
25 KiB
Markdown
# Blackbox Tests
|
|
|
|
## Positive Scenarios
|
|
|
|
### FT-P-01: Successful Login
|
|
|
|
**Summary**: User with valid credentials receives a JWT token.
|
|
**Traces to**: AC-1
|
|
**Category**: Authentication
|
|
|
|
**Preconditions**:
|
|
- Seed user `admin@azaion.com` exists in database
|
|
|
|
**Input data**: Valid email/password for seed admin user
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /login with valid email and password | HTTP 200, body contains non-empty `token` string |
|
|
|
|
**Expected outcome**: HTTP 200 with JWT token in response body
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-02: Successful User Registration
|
|
|
|
**Summary**: ApiAdmin creates a new user account.
|
|
**Traces to**: AC-5, AC-6, AC-7
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
|
|
**Input data**: `{"email":"newuser@test.com","password":"validpwd1","role":"Operator"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | Login as admin to get JWT | HTTP 200, JWT token |
|
|
| 2 | POST /users with valid registration data and ApiAdmin JWT | HTTP 200 |
|
|
|
|
**Expected outcome**: HTTP 200, user created
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-03: JWT Token Structure Validation
|
|
|
|
**Summary**: JWT token contains correct issuer, audience, and lifetime claims.
|
|
**Traces to**: AC-4
|
|
**Category**: Authentication
|
|
|
|
**Preconditions**:
|
|
- Valid login completed
|
|
|
|
**Input data**: JWT token from login response
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | Login to get JWT | HTTP 200, JWT token |
|
|
| 2 | Decode JWT payload (Base64) | Claims contain `iss`, `aud`, `exp` |
|
|
| 3 | Validate `iss` == "AzaionApi" | Match |
|
|
| 4 | Validate `aud` == "Annotators/OrangePi/Admins" | Match |
|
|
| 5 | Validate `exp` - `iat` ≈ 14400s (4 hours) | Within ± 60s |
|
|
|
|
**Expected outcome**: All JWT claims match expected values
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-04: First Hardware Check Stores Fingerprint
|
|
|
|
**Summary**: On first hardware check, the fingerprint is stored for the user.
|
|
**Traces to**: AC-10
|
|
**Category**: Hardware Binding
|
|
|
|
**Preconditions**:
|
|
- User exists with no hardware bound
|
|
|
|
**Input data**: `{"hardware":"test-hw-fingerprint-001"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | Register new user, login to get JWT | HTTP 200 |
|
|
| 2 | POST /resources/check with hardware string | HTTP 200, body `true` |
|
|
|
|
**Expected outcome**: HTTP 200, hardware stored
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-05: Subsequent Hardware Check Matches
|
|
|
|
**Summary**: Same hardware fingerprint passes validation on subsequent calls.
|
|
**Traces to**: AC-11
|
|
**Category**: Hardware Binding
|
|
|
|
**Preconditions**:
|
|
- User with hardware already bound (from FT-P-04)
|
|
|
|
**Input data**: Same hardware string as initial binding
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /resources/check with same hardware | HTTP 200, body `true` |
|
|
|
|
**Expected outcome**: HTTP 200
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-06: List All Users
|
|
|
|
**Summary**: ApiAdmin retrieves the user list.
|
|
**Traces to**: AC-9
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
|
|
**Input data**: GET /users with ApiAdmin JWT
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | GET /users with ApiAdmin JWT | HTTP 200, JSON array with >= 1 user |
|
|
|
|
**Expected outcome**: HTTP 200, array containing at least seed users
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-07: Filter Users by Email
|
|
|
|
**Summary**: ApiAdmin filters users by email substring.
|
|
**Traces to**: AC-9
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin, seed users exist
|
|
|
|
**Input data**: GET /users?email=admin
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | GET /users?email=admin with ApiAdmin JWT | HTTP 200, all returned emails contain "admin" |
|
|
|
|
**Expected outcome**: HTTP 200, filtered list
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-08: Upload Resource File
|
|
|
|
**Summary**: Authenticated user uploads a file to a resource folder.
|
|
**Traces to**: AC-13
|
|
**Category**: Resource Distribution
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated
|
|
|
|
**Input data**: Multipart form upload with 1 KB text file
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /resources/testfolder with multipart file | HTTP 200 |
|
|
|
|
**Expected outcome**: HTTP 200, file stored
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-09: Download Encrypted Resource — OBSOLETE (cycle 2, 2026-05-14)
|
|
|
|
The `POST /resources/get/{dataFolder?}` endpoint, the `Security.GetApiEncryptionKey` / `EncryptTo` helpers, the `ResourcesService.GetEncryptedResource` method, the `GetResourceRequest` DTO, and the e2e tests `Encrypted_download_returns_octet_stream_and_non_empty_body` (in `ResourceTests.cs`) and `Per_user_encryption_produces_distinct_ciphertext_for_same_file` (in `SecurityTests.cs`) were all removed. The endpoint now returns 404 — verified by FT-N-16 below.
|
|
|
|
ID retained for traceability stability; do not regenerate the spec body until a full `/test-spec` rerun.
|
|
|
|
---
|
|
|
|
### FT-P-10: Encryption Round-Trip Verification — OBSOLETE (cycle 2, 2026-05-14)
|
|
|
|
Same removal as FT-P-09. Additionally `Security.DecryptTo` and the e2e test `Encryption_round_trip_decrypt_matches_original_bytes` (in `ResourceTests.cs`) are gone. ID retained for traceability stability.
|
|
|
|
---
|
|
|
|
### FT-P-11: Change User Role
|
|
|
|
**Summary**: ApiAdmin changes a user's role.
|
|
**Traces to**: AC-9
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Target user exists, caller is ApiAdmin
|
|
|
|
**Input data**: `{"email":"testuser@test.com","role":"Admin"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PUT /users/role with ApiAdmin JWT | HTTP 200 |
|
|
|
|
**Expected outcome**: HTTP 200, role updated
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-12: Disable User Account
|
|
|
|
**Summary**: ApiAdmin disables a user account.
|
|
**Traces to**: AC-9
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Target user exists, caller is ApiAdmin
|
|
|
|
**Input data**: `{"email":"testuser@test.com","isEnabled":false}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PUT /users/enable with ApiAdmin JWT | HTTP 200 |
|
|
|
|
**Expected outcome**: HTTP 200, account disabled
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-P-13: Delete User
|
|
|
|
**Summary**: ApiAdmin deletes a user account.
|
|
**Traces to**: AC-9
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Target user exists, caller is ApiAdmin
|
|
|
|
**Input data**: DELETE /users?email=testuser@test.com
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | DELETE /users?email=testuser@test.com with ApiAdmin JWT | HTTP 200 |
|
|
|
|
**Expected outcome**: HTTP 200, user deleted
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
## Negative Scenarios
|
|
|
|
### FT-N-01: Login with Unknown Email
|
|
|
|
**Summary**: Login attempt with non-existent email returns appropriate error.
|
|
**Traces to**: AC-2
|
|
**Category**: Authentication
|
|
|
|
**Preconditions**:
|
|
- Email does not exist in database
|
|
|
|
**Input data**: `{"email":"nonexistent@test.com","password":"anypass1"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /login with unknown email | HTTP 409, ExceptionEnum code 10 (NoEmailFound) |
|
|
|
|
**Expected outcome**: HTTP 409 with error code 10
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-02: Login with Wrong Password
|
|
|
|
**Summary**: Login attempt with correct email but wrong password returns error.
|
|
**Traces to**: AC-3
|
|
**Category**: Authentication
|
|
|
|
**Preconditions**:
|
|
- User exists in database
|
|
|
|
**Input data**: `{"email":"admin@azaion.com","password":"wrongpassword123"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /login with wrong password | HTTP 409, ExceptionEnum code 30 (WrongPassword) |
|
|
|
|
**Expected outcome**: HTTP 409 with error code 30
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-03: Register with Short Email
|
|
|
|
**Summary**: Registration with email shorter than 8 characters is rejected.
|
|
**Traces to**: AC-5
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
|
|
**Input data**: `{"email":"short","password":"validpwd1","role":"Operator"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /users with short email | HTTP 400, validation error |
|
|
|
|
**Expected outcome**: HTTP 400 with email length validation error
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-04: Register with Invalid Email Format
|
|
|
|
**Summary**: Registration with invalid email format (>= 8 chars but not email) is rejected.
|
|
**Traces to**: AC-6
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
|
|
**Input data**: `{"email":"notanemail","password":"validpwd1","role":"Operator"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /users with invalid email format | HTTP 400, validation error |
|
|
|
|
**Expected outcome**: HTTP 400 with email format validation error
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-05: Upload Empty File
|
|
|
|
**Summary**: Upload request with no file attached returns error.
|
|
**Traces to**: AC-16
|
|
**Category**: Resource Distribution
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated
|
|
|
|
**Input data**: POST /resources/testfolder with no file
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /resources/testfolder with empty request | HTTP 409, ExceptionEnum code 70 (NoFileProvided) |
|
|
|
|
**Expected outcome**: HTTP 409 with error code 70
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-06: Hardware Mismatch
|
|
|
|
**Summary**: Hardware check with different fingerprint after binding returns error.
|
|
**Traces to**: AC-12
|
|
**Category**: Hardware Binding
|
|
|
|
**Preconditions**:
|
|
- User has hardware already bound to a different fingerprint
|
|
|
|
**Input data**: `{"hardware":"different-hardware-xyz"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /resources/check with different hardware | HTTP 409, ExceptionEnum code 40 (HardwareIdMismatch) |
|
|
|
|
**Expected outcome**: HTTP 409 with error code 40
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-07: Register Duplicate Email
|
|
|
|
**Summary**: Registration with already-existing email returns error.
|
|
**Traces to**: AC-8
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- User with target email already exists
|
|
|
|
**Input data**: `{"email":"admin@azaion.com","password":"validpwd1","role":"Operator"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /users with existing email | HTTP 409, ExceptionEnum code 20 (EmailExists) |
|
|
|
|
**Expected outcome**: HTTP 409 with error code 20
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### FT-N-08: Register with Short Password
|
|
|
|
**Summary**: Registration with password shorter than 8 characters is rejected.
|
|
**Traces to**: AC-7
|
|
**Category**: User Management
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
|
|
**Input data**: `{"email":"newuser@test.com","password":"short","role":"Operator"}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /users with short password | HTTP 400, validation error |
|
|
|
|
**Expected outcome**: HTTP 400 with password length validation error
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
## Cycle 1 Additions (2026-05-13)
|
|
|
|
The scenarios below were appended during the existing-code cycle 1 Test-Spec Sync (autodev Step 12) for tasks AZ-513, AZ-196, AZ-183, AZ-197. Numbering continues from the legacy IDs above; existing IDs are preserved.
|
|
|
|
### Cycle 1 Obsoletion Note
|
|
|
|
The following legacy entries describe behaviour removed by AZ-197 (admin-side hardware-binding cleanup). Their bodies are intentionally left intact to preserve traceability IDs per the cycle-update rule "preserve existing traceability IDs"; they should be treated as obsolete and superseded by FT-N-15 below:
|
|
|
|
- FT-P-04 (First Hardware Check Stores Fingerprint) — superseded; the `POST /resources/check` endpoint and the hardware-store side-effect were removed.
|
|
- FT-P-05 (Subsequent Hardware Check Matches) — superseded; same endpoint removed.
|
|
- FT-N-06 (Hardware Mismatch) — superseded; the `HardwareIdMismatch` / error code 40 path no longer exists in `ExceptionEnum`.
|
|
- FT-P-09 / FT-P-10 — fully obsolete after the cycle-2 cleanup; the endpoint, support code, and corresponding e2e tests are gone (see the FT-P-09 / FT-P-10 stubs above and FT-N-16 below).
|
|
|
|
See `_docs/03_implementation/batch_06_report.md` for the full AZ-197 implementation rationale and the wire-compat policy decision (drop entirely).
|
|
|
|
---
|
|
|
|
### Cycle-2 Cleanup (2026-05-14) — Obsolete Resource Endpoints Removed
|
|
|
|
#### FT-N-16: Removed Resource Endpoints Return 404
|
|
|
|
**Summary**: After the cycle-2 cleanup, the three obsolete resource endpoints are no longer routed and return 404.
|
|
**Traces to**: Cycle-2 AC-1, Cycle-2 AC-2, Cycle-2 AC-3
|
|
**Category**: Negative — Removed Endpoints
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as any user (404 must precede any auth check, since the route is gone)
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | `POST /resources/get` (with or without body) | HTTP 404 |
|
|
| 2 | `POST /resources/get/somefolder` | HTTP 404 |
|
|
| 3 | `GET /resources/get-installer` | HTTP 404 |
|
|
| 4 | `GET /resources/get-installer/stage` | HTTP 404 |
|
|
|
|
**Expected outcome**: each request returns HTTP 404 (not 401, not 405); no `Security.GetApiEncryptionKey` / `EncryptTo` invocation observable in logs.
|
|
|
|
**Notes**: this is a parallel to FT-N-15 (which covers the AZ-197 endpoint removals). Together they enumerate every route that has been retired in cycles 1 and 2.
|
|
|
|
---
|
|
|
|
### Detection Classes CRUD (AZ-513)
|
|
|
|
#### FT-P-14: POST /classes Creates Detection Class
|
|
|
|
**Summary**: ApiAdmin creates a new detection class and the response includes the assigned id.
|
|
**Traces to**: AZ-513 AC-1
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
- `detection_classes` table exists
|
|
|
|
**Input data**: `{"name":"Tank","shortName":"T","color":"#FF0000","maxSizeM":5.0}`
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /classes with valid body and ApiAdmin JWT | HTTP 200/201 with body containing assigned `id` and the submitted fields |
|
|
|
|
**Expected outcome**: HTTP 200 or 201, response body has integer `id` and matches input fields
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-P-15: PATCH /classes/{id} Full Body Update
|
|
|
|
**Summary**: Updating a detection class with a full body replaces the changed fields.
|
|
**Traces to**: AZ-513 AC-3
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**:
|
|
- A detection class with id `7` exists with `name: "Tank"`
|
|
|
|
**Input data**: `{"name":"Heavy Tank","shortName":"T","color":"#FF0000","maxSizeM":5.0}` to PATCH /classes/7
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PATCH /classes/7 with full body and ApiAdmin JWT | HTTP 200, response body shows `name: "Heavy Tank"` |
|
|
|
|
**Expected outcome**: HTTP 200, updated entity reflects the changed field
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-P-16: PATCH /classes/{id} Partial Body Update
|
|
|
|
**Summary**: PATCH with only the changed field updates that field and leaves others intact.
|
|
**Traces to**: AZ-513 AC-4
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**:
|
|
- A detection class with id `7` exists with `name: "Tank", color: "#FF0000", maxSizeM: 5.0`
|
|
|
|
**Input data**: `{"color":"#00FF00"}` to PATCH /classes/7
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PATCH /classes/7 with partial body and ApiAdmin JWT | HTTP 200, response body shows `color: "#00FF00"`; other fields unchanged |
|
|
|
|
**Expected outcome**: HTTP 200, partial-merge semantics confirmed
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-P-17: DELETE /classes/{id} Removes Class
|
|
|
|
**Summary**: ApiAdmin deletes a detection class and it disappears from the DB.
|
|
**Traces to**: AZ-513 AC-7
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**:
|
|
- A detection class with id `7` exists
|
|
|
|
**Input data**: DELETE /classes/7
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | DELETE /classes/7 with ApiAdmin JWT | HTTP 200 or 204 |
|
|
| 2 | GET the class list (or PATCH the same id) | id 7 no longer present |
|
|
|
|
**Expected outcome**: HTTP 200/204; class removed from DB
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-N-09: POST /classes Without ApiAdmin JWT
|
|
|
|
**Summary**: POST /classes requires the same `apiAdminPolicy` as `/users`; non-admin / unauthenticated calls are rejected.
|
|
**Traces to**: AZ-513 AC-2
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**: None (negative path)
|
|
|
|
**Input data**: Valid body, but caller has no JWT or a non-ApiAdmin JWT
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /classes without JWT | HTTP 401 |
|
|
| 2 | POST /classes with non-ApiAdmin JWT | HTTP 403 |
|
|
|
|
**Expected outcome**: HTTP 401 (no JWT) or 403 (non-admin)
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-N-10: PATCH /classes/{id} Unknown id Returns 404
|
|
|
|
**Summary**: PATCH against a non-existent id returns 404.
|
|
**Traces to**: AZ-513 AC-5
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**: No detection class with id `9999`
|
|
|
|
**Input data**: PATCH /classes/9999 with any valid body
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PATCH /classes/9999 with ApiAdmin JWT | HTTP 404 |
|
|
|
|
**Expected outcome**: HTTP 404
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-N-11: PATCH /classes/{id} Without ApiAdmin JWT
|
|
|
|
**Summary**: PATCH /classes/{id} requires `apiAdminPolicy`.
|
|
**Traces to**: AZ-513 AC-6
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Input data**: Any valid body to PATCH /classes/{id}
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PATCH /classes/{id} without JWT | HTTP 401 |
|
|
| 2 | PATCH /classes/{id} with non-ApiAdmin JWT | HTTP 403 |
|
|
|
|
**Expected outcome**: HTTP 401 or 403
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-N-12: DELETE /classes/{id} Unknown id Returns 404
|
|
|
|
**Summary**: DELETE against a non-existent id returns 404 (matching `/users` semantics — non-idempotent).
|
|
**Traces to**: AZ-513 AC-8
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Preconditions**: No detection class with id `9999`
|
|
|
|
**Input data**: DELETE /classes/9999
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | DELETE /classes/9999 with ApiAdmin JWT | HTTP 404 |
|
|
|
|
**Expected outcome**: HTTP 404
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-N-13: DELETE /classes/{id} Without ApiAdmin JWT
|
|
|
|
**Summary**: DELETE /classes/{id} requires `apiAdminPolicy`.
|
|
**Traces to**: AZ-513 AC-9
|
|
**Category**: Detection Classes CRUD
|
|
|
|
**Input data**: DELETE /classes/{id}
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | DELETE /classes/{id} without JWT | HTTP 401 |
|
|
| 2 | DELETE /classes/{id} with non-ApiAdmin JWT | HTTP 403 |
|
|
|
|
**Expected outcome**: HTTP 401 or 403
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### Device Auto-Registration (AZ-196)
|
|
|
|
#### FT-P-18: POST /devices Returns Serial / Email / Password
|
|
|
|
**Summary**: First call to POST /devices returns the next serial in the `azj-NNNN` sequence with a generated email and 32-char hex password.
|
|
**Traces to**: AZ-196 AC-1
|
|
**Category**: Device Provisioning
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
- No (or known-prior) CompanionPC users in DB
|
|
|
|
**Input data**: POST /devices with no body, ApiAdmin JWT
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /devices with ApiAdmin JWT | HTTP 200 with `serial` matching `^azj-\d{4}$`, `email` = `{serial}@azaion.com`, `password` = 32 lowercase hex chars |
|
|
|
|
**Expected outcome**: HTTP 200, all three fields shaped per spec
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-P-19: Sequential Device Serials
|
|
|
|
**Summary**: Repeated calls to POST /devices yield strictly increasing serial numbers.
|
|
**Traces to**: AZ-196 AC-2
|
|
**Category**: Device Provisioning
|
|
|
|
**Preconditions**:
|
|
- Most recent CompanionPC user has a known serial `azj-NNNN`
|
|
|
|
**Input data**: POST /devices twice in succession
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /devices → record serial `S1` | HTTP 200 |
|
|
| 2 | POST /devices → record serial `S2` | HTTP 200 |
|
|
| 3 | Parse the numeric suffix of both | numeric(S2) == numeric(S1) + 1 |
|
|
|
|
**Expected outcome**: HTTP 200, suffix increments by exactly 1
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-P-20: Returned Device Credentials Can Login
|
|
|
|
**Summary**: The plaintext password returned by POST /devices succeeds against POST /login (and the persisted hash is therefore correct).
|
|
**Traces to**: AZ-196 AC-3, AZ-196 AC-4
|
|
**Category**: Device Provisioning
|
|
|
|
**Preconditions**:
|
|
- Caller authenticated as ApiAdmin
|
|
|
|
**Input data**: Use the response from POST /devices as `{Email, Password}` to POST /login
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /devices with ApiAdmin JWT | HTTP 200, `{Serial, Email, Password}` returned |
|
|
| 2 | POST /login with the returned `Email` and `Password` | HTTP 200 with non-empty JWT |
|
|
|
|
**Expected outcome**: HTTP 200 on login; persisted user has Role=CompanionPC, IsEnabled=true (verified by AdminApi behaviour rather than direct DB inspection)
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
#### FT-N-14: POST /devices Without ApiAdmin JWT
|
|
|
|
**Summary**: POST /devices requires `apiAdminPolicy`.
|
|
**Traces to**: AZ-196 AC-5
|
|
**Category**: Device Provisioning
|
|
|
|
**Input data**: POST /devices
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | POST /devices without JWT | HTTP 401 |
|
|
| 2 | POST /devices with non-ApiAdmin JWT | HTTP 403 |
|
|
|
|
**Expected outcome**: HTTP 401 or 403
|
|
**Max execution time**: 5s
|
|
|
|
---
|
|
|
|
### Resources OTA Update Check (AZ-183) — REVERTED post-cycle-1
|
|
|
|
The OTA update check & publish feature shipped in cycle 1 was reverted later the same day after the security audit (finding F-1: `/get-update` disclosed plaintext per-resource encryption keys to any authenticated caller). The OTA delivery model itself was deemed obsolete in the target architecture.
|
|
|
|
The scenarios `FT-P-21`, `FT-P-22`, `FT-P-23` are retained here as ID placeholders so previously-cited references resolve. Their bodies are intentionally collapsed because the underlying endpoints, service, entity, table, and the e2e test class `ResourceUpdateTests.cs` were all removed. See `_docs/02_document/system-flows.md` (Flow F10) and `_docs/05_security/security_report.md` (finding F-1) for context.
|
|
|
|
| Removed Test ID | Was tracing | Disposition |
|
|
|-----------------|-------------|-------------|
|
|
| FT-P-21 | AZ-183 AC-2 | Removed — endpoint and test deleted |
|
|
| FT-P-22 | AZ-183 AC-3 | Removed — endpoint and test deleted |
|
|
| FT-P-23 | AZ-183 AC-5 | Removed — endpoint and test deleted |
|
|
|
|
---
|
|
|
|
### Hardware-Binding Removal (AZ-197)
|
|
|
|
#### FT-N-15: Hardware Endpoints Removed
|
|
|
|
**Summary**: The legacy `PUT /users/hardware/set` endpoint and the `POST /resources/check` endpoint have been removed and now return 404.
|
|
**Traces to**: AZ-197 AC-2
|
|
**Category**: Authorization & Routing
|
|
|
|
**Preconditions**:
|
|
- Updated admin API build (post-AZ-197)
|
|
|
|
**Input data**: PUT /users/hardware/set and POST /resources/check
|
|
|
|
**Steps**:
|
|
|
|
| Step | Consumer Action | Expected System Response |
|
|
|------|----------------|------------------------|
|
|
| 1 | PUT /users/hardware/set with ApiAdmin JWT | HTTP 404 |
|
|
| 2 | POST /resources/check with ApiAdmin JWT | HTTP 404 |
|
|
|
|
**Expected outcome**: HTTP 404 on both routes
|
|
**Max execution time**: 5s
|
|
|
|
Note: AZ-197 AC-1 (resource download works without `Hardware`) is implicitly covered by the existing FT-P-09 / FT-P-10 scenarios once their request bodies are aligned with the new wire shape. AZ-197 AC-3..AC-8 are internal-signature / build-system invariants and are verified at build/CI time, not via a blackbox HTTP scenario.
|