Files
Imrayya 871a3cc174
Build Android APK / build (push) Failing after 5m7s
Build Server .exe / build (push) Failing after 2m27s
feat: config files, settings screen, hardware check
- Add config.py with multi-source config (YAML, .env, env vars)
- Add .env.example and config.yaml.example
- Add settings API endpoints (GET/POST /api/v1/settings)
- Add status endpoint (GET /api/v1/status) with hardware subsystem check
- Add settings screen to web client (protected by API token)
- Add localization strings for settings UI
- Add tests for config module and new endpoints
- Update README with config documentation
2026-07-01 13:32:08 +00:00

150 lines
4.8 KiB
Python

"""Tests for the FastAPI application endpoints."""
from __future__ import annotations
import os
from unittest.mock import AsyncMock, patch
import pytest
from fastapi.testclient import TestClient
from ..main import app
@pytest.fixture
def client() -> TestClient:
return TestClient(app)
class TestHealth:
def test_health_returns_ok(self, client: TestClient) -> None:
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
class TestGetState:
def test_get_state_returns_dict(self, client: TestClient) -> None:
with (
patch.object(app.state, "media", create=True),
patch.object(app.state, "audio", create=True),
patch.object(app.state, "notifications", create=True),
patch.object(app.state, "telemetry", create=True),
):
app.state.media.get_state = lambda: {"title": "Test"}
app.state.audio.get_state = lambda: {"backend": "windows"}
app.state.notifications.get_active = list
app.state.telemetry.get_state = dict
response = client.get("/api/v1/state")
assert response.status_code == 200
data = response.json()
assert "media" in data
assert "audio" in data
assert "notifications" in data
assert "telemetry" in data
class TestNotifyEndpoint:
def test_notify_without_token_returns_401(self, client: TestClient) -> None:
response = client.post(
"/api/v1/notify",
json={
"type": "alert",
"title": "Test",
"message": "Hello",
},
)
assert response.status_code == 401
def test_notify_with_valid_token(self, client: TestClient) -> None:
os.environ["API_TOKEN"] = "test-token-123"
with patch.object(app.state, "notifications", create=True) as mock_notif:
mock_notif.create = AsyncMock()
mock_notif.create.return_value = type(
"Notification", (), {"id": "test-id"}
)()
response = client.post(
"/api/v1/notify",
json={
"type": "alert",
"title": "Build Complete",
"message": "Success",
},
headers={"Authorization": "Bearer test-token-123"},
)
assert response.status_code == 200
assert response.json()["status"] == "created"
# Cleanup
del os.environ["API_TOKEN"]
def test_notify_with_wrong_token_returns_401(self, client: TestClient) -> None:
os.environ["API_TOKEN"] = "correct-token"
response = client.post(
"/api/v1/notify",
json={"type": "alert", "title": "Test", "message": "Hello"},
headers={"Authorization": "Bearer wrong-token"},
)
assert response.status_code == 401
del os.environ["API_TOKEN"]
class TestSettingsEndpoint:
def test_get_settings_without_token_returns_401(self, client: TestClient) -> None:
response = client.get("/api/v1/settings")
assert response.status_code == 401
def test_get_settings_with_valid_token(self, client: TestClient) -> None:
os.environ["API_TOKEN"] = "settings-token"
response = client.get(
"/api/v1/settings",
headers={"Authorization": "Bearer settings-token"},
)
assert response.status_code == 200
data = response.json()
assert "dashboard_pin" in data
assert "api_token" in data
assert "host" in data
assert "port" in data
del os.environ["API_TOKEN"]
def test_update_settings(self, client: TestClient) -> None:
os.environ["API_TOKEN"] = "update-token"
response = client.post(
"/api/v1/settings",
json={
"dashboard_pin": "9999",
"api_token": "new-token",
},
headers={"Authorization": "Bearer update-token"},
)
assert response.status_code == 200
data = response.json()
assert data["dashboard_pin"] == "9999"
assert data["api_token"] == "new-token"
del os.environ["API_TOKEN"]
class TestStatusEndpoint:
def test_status_returns_subsystems(self, client: TestClient) -> None:
# Ensure subsystems are initialized
if not hasattr(app.state, "subsystems"):
app.state.subsystems = {}
response = client.get("/api/v1/status")
assert response.status_code == 200
data = response.json()
assert "subsystems" in data
assert "config" in data
assert "pin_configured" in data["config"]
assert "token_configured" in data["config"]