# Blackbox Tests ## Positive Scenarios ### FT-P-01: Health endpoint returns healthy **Summary**: Verify the liveness probe returns a healthy status without authentication. **Traces to**: AC-1 **Category**: Health Check **Preconditions**: Loader service is running. **Input data**: None **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | GET /health | HTTP 200, body: `{"status": "healthy"}` | **Expected outcome**: HTTP 200 with exact body `{"status": "healthy"}` **Max execution time**: 2s --- ### FT-P-02: Status reports unauthenticated state **Summary**: Verify status endpoint reports no authentication before login. **Traces to**: AC-1 **Category**: Health Check **Preconditions**: Loader service is running, no prior login. **Input data**: None **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | GET /status | HTTP 200, body contains `"authenticated": false` and `"modelCacheDir": "models"` | **Expected outcome**: HTTP 200 with `authenticated=false` **Max execution time**: 2s --- ### FT-P-03: Login with valid credentials **Summary**: Verify login succeeds with valid email/password and sets credentials on the API client. **Traces to**: AC-2, AC-14 **Category**: Authentication **Preconditions**: Loader service is running, mock API configured to accept credentials. **Input data**: `{"email": "test@azaion.com", "password": "validpass"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /login with valid credentials | HTTP 200, body: `{"status": "ok"}` | | 2 | GET /status | HTTP 200, body contains `"authenticated": true` | **Expected outcome**: Login returns 200; subsequent status shows authenticated=true **Max execution time**: 5s --- ### FT-P-04: Download resource via binary-split **Summary**: Verify a resource can be downloaded and decrypted through the big/small split scheme. **Traces to**: AC-4, AC-11, AC-13 **Category**: Resource Download **Preconditions**: Logged in; mock API serves encrypted small part; mock CDN hosts big part. **Input data**: `{"filename": "testmodel", "folder": "models"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /login with valid credentials | HTTP 200 | | 2 | POST /load/testmodel with body `{"filename": "testmodel", "folder": "models"}` | HTTP 200, Content-Type: application/octet-stream, non-empty body | **Expected outcome**: HTTP 200 with binary content matching the original test resource **Max execution time**: 10s --- ### FT-P-05: Upload resource via binary-split **Summary**: Verify a resource can be uploaded, split, encrypted, and stored. **Traces to**: AC-5 **Category**: Resource Upload **Preconditions**: Logged in; mock API accepts uploads; mock CDN accepts writes. **Input data**: Binary test file + folder="models" **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /login with valid credentials | HTTP 200 | | 2 | POST /upload/testmodel multipart (file=test_bytes, folder="models") | HTTP 200, body: `{"status": "ok"}` | **Expected outcome**: Upload returns 200; big part present on CDN, small part on mock API **Max execution time**: 10s --- ### FT-P-06: Unlock starts background workflow **Summary**: Verify unlock endpoint starts the background decryption and Docker loading workflow. **Traces to**: AC-6, AC-9 **Category**: Docker Unlock **Preconditions**: Encrypted test archive at IMAGES_PATH; Docker daemon accessible; mock API configured. **Input data**: `{"email": "test@azaion.com", "password": "validpass"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /unlock with valid credentials | HTTP 200, body contains `"state"` field | | 2 | Poll GET /unlock/status until state changes | States progress through: authenticating → downloading_key → decrypting → loading_images → ready | **Expected outcome**: Final state is "ready" **Max execution time**: 60s --- ### FT-P-07: Unlock detects already-loaded images **Summary**: Verify unlock returns immediately when Docker images are already present. **Traces to**: AC-7 **Category**: Docker Unlock **Preconditions**: All 7 API_SERVICES Docker images already loaded with correct version tag. **Input data**: `{"email": "test@azaion.com", "password": "validpass"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /unlock with valid credentials | HTTP 200, body: `{"state": "ready"}` | **Expected outcome**: Immediate ready state, no background processing **Max execution time**: 5s --- ### FT-P-08: Unlock status poll **Summary**: Verify unlock status endpoint returns current state and error. **Traces to**: AC-8 **Category**: Docker Unlock **Preconditions**: No unlock started (idle state). **Input data**: None **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | GET /unlock/status | HTTP 200, body: `{"state": "idle", "error": null}` | **Expected outcome**: State is idle, error is null **Max execution time**: 2s --- ## Negative Scenarios ### FT-N-01: Login with invalid credentials **Summary**: Verify login rejects invalid credentials with HTTP 401. **Traces to**: AC-3 **Category**: Authentication **Preconditions**: Loader service is running; mock API rejects these credentials. **Input data**: `{"email": "bad@test.com", "password": "wrongpass"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /login with invalid credentials | HTTP 401, body has `"detail"` field | **Expected outcome**: HTTP 401 with error detail **Max execution time**: 5s --- ### FT-N-02: Login with missing fields **Summary**: Verify login rejects requests with missing email/password fields. **Traces to**: AC-3 **Category**: Authentication **Preconditions**: Loader service is running. **Input data**: `{}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /login with empty JSON body | HTTP 422 (validation error) | **Expected outcome**: HTTP 422 from Pydantic validation **Max execution time**: 2s --- ### FT-N-03: Upload without file attachment **Summary**: Verify upload rejects requests without a file. **Traces to**: AC-5 (negative) **Category**: Resource Upload **Preconditions**: Logged in. **Input data**: POST without multipart file **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /upload/testfile without file attachment | HTTP 422 | **Expected outcome**: HTTP 422 validation error **Max execution time**: 2s --- ### FT-N-04: Download non-existent resource **Summary**: Verify download returns 500 when the requested resource does not exist. **Traces to**: AC-4 (negative) **Category**: Resource Download **Preconditions**: Logged in; resource "nonexistent" does not exist on API or CDN. **Input data**: `{"filename": "nonexistent", "folder": "models"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /load/nonexistent with body | HTTP 500, body has `"detail"` field | **Expected outcome**: HTTP 500 with error detail **Max execution time**: 10s --- ### FT-N-05: Unlock without encrypted archive **Summary**: Verify unlock returns 404 when no encrypted archive is present and images are not loaded. **Traces to**: AC-10 **Category**: Docker Unlock **Preconditions**: No file at IMAGES_PATH; Docker images not loaded. **Input data**: `{"email": "test@azaion.com", "password": "validpass"}` **Steps**: | Step | Consumer Action | Expected System Response | |------|----------------|------------------------| | 1 | POST /unlock with valid credentials | HTTP 404, body has `"detail"` containing "Encrypted archive not found" | **Expected outcome**: HTTP 404 with archive-not-found message **Max execution time**: 5s