feat: add dashboard API with quick links and widgets
Build and Push / build (release) Successful in 1m28s
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:
co-authored by
Claude Opus 4.5
parent
e85c9a123d
commit
381d43b60b
@@ -0,0 +1,199 @@
|
||||
"""Tests for dashboard endpoints registration and OpenAPI spec."""
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from src.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
"""Create a test client."""
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
class TestDashboardOpenAPISpec:
|
||||
"""Test that dashboard endpoints are documented in OpenAPI spec."""
|
||||
|
||||
def test_quick_links_list_in_openapi(self, client):
|
||||
"""Quick links list endpoint should be in OpenAPI spec."""
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
spec = response.json()
|
||||
assert "/dashboard/quick-links" in spec["paths"]
|
||||
|
||||
def test_quick_links_get_in_openapi(self, client):
|
||||
"""Quick links get endpoint should be in OpenAPI spec."""
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
spec = response.json()
|
||||
assert "/dashboard/quick-links/{link_id}" in spec["paths"]
|
||||
|
||||
def test_quick_links_reorder_in_openapi(self, client):
|
||||
"""Quick links reorder endpoint should be in OpenAPI spec."""
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
spec = response.json()
|
||||
assert "/dashboard/quick-links/reorder" in spec["paths"]
|
||||
|
||||
def test_widgets_list_in_openapi(self, client):
|
||||
"""Widgets list endpoint should be in OpenAPI spec."""
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
spec = response.json()
|
||||
assert "/dashboard/widgets" in spec["paths"]
|
||||
|
||||
def test_widgets_get_in_openapi(self, client):
|
||||
"""Widgets get endpoint should be in OpenAPI spec."""
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
spec = response.json()
|
||||
assert "/dashboard/widgets/{widget_id}" in spec["paths"]
|
||||
|
||||
def test_quick_links_supports_crud_operations(self, client):
|
||||
"""Quick links should support all CRUD operations."""
|
||||
response = client.get("/openapi.json")
|
||||
spec = response.json()
|
||||
|
||||
# List endpoint
|
||||
list_path = spec["paths"].get("/dashboard/quick-links", {})
|
||||
assert "get" in list_path # List
|
||||
assert "post" in list_path # Create
|
||||
|
||||
# Item endpoint
|
||||
item_path = spec["paths"].get("/dashboard/quick-links/{link_id}", {})
|
||||
assert "get" in item_path # Read
|
||||
assert "put" in item_path # Update
|
||||
assert "delete" in item_path # Delete
|
||||
|
||||
def test_widgets_supports_crud_operations(self, client):
|
||||
"""Widgets should support all CRUD operations."""
|
||||
response = client.get("/openapi.json")
|
||||
spec = response.json()
|
||||
|
||||
# List endpoint
|
||||
list_path = spec["paths"].get("/dashboard/widgets", {})
|
||||
assert "get" in list_path # List
|
||||
assert "post" in list_path # Create
|
||||
|
||||
# Item endpoint
|
||||
item_path = spec["paths"].get("/dashboard/widgets/{widget_id}", {})
|
||||
assert "get" in item_path # Read
|
||||
assert "put" in item_path # Update
|
||||
assert "delete" in item_path # Delete
|
||||
|
||||
|
||||
class TestDashboardSchemaValidation:
|
||||
"""Test that request validation works correctly."""
|
||||
|
||||
def test_create_quick_link_requires_title(self, client):
|
||||
"""Create quick link should require title (422 for validation)."""
|
||||
response = client.post(
|
||||
"/dashboard/quick-links",
|
||||
json={
|
||||
"url": "https://example.com",
|
||||
},
|
||||
)
|
||||
# Either 422 for validation or 401/403/500 for auth
|
||||
assert response.status_code in [401, 403, 422, 500]
|
||||
|
||||
def test_create_widget_requires_widget_type(self, client):
|
||||
"""Create widget should require widget_type (422 for validation)."""
|
||||
response = client.post(
|
||||
"/dashboard/widgets",
|
||||
json={},
|
||||
)
|
||||
assert response.status_code in [401, 403, 422, 500]
|
||||
|
||||
def test_reorder_requires_link_ids(self, client):
|
||||
"""Reorder should require link_ids list (422 for validation)."""
|
||||
response = client.post(
|
||||
"/dashboard/quick-links/reorder",
|
||||
json={},
|
||||
)
|
||||
assert response.status_code in [401, 403, 422, 500]
|
||||
|
||||
|
||||
class TestDashboardControllerInit:
|
||||
"""Test dashboard controller initialization."""
|
||||
|
||||
def test_controller_module_imports(self):
|
||||
"""Dashboard controller should be importable."""
|
||||
from src.domains.dashboard.controller import DashboardController, dashboard_controller
|
||||
assert DashboardController is not None
|
||||
assert dashboard_controller is not None
|
||||
|
||||
def test_controller_has_correct_prefix(self):
|
||||
"""Dashboard controller should have correct prefix."""
|
||||
from src.domains.dashboard.controller import dashboard_controller
|
||||
assert dashboard_controller.prefix == "/dashboard"
|
||||
|
||||
def test_controller_has_correct_tags(self):
|
||||
"""Dashboard controller should have correct tags."""
|
||||
from src.domains.dashboard.controller import dashboard_controller
|
||||
assert "Dashboard" in dashboard_controller.tags
|
||||
|
||||
|
||||
class TestDashboardServiceInit:
|
||||
"""Test dashboard service initialization."""
|
||||
|
||||
def test_service_module_imports(self):
|
||||
"""Dashboard service should be importable."""
|
||||
from src.domains.dashboard.service import DashboardService, get_dashboard_service
|
||||
assert DashboardService is not None
|
||||
assert get_dashboard_service is not None
|
||||
|
||||
def test_service_singleton(self):
|
||||
"""get_dashboard_service should return singleton."""
|
||||
from src.domains.dashboard.service import get_dashboard_service
|
||||
|
||||
service1 = get_dashboard_service()
|
||||
service2 = get_dashboard_service()
|
||||
assert service1 is service2
|
||||
|
||||
|
||||
class TestDashboardModels:
|
||||
"""Test dashboard models."""
|
||||
|
||||
def test_quick_link_model_imports(self):
|
||||
"""QuickLink model should be importable."""
|
||||
from src.domains.dashboard.models import QuickLink
|
||||
assert QuickLink is not None
|
||||
|
||||
def test_dashboard_widget_model_imports(self):
|
||||
"""DashboardWidget model should be importable."""
|
||||
from src.domains.dashboard.models import DashboardWidget
|
||||
assert DashboardWidget is not None
|
||||
|
||||
|
||||
class TestDashboardSchemas:
|
||||
"""Test dashboard schemas."""
|
||||
|
||||
def test_quick_link_schemas_import(self):
|
||||
"""QuickLink schemas should be importable."""
|
||||
from src.domains.dashboard.schemas import (
|
||||
QuickLinkCreate,
|
||||
QuickLinkUpdate,
|
||||
QuickLinkResponse,
|
||||
QuickLinkListResponse,
|
||||
QuickLinkReorderRequest,
|
||||
QuickLinkReorderResponse,
|
||||
)
|
||||
assert QuickLinkCreate is not None
|
||||
assert QuickLinkUpdate is not None
|
||||
assert QuickLinkResponse is not None
|
||||
assert QuickLinkListResponse is not None
|
||||
assert QuickLinkReorderRequest is not None
|
||||
assert QuickLinkReorderResponse is not None
|
||||
|
||||
def test_dashboard_widget_schemas_import(self):
|
||||
"""DashboardWidget schemas should be importable."""
|
||||
from src.domains.dashboard.schemas import (
|
||||
DashboardWidgetCreate,
|
||||
DashboardWidgetUpdate,
|
||||
DashboardWidgetResponse,
|
||||
DashboardWidgetListResponse,
|
||||
)
|
||||
assert DashboardWidgetCreate is not None
|
||||
assert DashboardWidgetUpdate is not None
|
||||
assert DashboardWidgetResponse is not None
|
||||
assert DashboardWidgetListResponse is not None
|
||||
Reference in New Issue
Block a user