feat: add dashboard API with quick links and widgets
Build and Push / build (release) Successful in 1m28s

- Dashboard domain with Quick Links CRUD + reorder endpoints
- Dashboard widgets management endpoints
- Database migrations for quick_links and dashboard_widgets tables
- Static file controller for Organizr widgets
- Default local user when OIDC is disabled
- Domain-based architecture refactor (src/domains/, src/shared/)
- Test suite updated for new structure (285 tests passing)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-01-03 12:27:57 +01:00
co-authored by Claude Opus 4.5
parent e85c9a123d
commit 381d43b60b
51 changed files with 8215 additions and 784 deletions
+31 -72
View File
@@ -31,74 +31,31 @@ class TestRootEndpoint:
assert data["service"] == "Core Code API"
assert data["status"] == "healthy"
def test_root_returns_documentation_links(self, client):
"""Root endpoint should return documentation links."""
def test_root_returns_docs_link(self, client):
"""Root endpoint should return docs link."""
response = client.get("/")
data = response.json()
assert "documentation" in data
assert "swagger_ui" in data["documentation"]
assert "redoc" in data["documentation"]
def test_root_returns_endpoints(self, client):
"""Root endpoint should return available endpoints."""
response = client.get("/")
data = response.json()
assert "endpoints" in data
assert "health" in data["endpoints"]
assert "docs" in data
assert data["docs"] == "/docs"
class TestHealthEndpoint:
"""Test /health endpoint."""
@patch("src.controllers.health_controller.get_ollama_client")
def test_health_returns_200_when_ollama_healthy(self, mock_get_ollama, client):
"""Health endpoint should return 200 when Ollama is healthy."""
mock_client = AsyncMock()
mock_client.health_check.return_value = True
mock_get_ollama.return_value = mock_client
def test_health_returns_200(self, client):
"""Health endpoint should return 200."""
response = client.get("/health")
assert response.status_code == 200
@patch("src.controllers.health_controller.get_ollama_client")
def test_health_returns_status(self, mock_get_ollama, client):
def test_health_returns_status(self, client):
"""Health endpoint should return status information."""
mock_client = AsyncMock()
mock_client.health_check.return_value = True
mock_get_ollama.return_value = mock_client
response = client.get("/health")
data = response.json()
assert "status" in data
assert "version" in data
assert "ollama_connected" in data
@patch("src.controllers.health_controller.get_ollama_client")
def test_health_returns_ollama_connected_true(self, mock_get_ollama, client):
"""Health should report Ollama connected when healthy."""
mock_client = AsyncMock()
mock_client.health_check.return_value = True
mock_get_ollama.return_value = mock_client
response = client.get("/health")
data = response.json()
assert data["ollama_connected"] is True
@patch("src.controllers.health_controller.get_ollama_client")
def test_health_returns_ollama_connected_false(self, mock_get_ollama, client):
"""Health should report Ollama disconnected when unhealthy."""
mock_client = AsyncMock()
mock_client.health_check.return_value = False
mock_get_ollama.return_value = mock_client
response = client.get("/health")
data = response.json()
assert data["ollama_connected"] is False
assert data["status"] == "healthy"
class TestOpenAPIEndpoint:
@@ -118,31 +75,30 @@ class TestOpenAPIEndpoint:
response = client.get("/docs")
assert response.status_code == 200
def test_redoc_available(self, client):
"""ReDoc should be available."""
response = client.get("/redoc")
assert response.status_code == 200
class TestFullHealthCheck:
"""Test /health/full endpoint."""
@patch("src.controllers.health_controller.get_ollama_client")
def test_full_health_returns_503_when_unhealthy(self, mock_get_ollama, client):
@patch("src.shared.database.Database.health_check")
@patch("src.models.ollama_client.get_ollama_client")
def test_full_health_returns_503_when_unhealthy(self, mock_get_ollama, mock_db_health, client):
"""Full health should return 503 when Ollama unhealthy."""
mock_client = AsyncMock()
mock_client.health_check.return_value = False
mock_get_ollama.return_value = mock_client
mock_db_health.return_value = True
response = client.get("/health/full")
assert response.status_code == 503
@patch("src.controllers.health_controller.get_ollama_client")
def test_full_health_returns_components_status(self, mock_get_ollama, client):
@patch("src.shared.database.Database.health_check")
@patch("src.models.ollama_client.get_ollama_client")
def test_full_health_returns_components_status(self, mock_get_ollama, mock_db_health, client):
"""Full health should return component status."""
mock_client = AsyncMock()
mock_client.health_check.return_value = False
mock_get_ollama.return_value = mock_client
mock_db_health.return_value = True
response = client.get("/health/full")
data = response.json()
@@ -150,15 +106,18 @@ class TestFullHealthCheck:
assert "status" in data
assert "components" in data
assert "ollama" in data["components"]
assert "database" in data["components"]
assert "response_time_ms" in data
@patch("src.controllers.health_controller.get_ollama_client")
def test_full_health_handles_list_models_error(self, mock_get_ollama, client):
@patch("src.shared.database.Database.health_check")
@patch("src.models.ollama_client.get_ollama_client")
def test_full_health_handles_list_models_error(self, mock_get_ollama, mock_db_health, client):
"""Full health should handle list_models errors."""
mock_client = AsyncMock()
mock_client.health_check.return_value = True
mock_client.list_models.side_effect = Exception("Connection error")
mock_get_ollama.return_value = mock_client
mock_db_health.return_value = True
response = client.get("/health/full")
data = response.json()
@@ -166,13 +125,15 @@ class TestFullHealthCheck:
# Should report error in component status
assert "ollama" in data["components"]
@patch("src.controllers.health_controller.get_ollama_client")
def test_full_health_handles_health_check_exception(self, mock_get_ollama, client):
@patch("src.shared.database.Database.health_check")
@patch("src.models.ollama_client.get_ollama_client")
def test_full_health_handles_health_check_exception(self, mock_get_ollama, mock_db_health, client):
"""Full health should handle health check exceptions gracefully."""
mock_client = AsyncMock()
# Return False instead of raising exception to test unhealthy path
mock_client.health_check.return_value = False
mock_get_ollama.return_value = mock_client
mock_db_health.return_value = True
response = client.get("/health/full")
# Should return 503 for unhealthy
@@ -184,7 +145,7 @@ class TestFullHealthCheck:
class TestDiagnosticsEndpoint:
"""Test /health/diagnostics endpoint."""
@patch("src.controllers.health_controller.get_ollama_client")
@patch("src.models.ollama_client.get_ollama_client")
def test_diagnostics_returns_200(self, mock_get_ollama, client):
"""Diagnostics should return 200."""
mock_client = AsyncMock()
@@ -194,7 +155,7 @@ class TestDiagnosticsEndpoint:
response = client.get("/health/diagnostics")
assert response.status_code == 200
@patch("src.controllers.health_controller.get_ollama_client")
@patch("src.models.ollama_client.get_ollama_client")
def test_diagnostics_returns_service_info(self, mock_get_ollama, client):
"""Diagnostics should return service information."""
mock_client = AsyncMock()
@@ -208,7 +169,7 @@ class TestDiagnosticsEndpoint:
assert "name" in data["service"]
assert "version" in data["service"]
@patch("src.controllers.health_controller.get_ollama_client")
@patch("src.models.ollama_client.get_ollama_client")
def test_diagnostics_returns_components(self, mock_get_ollama, client):
"""Diagnostics should return component details."""
mock_client = AsyncMock()
@@ -220,10 +181,8 @@ class TestDiagnosticsEndpoint:
assert "components" in data
assert "ollama" in data["components"]
assert "agent" in data["components"]
assert "qdrant" in data["components"]
@patch("src.controllers.health_controller.get_ollama_client")
@patch("src.models.ollama_client.get_ollama_client")
def test_diagnostics_returns_configuration(self, mock_get_ollama, client):
"""Diagnostics should return configuration info."""
mock_client = AsyncMock()
@@ -235,7 +194,7 @@ class TestDiagnosticsEndpoint:
assert "configuration" in data
@patch("src.controllers.health_controller.get_ollama_client")
@patch("src.models.ollama_client.get_ollama_client")
def test_diagnostics_returns_response_time(self, mock_get_ollama, client):
"""Diagnostics should return response time."""
mock_client = AsyncMock()
@@ -248,7 +207,7 @@ class TestDiagnosticsEndpoint:
assert "response_time_ms" in data
assert isinstance(data["response_time_ms"], int)
@patch("src.controllers.health_controller.get_ollama_client")
@patch("src.models.ollama_client.get_ollama_client")
def test_diagnostics_handles_ollama_error(self, mock_get_ollama, client):
"""Diagnostics should handle Ollama connection errors."""
mock_client = AsyncMock()