Files
detections/tests/test_az174_db_driven_config.py
Roman Meshko c9aeed3dd9
ci/woodpecker/push/02-build-push Pipeline was successful
ci/woodpecker/manual/02-build-push Pipeline was successful
ci/woodpecker/manual/01-test Pipeline failed
Added camera config
2026-05-15 09:31:23 +03:00

184 lines
5.4 KiB
Python

import base64
import json
import os
import time
from unittest.mock import MagicMock, patch
import pytest
from fastapi import HTTPException
def _access_jwt(sub: str = "u1") -> str:
secret = os.environ.get("JWT_SECRET", "")
if secret:
import jwt as pyjwt
return pyjwt.encode(
{"exp": int(time.time()) + 3600, "sub": sub},
secret,
algorithm="HS256",
)
raw = json.dumps(
{"exp": int(time.time()) + 3600, "sub": sub}, separators=(",", ":")
).encode()
payload = base64.urlsafe_b64encode(raw).decode().rstrip("=")
return f"h.{payload}.s"
def test_token_manager_decode_user_id_sub():
# Arrange
from main import TokenManager
token = _access_jwt("user-abc")
# Act
uid = TokenManager.decode_user_id(token)
# Assert
assert uid == "user-abc"
def test_token_manager_decode_user_id_invalid():
# Arrange
from main import TokenManager
# Act
uid = TokenManager.decode_user_id("not-a-jwt")
# Assert
assert uid is None
def test_merged_annotation_settings_pascal_case():
# Arrange
from main import _merged_annotation_settings_payload
raw = {
"FramePeriodRecognition": 5,
"ProbabilityThreshold": 0.4,
"Altitude": 300,
"FocalLength": 35,
"SensorWidth": 36,
}
# Act
out = _merged_annotation_settings_payload(raw)
# Assert
assert out["frame_period_recognition"] == 5
assert out["probability_threshold"] == 0.4
assert out["camera_config"]["current_height"] == 300
assert out["camera_config"]["focal_length"] == 35
assert out["camera_config"]["sensor_width"] == 36
def test_merged_annotation_nested_sections():
# Arrange
from main import _merged_annotation_settings_payload
raw = {
"aiRecognitionSettings": {"modelBatchSize": 4},
"cameraSettings": {"altitude": 100},
}
# Act
out = _merged_annotation_settings_payload(raw)
# Assert
assert out["model_batch_size"] == 4
assert out["camera_config"]["current_height"] == 100
def test_resolve_media_for_detect_uses_api_path_and_defaults_when_api_empty():
# Arrange
import main
tm = main.TokenManager(_access_jwt(), "")
mock_ann = MagicMock()
mock_ann.fetch_user_ai_settings.return_value = None
mock_ann.fetch_media_path.return_value = "/m/file.jpg"
with patch("main.annotations_client", mock_ann):
# Act
cfg, path = main._resolve_media_for_detect("mid-1", tm, None)
# Assert
assert path == "/m/file.jpg"
assert "paths" not in cfg
assert "probability_threshold" not in cfg
def test_resolve_media_for_detect_override_wins():
# Arrange
import main
tm = main.TokenManager(_access_jwt(), "")
override = main.AIConfigDto(probability_threshold=0.99)
mock_ann = MagicMock()
mock_ann.fetch_user_ai_settings.return_value = {
"probabilityThreshold": 0.2,
"camera_config": {"current_height": 500},
}
mock_ann.fetch_media_path.return_value = "/m/v.mp4"
with patch("main.annotations_client", mock_ann):
# Act
cfg, path = main._resolve_media_for_detect("vid-1", tm, override)
# Assert
assert cfg["probability_threshold"] == 0.99
assert cfg["camera_config"]["current_height"] == 500
assert path == "/m/v.mp4"
assert "paths" not in cfg
def test_resolve_media_for_detect_merges_camera_config_override():
# Arrange
import main
tm = main.TokenManager(_access_jwt(), "")
override = main.AIConfigDto(
camera_config=main.CameraConfigDto(current_height=500)
)
mock_ann = MagicMock()
mock_ann.fetch_user_ai_settings.return_value = {
"camera_config": {
"focal_length": 35,
"sensor_width": 36,
"current_zoom": 2,
"current_angle": 80,
"current_height": 300,
}
}
mock_ann.fetch_media_path.return_value = "/m/v.mp4"
with patch("main.annotations_client", mock_ann):
# Act
cfg, path = main._resolve_media_for_detect("vid-1", tm, override)
# Assert
assert cfg["camera_config"]["current_height"] == 500
assert cfg["camera_config"]["focal_length"] == 35
assert cfg["camera_config"]["sensor_width"] == 36
assert cfg["camera_config"]["current_zoom"] == 2
assert cfg["camera_config"]["current_angle"] == 80
assert path == "/m/v.mp4"
def test_resolve_media_for_detect_omits_altitude_when_not_provided():
# Arrange
import main
tm = main.TokenManager(_access_jwt(), "")
mock_ann = MagicMock()
mock_ann.fetch_user_ai_settings.return_value = {"probabilityThreshold": 0.2}
mock_ann.fetch_media_path.return_value = "/m/v.mp4"
with patch("main.annotations_client", mock_ann):
# Act
cfg, path = main._resolve_media_for_detect("vid-2", tm, None)
# Assert
assert "camera_config" not in cfg
assert cfg["probability_threshold"] == 0.2
assert path == "/m/v.mp4"
def test_resolve_media_for_detect_raises_when_no_media_path():
# Arrange
import main
tm = main.TokenManager(_access_jwt(), "")
mock_ann = MagicMock()
mock_ann.fetch_user_ai_settings.return_value = {}
mock_ann.fetch_media_path.return_value = None
with patch("main.annotations_client", mock_ann):
# Act / Assert
with pytest.raises(HTTPException) as exc:
main._resolve_media_for_detect("missing", tm, None)
assert exc.value.status_code == 503