Files
detections/_docs/02_document/tests/blackbox-tests.md
T

592 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Blackbox Tests
## Positive Scenarios
### FT-P-01: Health check returns status before engine initialization
**Summary**: Verify the health endpoint responds correctly when the inference engine has not yet been initialized.
**Traces to**: AC-API-1, AC-EL-1
**Category**: API, Engine Lifecycle
**Preconditions**:
- Detections service is running
- No detection requests have been made (engine is not initialized)
**Input data**: None
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `GET /health` | 200 OK with `{"status": "healthy", "aiAvailability": "None"}` |
**Expected outcome**: Health endpoint returns `status: "healthy"` and `aiAvailability: "None"` (engine not yet loaded).
**Max execution time**: 2s
---
### FT-P-02: Health check reflects engine availability after initialization
**Summary**: Verify the health endpoint reports the correct engine state after the engine has been initialized by a detection request.
**Traces to**: AC-API-1, AC-EL-2
**Category**: API, Engine Lifecycle
**Preconditions**:
- Detections service is running
- Mock-loader serves the ONNX model file
- At least one successful detection has been performed (engine initialized)
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image (trigger engine init) | 200 OK with detection results |
| 2 | `GET /health` | 200 OK with `aiAvailability` set to `"Enabled"` or `"Warning"` |
**Expected outcome**: `aiAvailability` reflects an initialized engine state (not `"None"` or `"Downloading"`).
**Max execution time**: 30s (includes engine init on first call)
---
### FT-P-03: Single image detection returns detections
**Summary**: Verify that a valid small image submitted via POST /detect returns structured detection results.
**Traces to**: AC-DA-1, AC-API-2
**Category**: Detection Accuracy, API
**Preconditions**:
- Engine is initialized (or will be on this call)
- Mock-loader serves the model
**Input data**: small-image (640×480, contains detectable objects)
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image as multipart file | 200 OK |
| 2 | Parse response JSON | Array of detection objects, each with `x`, `y`, `width`, `height`, `label`, `confidence` |
| 3 | Verify all confidence values | Every detection has `confidence >= 0.25` (default probability_threshold) |
**Expected outcome**: Non-empty array of DetectionDto objects. All confidences meet threshold. Each detection has valid bounding box coordinates (0.01.0 range).
**Max execution time**: 30s
---
### FT-P-04: Large image triggers GSD-based tiling
**Summary**: Verify that an image exceeding 1.5× model dimensions is tiled and processed with tile-level detection results merged.
**Traces to**: AC-IP-1, AC-IP-2
**Category**: Image Processing
**Preconditions**:
- Engine is initialized
- Config includes altitude, focal_length, sensor_width for GSD calculation
**Input data**: large-image (4000×3000)
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with large-image and config `{"altitude": 400, "focal_length": 24, "sensor_width": 23.5}` | 200 OK |
| 2 | Parse response JSON | Array of detections |
| 3 | Verify detection coordinates | Bounding box coordinates are in 0.01.0 range relative to the full original image |
**Expected outcome**: Detections returned for the full image. Coordinates are normalized to original image dimensions (not tile dimensions). Processing time is longer than small-image due to tiling.
**Max execution time**: 60s
---
### FT-P-05: Detection confidence filtering respects threshold
**Summary**: Verify that detections below the configured probability_threshold are filtered out.
**Traces to**: AC-DA-1
**Category**: Detection Accuracy
**Preconditions**:
- Engine is initialized
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image and config `{"probability_threshold": 0.8}` | 200 OK |
| 2 | Parse response JSON | All returned detections have `confidence >= 0.8` |
| 3 | `POST /detect` with same image and config `{"probability_threshold": 0.1}` | 200 OK |
| 4 | Compare result counts | Step 3 returns >= number of detections from Step 1 |
**Expected outcome**: Higher threshold produces fewer or equal detections. No detection below threshold appears in results.
**Max execution time**: 30s
---
### FT-P-06: Overlapping detections are deduplicated
**Summary**: Verify that overlapping detections with containment ratio above threshold are deduplicated, keeping the higher-confidence one.
**Traces to**: AC-DA-2
**Category**: Detection Accuracy
**Preconditions**:
- Engine is initialized
- Image produces overlapping detections (dense scene)
**Input data**: small-image (scene with clustered objects)
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image and config `{"tracking_intersection_threshold": 0.6}` | 200 OK |
| 2 | Collect detections | No two detections of the same class overlap by more than 60% containment ratio |
| 3 | `POST /detect` with same image and config `{"tracking_intersection_threshold": 0.01}` | 200 OK |
| 4 | Compare result counts | Step 3 returns fewer or equal detections (more aggressive dedup) |
**Expected outcome**: No pair of returned detections exceeds the configured overlap threshold.
**Max execution time**: 30s
---
### FT-P-07: Physical size filtering removes oversized detections
**Summary**: Verify that detections exceeding the MaxSizeM for their class (given GSD) are removed.
**Traces to**: AC-DA-4
**Category**: Detection Accuracy
**Preconditions**:
- Engine is initialized
- classes.json loaded with MaxSizeM values
**Input data**: small-image, config with known GSD parameters
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image and config `{"altitude": 400, "focal_length": 24, "sensor_width": 23.5}` | 200 OK |
| 2 | For each detection, compute physical size from bounding box + GSD | No detection's physical size exceeds the MaxSizeM defined for its class in classes.json |
**Expected outcome**: All returned detections have plausible physical dimensions for their class.
**Max execution time**: 30s
---
### FT-P-08: Async media detection returns "started" immediately
**Summary**: Verify that POST /detect/{media_id} returns immediately with status "started" while processing continues in background.
**Traces to**: AC-API-3
**Category**: API
**Preconditions**:
- Engine is initialized
- Media file paths are available via config
**Input data**: jwt-token, test-video path in config
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect/test-media-001` with config paths and auth headers | 200 OK, `{"status": "started"}` |
| 2 | Measure response time | Response arrives within 1s (before video processing completes) |
**Expected outcome**: Immediate response with `{"status": "started"}`. Processing continues asynchronously.
**Max execution time**: 2s (response only; processing continues in background)
---
### FT-P-09: SSE streaming delivers detection events during async processing
**Summary**: Verify that SSE clients receive real-time detection events during async media detection.
**Traces to**: AC-API-4, AC-API-3
**Category**: API
**Preconditions**:
- Engine is initialized
- SSE client connected before triggering detection
**Input data**: jwt-token, test-video path in config
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Open SSE connection: `GET /detect/stream` | Connection established |
| 2 | `POST /detect/test-media-002` with config and auth headers | `{"status": "started"}` |
| 3 | Listen on SSE connection | Receive events with `mediaStatus: "AIProcessing"` as frames are processed |
| 4 | Wait for completion | Final event with `mediaStatus: "AIProcessed"` and `percent: 100` |
**Expected outcome**: Multiple SSE events received. Events include detection data. Final event signals completion.
**Max execution time**: 120s
---
### FT-P-10: Video frame sampling processes every Nth frame
**Summary**: Verify that video processing respects the `frame_period_recognition` setting.
**Traces to**: AC-VP-1
**Category**: Video Processing
**Preconditions**:
- Engine is initialized
- SSE client connected
**Input data**: test-video (10s, 30fps = 300 frames), config `{"frame_period_recognition": 4}`
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Open SSE connection | Connection established |
| 2 | `POST /detect/test-media-003` with config `{"frame_period_recognition": 4, "paths": ["/media/test-video.mp4"]}` | `{"status": "started"}` |
| 3 | Count distinct SSE events with detection data | Number of processed frames ≈ 300/4 = 75 (±10% tolerance for start/end frames) |
**Expected outcome**: Approximately 75 frames processed (not all 300). The count scales proportionally with frame_period_recognition.
**Max execution time**: 120s
---
### FT-P-11: Video annotation interval enforcement
**Summary**: Verify that annotations are not reported more frequently than `frame_recognition_seconds`.
**Traces to**: AC-VP-2
**Category**: Video Processing
**Preconditions**:
- Engine is initialized
- SSE client connected
**Input data**: test-video, config `{"frame_recognition_seconds": 2}`
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Open SSE connection | Connection established |
| 2 | `POST /detect/test-media-004` with config `{"frame_recognition_seconds": 2, "paths": ["/media/test-video.mp4"]}` | `{"status": "started"}` |
| 3 | Record timestamps of consecutive SSE detection events | Minimum gap between consecutive annotation events ≥ 2 seconds |
**Expected outcome**: No two annotation events are closer than 2 seconds apart.
**Max execution time**: 120s
---
### FT-P-12: Video tracking accepts new annotations on movement
**Summary**: Verify that new annotations are accepted when detections move beyond the tracking threshold.
**Traces to**: AC-VP-3
**Category**: Video Processing
**Preconditions**:
- Engine is initialized
- SSE client connected
- Video contains moving objects
**Input data**: test-video, config with `tracking_distance_confidence > 0`
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Open SSE connection | Connection established |
| 2 | `POST /detect/test-media-005` with config `{"tracking_distance_confidence": 0.05, "paths": ["/media/test-video.mp4"]}` | `{"status": "started"}` |
| 3 | Collect SSE events | Annotations are emitted when object positions change between frames |
**Expected outcome**: Annotations contain updated positions reflecting object movement. Static objects do not generate redundant annotations.
**Max execution time**: 120s
---
### FT-P-13: Weather mode class variants
**Summary**: Verify that the system supports detection across different weather mode class variants (Norm, Wint, Night).
**Traces to**: AC-OC-1
**Category**: Object Classes
**Preconditions**:
- Engine is initialized
- classes.json includes weather-mode variants
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image | 200 OK |
| 2 | Inspect returned detection labels | Labels correspond to valid class names from classes.json (base or weather-variant) |
**Expected outcome**: All returned labels are valid entries from the 19-class × 3-mode registry.
**Max execution time**: 30s
---
### FT-P-14: Engine lazy initialization on first detection request
**Summary**: Verify that the engine is not initialized at startup but is initialized on the first detection request.
**Traces to**: AC-EL-1, AC-EL-2
**Category**: Engine Lifecycle
**Preconditions**:
- Fresh service start, no prior requests
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `GET /health` immediately after service starts | `aiAvailability: "None"` — engine not loaded |
| 2 | `POST /detect` with small-image | 200 OK (may take longer — engine initializing) |
| 3 | `GET /health` | `aiAvailability` changed to `"Enabled"` or status indicating engine is active |
**Expected outcome**: Engine transitions from "None" to an active state only after a detection request.
**Max execution time**: 60s
---
### FT-P-15: ONNX fallback when GPU unavailable
**Summary**: Verify that the system falls back to ONNX Runtime when no compatible GPU is available.
**Traces to**: AC-EL-2, RESTRICT-HW-1
**Category**: Engine Lifecycle
**Preconditions**:
- Detections service running WITHOUT GPU runtime (CPU-only Docker profile)
- Mock-loader serves ONNX model
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with small-image | 200 OK with detection results |
| 2 | `GET /health` | `aiAvailability` indicates engine is active (ONNX fallback) |
**Expected outcome**: Detection succeeds via ONNX Runtime. No TensorRT-related errors.
**Max execution time**: 60s
---
### FT-P-16: Tile deduplication removes duplicate detections at tile boundaries
**Summary**: Verify that detections appearing in overlapping tile regions are deduplicated.
**Traces to**: AC-DA-3
**Category**: Detection Accuracy
**Preconditions**:
- Engine is initialized
- Large image that triggers tiling
**Input data**: large-image with config including GSD parameters and `big_image_tile_overlap_percent: 20`
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with large-image and tiling config | 200 OK |
| 2 | Inspect detections near tile boundaries | No two detections of the same class are within 0.01 coordinate difference of each other (TILE_DUPLICATE_CONFIDENCE_THRESHOLD) |
**Expected outcome**: Tile boundary detections are merged. No duplicates with near-identical coordinates remain.
**Max execution time**: 60s
---
## Negative Scenarios
### FT-N-01: Empty image returns 400
**Summary**: Verify that submitting an empty file to POST /detect returns a 400 error.
**Traces to**: AC-API-2 (negative case)
**Category**: API
**Preconditions**:
- Detections service is running
**Input data**: empty-image (zero-byte file)
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with empty-image as multipart file | 400 Bad Request |
**Expected outcome**: HTTP 400 with error message indicating empty or invalid image.
**Max execution time**: 5s
---
### FT-N-02: Invalid image data returns 400
**Summary**: Verify that submitting a corrupt/non-image file returns a 400 error.
**Traces to**: AC-API-2 (negative case)
**Category**: API
**Preconditions**:
- Detections service is running
**Input data**: corrupt-image (random binary data)
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect` with corrupt-image as multipart file | 400 Bad Request |
**Expected outcome**: HTTP 400. Image decoding fails gracefully with an error response (not a 500).
**Max execution time**: 5s
---
### FT-N-03: Detection when engine unavailable returns 503
**Summary**: Verify that a detection request returns 503 when the engine cannot be initialized.
**Traces to**: AC-API-2 (negative case), AC-EL-2
**Category**: API, Engine Lifecycle
**Preconditions**:
- Mock-loader configured to return errors (model download fails)
- Engine has not been previously initialized
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Configure mock-loader to return 503 on model requests | — |
| 2 | `POST /detect` with small-image | 503 Service Unavailable or 422 |
**Expected outcome**: HTTP 503 or 422 error indicating engine is not available. No crash or unhandled exception.
**Max execution time**: 30s
---
### FT-N-04: Duplicate media_id returns 409
**Summary**: Verify that submitting a second async detection request with an already-active media_id returns 409.
**Traces to**: AC-API-3 (negative case)
**Category**: API
**Preconditions**:
- Engine is initialized
- An async detection is already in progress for media_id "dup-test"
**Input data**: jwt-token, test-video
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | `POST /detect/dup-test` with config and auth headers | `{"status": "started"}` |
| 2 | Immediately `POST /detect/dup-test` again (same media_id) | 409 Conflict |
**Expected outcome**: Second request is rejected with 409. First detection continues normally.
**Max execution time**: 5s
---
### FT-N-05: Missing classes.json prevents startup
**Summary**: Verify that the service fails or returns no detections when classes.json is not present.
**Traces to**: RESTRICT-SW-4
**Category**: Restrictions
**Preconditions**:
- Detections service started WITHOUT classes.json volume mount
**Input data**: None
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Attempt to start detections service without classes.json | Service fails to start OR starts with empty class registry |
| 2 | If started: `POST /detect` with small-image | Empty detections or error response |
**Expected outcome**: Service either fails to start or returns no detections. No unhandled crash.
**Max execution time**: 30s
---
### FT-N-06: Loader service unreachable during model download
**Summary**: Verify that the system handles Loader service being unreachable during engine initialization.
**Traces to**: RESTRICT-ENV-1, AC-EL-2
**Category**: Resilience, Engine Lifecycle
**Preconditions**:
- Mock-loader is stopped or unreachable
- Engine not yet initialized
**Input data**: small-image
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Stop mock-loader service | — |
| 2 | `POST /detect` with small-image | Error response (503 or 422) |
| 3 | `GET /health` | `aiAvailability` reflects error state |
**Expected outcome**: Detection fails gracefully. Health endpoint reflects the engine error state.
**Max execution time**: 30s
---
### FT-N-07: Annotations service unreachable — detection continues
**Summary**: Verify that async detection continues even when the Annotations service is unreachable.
**Traces to**: RESTRICT-ENV-2
**Category**: Resilience
**Preconditions**:
- Engine is initialized
- Mock-annotations is stopped or returns errors
- SSE client connected
**Input data**: jwt-token, test-video
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Stop mock-annotations service | — |
| 2 | `POST /detect/test-media-006` with config and auth | `{"status": "started"}` |
| 3 | Listen on SSE | Detection events still arrive (annotations POST failure is silently caught) |
| 4 | Wait for completion | Final `AIProcessed` event received |
**Expected outcome**: Detection processing completes. SSE events are delivered. Annotations POST failure does not stop the detection pipeline.
**Max execution time**: 120s
---
### FT-N-08: SSE queue overflow is silently dropped
**Summary**: Verify that when an SSE client's queue reaches 100 events, additional events are dropped without error.
**Traces to**: AC-API-4
**Category**: API
**Preconditions**:
- Engine is initialized
- SSE client connected but NOT consuming events (stalled reader)
**Input data**: test-video (generates many events)
**Steps**:
| Step | Consumer Action | Expected System Response |
|------|----------------|------------------------|
| 1 | Open SSE connection but pause reading | Connection established |
| 2 | `POST /detect/test-media-007` with config that generates > 100 events | `{"status": "started"}` |
| 3 | Wait for processing to complete | No error on the detection side |
| 4 | Resume reading SSE | Receive ≤ 100 events (queue max depth) |
**Expected outcome**: No crash or error. Overflow events are silently dropped. Detection completes normally.
**Max execution time**: 120s