Add comprehensive test suites for: - NPM client (27 tests) - Ollama client (16 tests) - AI client and controller (34 tests) - Static controller (8 tests) - Tools controller DNS lookup (9 tests) - OIDC authentication (10 tests) - Housekeeping endpoints (28 tests) - Infrastructure endpoints (15 tests) - Health endpoints (12 tests) - Portainer client (12 tests) - Home Assistant client (24 tests) Total: 285 tests passing with 65% code coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
"""Tests for static controller."""
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import patch, MagicMock
|
|
from pathlib import Path
|
|
import tempfile
|
|
import os
|
|
|
|
from src.main import app
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
"""Create a test client."""
|
|
return TestClient(app)
|
|
|
|
|
|
class TestListWidgets:
|
|
"""Test /static/widgets endpoint."""
|
|
|
|
def test_list_widgets_returns_200(self, client):
|
|
"""List widgets should return 200."""
|
|
response = client.get("/static/widgets")
|
|
assert response.status_code == 200
|
|
|
|
def test_list_widgets_returns_widgets_list(self, client):
|
|
"""List widgets should return widgets array."""
|
|
response = client.get("/static/widgets")
|
|
data = response.json()
|
|
|
|
assert "widgets" in data
|
|
assert "count" in data
|
|
assert isinstance(data["widgets"], list)
|
|
|
|
@patch("src.controllers.static_controller.StaticController")
|
|
def test_list_widgets_handles_missing_directory(self, mock_controller_class, client):
|
|
"""List widgets should handle missing widgets directory."""
|
|
# Create a mock controller with non-existent static dir
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
mock_static_dir = Path(tmpdir) / "nonexistent"
|
|
|
|
with patch.object(
|
|
client.app.state if hasattr(client.app, 'state') else client.app,
|
|
'static_dir',
|
|
mock_static_dir,
|
|
create=True
|
|
):
|
|
# The actual endpoint handles this case gracefully
|
|
response = client.get("/static/widgets")
|
|
# Should still return 200 with empty list or message
|
|
assert response.status_code == 200
|
|
|
|
|
|
class TestGetWidget:
|
|
"""Test /static/widgets/{filename} endpoint."""
|
|
|
|
def test_get_widget_returns_404_for_nonexistent(self, client):
|
|
"""Get widget should return 404 for non-existent file."""
|
|
response = client.get("/static/widgets/nonexistent-widget.html")
|
|
assert response.status_code == 404
|
|
|
|
def test_get_widget_returns_html_content_type(self, client):
|
|
"""Get widget should return HTML content type for existing file."""
|
|
# First check if any widgets exist
|
|
list_response = client.get("/static/widgets")
|
|
widgets = list_response.json().get("widgets", [])
|
|
|
|
if widgets:
|
|
# Test with first available widget
|
|
widget_name = widgets[0]["name"]
|
|
response = client.get(f"/static/widgets/{widget_name}")
|
|
assert response.status_code == 200
|
|
assert "text/html" in response.headers.get("content-type", "")
|
|
|
|
def test_get_widget_prevents_path_traversal(self, client):
|
|
"""Get widget should prevent path traversal attacks."""
|
|
# Attempt path traversal
|
|
response = client.get("/static/widgets/../../../etc/passwd")
|
|
# Should either return 404 or 403, not the actual file
|
|
assert response.status_code in [400, 403, 404]
|
|
|
|
def test_get_widget_includes_cache_headers(self, client):
|
|
"""Get widget should include no-cache headers."""
|
|
list_response = client.get("/static/widgets")
|
|
widgets = list_response.json().get("widgets", [])
|
|
|
|
if widgets:
|
|
widget_name = widgets[0]["name"]
|
|
response = client.get(f"/static/widgets/{widget_name}")
|
|
|
|
if response.status_code == 200:
|
|
assert "no-cache" in response.headers.get("cache-control", "")
|
|
|
|
|
|
class TestStaticControllerInit:
|
|
"""Test StaticController initialization."""
|
|
|
|
def test_controller_has_static_dir(self):
|
|
"""Controller should have static directory configured."""
|
|
from src.controllers.static_controller import static_controller
|
|
|
|
assert static_controller.static_dir is not None
|
|
assert isinstance(static_controller.static_dir, Path)
|
|
|
|
def test_controller_has_correct_prefix(self):
|
|
"""Controller should have /static prefix."""
|
|
from src.controllers.static_controller import static_controller
|
|
|
|
assert static_controller.prefix == "/static"
|
|
|
|
def test_controller_has_correct_tags(self):
|
|
"""Controller should have Static tag."""
|
|
from src.controllers.static_controller import static_controller
|
|
|
|
assert "Static" in static_controller.tags
|