Improve test coverage to 65%
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>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user