""" Tests for Admin Document Routes. """ import pytest from datetime import datetime from io import BytesIO from pathlib import Path from unittest.mock import MagicMock, patch from uuid import UUID from fastapi import HTTPException from fastapi.testclient import TestClient from src.data.admin_models import AdminDocument, AdminToken from src.web.api.v1.admin.documents import _validate_uuid, create_admin_router # Test UUID TEST_DOC_UUID = "550e8400-e29b-41d4-a716-446655440000" TEST_TOKEN = "test-admin-token-12345" class TestValidateUUID: """Tests for UUID validation.""" def test_valid_uuid(self): """Test valid UUID passes validation.""" _validate_uuid(TEST_DOC_UUID, "test") # Should not raise def test_invalid_uuid_raises_400(self): """Test invalid UUID raises 400.""" with pytest.raises(HTTPException) as exc_info: _validate_uuid("not-a-uuid", "document_id") assert exc_info.value.status_code == 400 assert "Invalid document_id format" in exc_info.value.detail class TestAdminRouter: """Tests for admin router creation.""" def test_creates_router_with_endpoints(self): """Test router is created with expected endpoints.""" router = create_admin_router((".pdf", ".png", ".jpg")) # Get route paths (include prefix from router) paths = [route.path for route in router.routes] # Paths include the /admin prefix assert any("/auth/token" in p for p in paths) assert any("/documents" in p for p in paths) assert any("/documents/stats" in p for p in paths) assert any("{document_id}" in p for p in paths) class TestCreateTokenEndpoint: """Tests for POST /admin/auth/token endpoint.""" @pytest.fixture def mock_db(self): """Create mock AdminDB.""" db = MagicMock() db.is_valid_admin_token.return_value = True return db def test_create_token_success(self, mock_db): """Test successful token creation.""" from src.web.schemas.admin import AdminTokenCreate request = AdminTokenCreate(name="Test Token", expires_in_days=30) # The actual endpoint would generate a token # This tests the schema validation assert request.name == "Test Token" assert request.expires_in_days == 30 class TestDocumentUploadEndpoint: """Tests for POST /admin/documents endpoint.""" @pytest.fixture def sample_pdf_bytes(self): """Create sample PDF-like bytes.""" # Minimal PDF header return b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n" @pytest.fixture def mock_admin_db(self): """Create mock AdminDB.""" db = MagicMock() db.is_valid_admin_token.return_value = True db.create_document.return_value = TEST_DOC_UUID return db def test_rejects_invalid_extension(self): """Test that invalid file extensions are rejected.""" # Schema validation would happen at the route level allowed = (".pdf", ".png", ".jpg") file_ext = ".exe" assert file_ext not in allowed class TestDocumentListEndpoint: """Tests for GET /admin/documents endpoint.""" @pytest.fixture def sample_documents(self): """Create sample documents.""" return [ AdminDocument( document_id=UUID(TEST_DOC_UUID), admin_token=TEST_TOKEN, filename="test.pdf", file_size=1024, content_type="application/pdf", file_path="/tmp/test.pdf", page_count=1, status="pending", ), ] def test_validates_status_filter(self): """Test that invalid status filter is rejected.""" valid_statuses = ("pending", "auto_labeling", "labeled", "exported") assert "invalid_status" not in valid_statuses assert "pending" in valid_statuses class TestDocumentDetailEndpoint: """Tests for GET /admin/documents/{document_id} endpoint.""" def test_requires_valid_uuid(self): """Test that invalid UUID is rejected.""" with pytest.raises(HTTPException) as exc_info: _validate_uuid("invalid", "document_id") assert exc_info.value.status_code == 400 class TestDocumentDeleteEndpoint: """Tests for DELETE /admin/documents/{document_id} endpoint.""" def test_validates_document_id(self): """Test that document_id is validated.""" # Valid UUID should not raise _validate_uuid(TEST_DOC_UUID, "document_id") # Invalid should raise with pytest.raises(HTTPException): _validate_uuid("bad-id", "document_id") class TestDocumentStatusUpdateEndpoint: """Tests for PATCH /admin/documents/{document_id}/status endpoint.""" def test_validates_status_values(self): """Test that only valid statuses are accepted.""" valid_statuses = ("pending", "labeled", "exported") assert "pending" in valid_statuses assert "invalid" not in valid_statuses