mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 12:01:10 +00:00
refactor: remove deploy.cmd and update Dockerfile for health checks
- Deleted the deploy.cmd script as it was no longer needed. - Updated Dockerfile to include curl for health checks and added a non-root user for improved security. - Modified health check command to use curl for better reliability. - Adjusted docker-compose.test.yml to reflect changes in health check configuration. - Cleaned up appsettings.json and removed unused configuration properties. - Removed Resource entity and related requests from the codebase as part of the architectural shift. - Updated documentation to reflect the removal of hardware binding and related endpoints. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -473,3 +473,349 @@
|
||||
|
||||
**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 wire shape — the `hardware` field on `POST /resources/get/{dataFolder}` is no longer required; the encryption key is now derived from `email + password` only. The tests still pass without the field; do not regenerate spec bodies until a full `/test-spec` rerun.
|
||||
|
||||
See `_docs/03_implementation/batch_06_report.md` for the full AZ-197 implementation rationale and the wire-compat policy decision (drop entirely).
|
||||
|
||||
---
|
||||
|
||||
### 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.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Performance Tests
|
||||
|
||||
> **Cycle 1 update (2026-05-13)**: NFT-PERF-02 and NFT-PERF-03 (encrypted resource download, small/large file) were removed because the OTA / encrypted-resource-download endpoints (`POST /resources/get/...`) and the hardware-binding flow they depended on were reverted in cycle 1 (AZ-183 OTA revert, AZ-197 hardware removal). When OTA returns under the new architecture, perf scenarios for it must be re-derived from the new endpoints.
|
||||
|
||||
### NFT-PERF-01: Login Endpoint Latency
|
||||
|
||||
**Summary**: Login endpoint responds within acceptable latency under normal load.
|
||||
@@ -7,77 +9,46 @@
|
||||
**Metric**: Response time (p95)
|
||||
|
||||
**Preconditions**:
|
||||
- System running with seed data
|
||||
- 10 concurrent users
|
||||
- System running with seed data (admin user from `e2e/db-init/99_test_seed.sql`)
|
||||
- 10 concurrent virtual users
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Measurement |
|
||||
|------|----------------|-------------|
|
||||
| 1 | Send 100 login requests (10 concurrent) | Measure p50, p95, p99 response times |
|
||||
| 1 | 10 VUs send POST /login for 30s | Measure p50, p95, p99 response times |
|
||||
|
||||
**Pass criteria**: p95 latency < 500ms
|
||||
**Duration**: 30 seconds
|
||||
|
||||
---
|
||||
|
||||
### NFT-PERF-02: Resource Download Latency (Small File)
|
||||
|
||||
**Summary**: Encrypted resource download for a small file (1 KB) completes quickly.
|
||||
**Traces to**: AC-14
|
||||
**Metric**: Response time including encryption
|
||||
|
||||
**Preconditions**:
|
||||
- 1 KB test file uploaded
|
||||
- User authenticated with bound hardware
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Measurement |
|
||||
|------|----------------|-------------|
|
||||
| 1 | Send 50 encrypted download requests (5 concurrent) | Measure p50, p95 response times |
|
||||
|
||||
**Pass criteria**: p95 latency < 1000ms
|
||||
**Duration**: 30 seconds
|
||||
|
||||
---
|
||||
|
||||
### NFT-PERF-03: Resource Download Latency (Large File)
|
||||
|
||||
**Summary**: Encrypted resource download for a larger file (50 MB) completes within limits.
|
||||
**Traces to**: AC-13, AC-14
|
||||
**Metric**: Response time including encryption + transfer
|
||||
|
||||
**Preconditions**:
|
||||
- 50 MB test file uploaded
|
||||
- User authenticated with bound hardware
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Measurement |
|
||||
|------|----------------|-------------|
|
||||
| 1 | Send 5 sequential encrypted download requests | Measure p50, p95 response times |
|
||||
|
||||
**Pass criteria**: p95 latency < 30000ms (30s)
|
||||
**Duration**: 3 minutes
|
||||
|
||||
---
|
||||
|
||||
### NFT-PERF-04: User List Endpoint Under Load
|
||||
|
||||
**Summary**: User list endpoint responds within limits when DB has many users.
|
||||
**Traces to**: AC-9
|
||||
**Metric**: Response time
|
||||
**Summary**: `GET /users` responds within limits when DB has many users.
|
||||
**Traces to**: AC-11
|
||||
|
||||
> **Note**: this scenario originally referenced AC-9. Post-cycle-1, AC-9 is "Registration rejects duplicate email". The user-listing criterion is AC-11 (filter support). The thresholds below verify the listing path under volume; the filter semantics are covered by functional tests.
|
||||
|
||||
**Metric**: Response time (p95)
|
||||
|
||||
**Preconditions**:
|
||||
- 500 users in database
|
||||
- Caller is ApiAdmin
|
||||
- Database seeded with 500 users (perf seed inserts dummy rows alongside the functional seed; see `scripts/run-performance-tests.sh`)
|
||||
- Caller is `admin@azaion.com` (ApiAdmin)
|
||||
|
||||
**Steps**:
|
||||
|
||||
| Step | Consumer Action | Measurement |
|
||||
|------|----------------|-------------|
|
||||
| 1 | Send 50 GET /users requests (10 concurrent) | Measure p50, p95 response times |
|
||||
| 1 | 10 VUs send GET /users for 30s, sharing one cached JWT | Measure p50, p95, p99 response times |
|
||||
|
||||
**Pass criteria**: p95 latency < 1000ms
|
||||
**Duration**: 30 seconds
|
||||
|
||||
---
|
||||
|
||||
## Runner
|
||||
|
||||
Both scenarios are implemented in `scripts/perf-scenarios.js` (k6, JS) and orchestrated by `scripts/run-performance-tests.sh`. The runner spins up the SUT via `docker-compose.test.yml`, seeds 500 perf users into `test-db`, executes k6, captures the JSON summary to `e2e/test-results/perf-summary.json`, and tears down.
|
||||
|
||||
To run locally: `./scripts/run-performance-tests.sh`. Requires `k6` (`brew install k6`) and Docker.
|
||||
|
||||
@@ -41,9 +41,10 @@
|
||||
|
||||
| Category | Total Items | Covered | Not Covered | Coverage % |
|
||||
|----------|-----------|---------|-------------|-----------|
|
||||
| Acceptance Criteria | 19 | 19 | 0 | 100% |
|
||||
| Acceptance Criteria (baseline) | 19 | 19 | 0 | 100% |
|
||||
| Acceptance Criteria (cycle 1) | 24 | 24 | 0 | 100% |
|
||||
| Restrictions | 8 | 5 | 3 | 63% |
|
||||
| **Total** | **27** | **24** | **3** | **89%** |
|
||||
| **Total** | **51** | **48** | **3** | **94%** |
|
||||
|
||||
## Uncovered Items Analysis
|
||||
|
||||
@@ -52,3 +53,68 @@
|
||||
| RESTRICT-HW-01 (ARM64) | Tests run on x64 dev/CI host; cross-architecture testing requires ARM hardware | Low — .NET runtime handles arch differences; no arch-specific code in application | CI builds ARM64 image; manual smoke test on target device |
|
||||
| RESTRICT-ENV-02 (CORS) | CORS is enforced by browsers, not by server-to-server HTTP calls | Low — CORS policy is declarative in Program.cs | Visual inspection of CORS configuration in code |
|
||||
| RESTRICT-OP-01 (Logging) | Log output format/content verification adds complexity without proportional value | Low — Serilog configuration is declarative | Code review of Serilog setup |
|
||||
|
||||
## Cycle 1 Additions (2026-05-13) — AZ-513, AZ-196, AZ-183, AZ-197
|
||||
|
||||
Appended during the existing-code cycle 1 Test-Spec Sync (autodev Step 12). Cycle 1 ACs are namespaced by their tracker ID to avoid colliding with the baseline AC-1..AC-19 numbering above.
|
||||
|
||||
### AZ-513 — Detection Classes CRUD
|
||||
|
||||
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
||||
|-------|---------------------|----------|----------|
|
||||
| AZ-513 AC-1 | POST /classes creates a class | FT-P-14 | Covered |
|
||||
| AZ-513 AC-2 | POST /classes requires ApiAdmin authorization | FT-N-09 | Covered |
|
||||
| AZ-513 AC-3 | PATCH /classes/{id} updates an existing class (full body) | FT-P-15 | Covered |
|
||||
| AZ-513 AC-4 | PATCH /classes/{id} accepts partial body (partial-merge) | FT-P-16 | Covered |
|
||||
| AZ-513 AC-5 | PATCH /classes/{id} returns 404 for unknown id | FT-N-10 | Covered |
|
||||
| AZ-513 AC-6 | PATCH /classes/{id} requires ApiAdmin authorization | FT-N-11 | Covered |
|
||||
| AZ-513 AC-7 | DELETE /classes/{id} removes a class | FT-P-17 | Covered |
|
||||
| AZ-513 AC-8 | DELETE /classes/{id} returns 404 for unknown id | FT-N-12 | Covered |
|
||||
| AZ-513 AC-9 | DELETE /classes/{id} requires ApiAdmin authorization | FT-N-13 | Covered |
|
||||
| AZ-513 AC-10 | UI add/delete/edit affordances work end-to-end | — | Cross-workspace (ui/ e2e harness) — out of scope for this workspace |
|
||||
|
||||
### AZ-196 — Device Auto-Registration
|
||||
|
||||
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
||||
|-------|---------------------|----------|----------|
|
||||
| AZ-196 AC-1 | First device gets serial azj-0000 (shape: serial / email / 32-hex password) | FT-P-18 | Covered |
|
||||
| AZ-196 AC-2 | Sequential numbering on subsequent calls | FT-P-19 | Covered |
|
||||
| AZ-196 AC-3 | Persisted user has Role=CompanionPC, IsEnabled=true | FT-P-20 | Covered (verified via successful login → role-gated behaviour) |
|
||||
| AZ-196 AC-4 | Returned plaintext password is hashed (SHA-384) in DB, not stored plaintext | FT-P-20 | Covered (verified via successful login round-trip) |
|
||||
| AZ-196 AC-5 | Requires ApiAdmin authorization | FT-N-14 | Covered |
|
||||
|
||||
### AZ-183 — Resources OTA Update Check (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 endpoints, service, entity, table, request DTOs, response DTO, cache key, master-key config field, and the e2e test class `ResourceUpdateTests` were all removed.
|
||||
|
||||
| AC ID | Acceptance Criterion | Test IDs | Status |
|
||||
|-------|---------------------|----------|--------|
|
||||
| AZ-183 AC-1 | Resources table created with required columns | — | **Reverted** — table dropped from migration set (`env/db/05_resources.sql` deleted) |
|
||||
| AZ-183 AC-2 | POST /get-update returns newer resources | ~~FT-P-21~~ | **Reverted** — endpoint and test deleted |
|
||||
| AZ-183 AC-3 | POST /get-update returns empty when device already current | ~~FT-P-22~~ | **Reverted** — endpoint and test deleted |
|
||||
| AZ-183 AC-4 | Memory cache avoids DB pressure under 2000-device polling | — | **Reverted** — cache key removed |
|
||||
| AZ-183 AC-5 | Cache invalidated on CI/CD publish | ~~FT-P-23~~ | **Reverted** — endpoint and test deleted |
|
||||
|
||||
### AZ-197 — Hardware-Binding Removal
|
||||
|
||||
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
||||
|-------|---------------------|----------|----------|
|
||||
| AZ-197 AC-1 | Resource download works without `Hardware` field | FT-P-09 / FT-P-10 (legacy bodies retained; wire shape now omits the field) | Covered (e2e `ResourceTests` updated by AZ-197 batch 6) |
|
||||
| AZ-197 AC-2 | `PUT /users/hardware/set` and `POST /resources/check` return 404 | FT-N-15 | Covered |
|
||||
| AZ-197 AC-3 | `Security.GetApiEncryptionKey` signature simplified to (email, password) | — | Internal signature — covered by `Azaion.Test/SecurityTest` unit tests, not blackbox |
|
||||
| AZ-197 AC-4 | `HardwareBindingTests` removed; no remaining test asserts code 40 / hardware-hash binding | — | Build/CI invariant — verified by test-suite enumeration |
|
||||
| AZ-197 AC-5 | Resource calls in remaining tests do not send `Hardware` | — | Build/CI invariant — verified by source review during AZ-197 batch 6 |
|
||||
| AZ-197 AC-6 | `ExceptionEnum` no longer carries `HardwareIdMismatch` / `BadHardware` | — | Build/CI invariant — verified by enum read |
|
||||
| AZ-197 AC-7 | `dotnet build` is clean (no new warnings) | — | Build invariant |
|
||||
| AZ-197 AC-8 | Test suite passes (excluding deleted `HardwareBindingTests`) | All e2e tests + `Azaion.Test` | Covered by Step 11 Run Tests (48/48 e2e + 2/2 unit, 2026-05-13) |
|
||||
|
||||
### Obsoleted Baseline Entries (superseded by AZ-197)
|
||||
|
||||
The matrix rows below are kept for ID stability but no longer reflect production behaviour. They are superseded by the AZ-197 entries above and by FT-N-15 in `blackbox-tests.md`. Do NOT regenerate or delete these in cycle-update mode — wait for a full `/test-spec` rerun.
|
||||
|
||||
| Legacy Matrix Row | Status |
|
||||
|-------------------|--------|
|
||||
| AC-10 (First hardware check stores) | Obsoleted by AZ-197 — endpoint removed |
|
||||
| AC-11 (Subsequent hardware check validates) | Obsoleted by AZ-197 — endpoint removed |
|
||||
| AC-12 (Hardware mismatch returns code 40) | Obsoleted by AZ-197 — `ExceptionEnum` value removed |
|
||||
| AC-19 (Encryption key derived from email+password+hw) | Partially obsoleted — derivation is now `email + password` only |
|
||||
|
||||
Reference in New Issue
Block a user