401 lines
13 KiB
Python
401 lines
13 KiB
Python
"""
|
|
Dashboard API Integration Tests
|
|
|
|
Tests Dashboard API endpoints with real database operations via TestClient.
|
|
"""
|
|
|
|
from datetime import datetime, timezone
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from backend.data.admin_models import (
|
|
AdminAnnotation,
|
|
AdminDocument,
|
|
AdminToken,
|
|
AnnotationHistory,
|
|
ModelVersion,
|
|
TrainingDataset,
|
|
TrainingTask,
|
|
)
|
|
from backend.web.api.v1.admin.dashboard import create_dashboard_router
|
|
from backend.web.core.auth import validate_admin_token
|
|
|
|
|
|
def create_test_app(override_token_dep):
|
|
"""Create a FastAPI test application with dashboard router."""
|
|
app = FastAPI()
|
|
router = create_dashboard_router()
|
|
app.include_router(router)
|
|
|
|
# Override auth dependency
|
|
app.dependency_overrides[validate_admin_token] = lambda: override_token_dep
|
|
|
|
return app
|
|
|
|
|
|
class TestDashboardStatsEndpoint:
|
|
"""Tests for GET /admin/dashboard/stats endpoint."""
|
|
|
|
def test_stats_empty_database(self, patched_session, admin_token):
|
|
"""Test stats endpoint with empty database."""
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/stats")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_documents"] == 0
|
|
assert data["annotation_complete"] == 0
|
|
assert data["annotation_incomplete"] == 0
|
|
assert data["pending"] == 0
|
|
assert data["completeness_rate"] == 0.0
|
|
|
|
def test_stats_with_pending_documents(self, patched_session, admin_token):
|
|
"""Test stats with pending documents."""
|
|
session = patched_session
|
|
|
|
# Create pending documents
|
|
for i in range(3):
|
|
doc = AdminDocument(
|
|
document_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
filename=f"pending_{i}.pdf",
|
|
file_size=1024,
|
|
content_type="application/pdf",
|
|
file_path=f"/uploads/pending_{i}.pdf",
|
|
page_count=1,
|
|
status="pending",
|
|
upload_source="ui",
|
|
category="invoice",
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(doc)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/stats")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_documents"] == 3
|
|
assert data["pending"] == 3
|
|
|
|
def test_stats_with_complete_annotations(self, patched_session, admin_token):
|
|
"""Test stats with complete annotations."""
|
|
session = patched_session
|
|
|
|
# Create labeled document with complete annotations
|
|
doc = AdminDocument(
|
|
document_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
filename="complete.pdf",
|
|
file_size=1024,
|
|
content_type="application/pdf",
|
|
file_path="/uploads/complete.pdf",
|
|
page_count=1,
|
|
status="labeled",
|
|
upload_source="ui",
|
|
category="invoice",
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(doc)
|
|
session.commit()
|
|
|
|
# Add identifier and payment annotations
|
|
session.add(AdminAnnotation(
|
|
annotation_id=uuid4(),
|
|
document_id=doc.document_id,
|
|
page_number=1,
|
|
class_id=0, # invoice_number
|
|
class_name="invoice_number",
|
|
x_center=0.5, y_center=0.1, width=0.2, height=0.05,
|
|
bbox_x=400, bbox_y=80, bbox_width=160, bbox_height=40,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
))
|
|
session.add(AdminAnnotation(
|
|
annotation_id=uuid4(),
|
|
document_id=doc.document_id,
|
|
page_number=1,
|
|
class_id=4, # bankgiro
|
|
class_name="bankgiro",
|
|
x_center=0.5, y_center=0.2, width=0.2, height=0.05,
|
|
bbox_x=400, bbox_y=160, bbox_width=160, bbox_height=40,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
))
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/stats")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["annotation_complete"] == 1
|
|
assert data["completeness_rate"] == 100.0
|
|
|
|
|
|
class TestActiveModelEndpoint:
|
|
"""Tests for GET /admin/dashboard/active-model endpoint."""
|
|
|
|
def test_active_model_none(self, patched_session, admin_token):
|
|
"""Test active-model endpoint with no active model."""
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/active-model")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["model"] is None
|
|
assert data["running_training"] is None
|
|
|
|
def test_active_model_with_model(self, patched_session, admin_token, sample_dataset):
|
|
"""Test active-model endpoint with active model."""
|
|
session = patched_session
|
|
|
|
# Create training task
|
|
task = TrainingTask(
|
|
task_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
name="Test Task",
|
|
status="completed",
|
|
task_type="train",
|
|
dataset_id=sample_dataset.dataset_id,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
# Create active model
|
|
model = ModelVersion(
|
|
version_id=uuid4(),
|
|
version="1.0.0",
|
|
name="Test Model",
|
|
model_path="/models/test.pt",
|
|
status="active",
|
|
is_active=True,
|
|
task_id=task.task_id,
|
|
dataset_id=sample_dataset.dataset_id,
|
|
metrics_mAP=0.90,
|
|
metrics_precision=0.88,
|
|
metrics_recall=0.85,
|
|
document_count=100,
|
|
file_size=50000000,
|
|
activated_at=datetime.now(timezone.utc),
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(model)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/active-model")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["model"] is not None
|
|
assert data["model"]["version"] == "1.0.0"
|
|
assert data["model"]["name"] == "Test Model"
|
|
assert data["model"]["metrics_mAP"] == 0.90
|
|
|
|
def test_active_model_with_running_training(self, patched_session, admin_token, sample_dataset):
|
|
"""Test active-model endpoint with running training."""
|
|
session = patched_session
|
|
|
|
# Create running training task
|
|
task = TrainingTask(
|
|
task_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
name="Running Task",
|
|
status="running",
|
|
task_type="train",
|
|
dataset_id=sample_dataset.dataset_id,
|
|
started_at=datetime.now(timezone.utc),
|
|
progress=50,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/active-model")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["running_training"] is not None
|
|
assert data["running_training"]["name"] == "Running Task"
|
|
assert data["running_training"]["status"] == "running"
|
|
assert data["running_training"]["progress"] == 50
|
|
|
|
|
|
class TestRecentActivityEndpoint:
|
|
"""Tests for GET /admin/dashboard/activity endpoint."""
|
|
|
|
def test_activity_empty(self, patched_session, admin_token):
|
|
"""Test activity endpoint with no activities."""
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/activity")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["activities"] == []
|
|
|
|
def test_activity_with_uploads(self, patched_session, admin_token):
|
|
"""Test activity includes document uploads."""
|
|
session = patched_session
|
|
|
|
# Create documents
|
|
for i in range(3):
|
|
doc = AdminDocument(
|
|
document_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
filename=f"activity_{i}.pdf",
|
|
file_size=1024,
|
|
content_type="application/pdf",
|
|
file_path=f"/uploads/activity_{i}.pdf",
|
|
page_count=1,
|
|
status="pending",
|
|
upload_source="ui",
|
|
category="invoice",
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(doc)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/activity")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
upload_activities = [a for a in data["activities"] if a["type"] == "document_uploaded"]
|
|
assert len(upload_activities) == 3
|
|
|
|
def test_activity_limit_parameter(self, patched_session, admin_token):
|
|
"""Test activity limit parameter."""
|
|
session = patched_session
|
|
|
|
# Create many documents
|
|
for i in range(15):
|
|
doc = AdminDocument(
|
|
document_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
filename=f"limit_{i}.pdf",
|
|
file_size=1024,
|
|
content_type="application/pdf",
|
|
file_path=f"/uploads/limit_{i}.pdf",
|
|
page_count=1,
|
|
status="pending",
|
|
upload_source="ui",
|
|
category="invoice",
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(doc)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/activity?limit=5")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["activities"]) <= 5
|
|
|
|
def test_activity_invalid_limit(self, patched_session, admin_token):
|
|
"""Test activity with invalid limit parameter."""
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
# Limit too high
|
|
response = client.get("/admin/dashboard/activity?limit=100")
|
|
assert response.status_code == 422
|
|
|
|
# Limit too low
|
|
response = client.get("/admin/dashboard/activity?limit=0")
|
|
assert response.status_code == 422
|
|
|
|
def test_activity_with_training_completion(self, patched_session, admin_token, sample_dataset):
|
|
"""Test activity includes training completions."""
|
|
session = patched_session
|
|
|
|
# Create completed training task
|
|
task = TrainingTask(
|
|
task_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
name="Completed Task",
|
|
status="completed",
|
|
task_type="train",
|
|
dataset_id=sample_dataset.dataset_id,
|
|
metrics_mAP=0.95,
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(task)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/activity")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
training_activities = [a for a in data["activities"] if a["type"] == "training_completed"]
|
|
assert len(training_activities) >= 1
|
|
|
|
def test_activity_sorted_by_timestamp(self, patched_session, admin_token):
|
|
"""Test activities are sorted by timestamp descending."""
|
|
session = patched_session
|
|
|
|
# Create documents
|
|
for i in range(5):
|
|
doc = AdminDocument(
|
|
document_id=uuid4(),
|
|
admin_token=admin_token.token,
|
|
filename=f"sorted_{i}.pdf",
|
|
file_size=1024,
|
|
content_type="application/pdf",
|
|
file_path=f"/uploads/sorted_{i}.pdf",
|
|
page_count=1,
|
|
status="pending",
|
|
upload_source="ui",
|
|
category="invoice",
|
|
created_at=datetime.now(timezone.utc),
|
|
updated_at=datetime.now(timezone.utc),
|
|
)
|
|
session.add(doc)
|
|
session.commit()
|
|
|
|
app = create_test_app(admin_token.token)
|
|
client = TestClient(app)
|
|
|
|
response = client.get("/admin/dashboard/activity")
|
|
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
timestamps = [a["timestamp"] for a in data["activities"]]
|
|
assert timestamps == sorted(timestamps, reverse=True)
|