1a195a96bd
- Merge origin/main into branch (resolve conflict in integrations_dashboard.html) - Add defensive JSON parsing with try/except for integration.config - Wrap tester() call in try/except to prevent 500 errors from bad config - Add i18n key integrations.connection_test_failed_fallback in en.json - Reference i18n key in template JS fallback message - Update SECURITY_AUDIT.md: add fix date (2026-03-23), update doc date - Remove accidental revert.sh file - Fix missing MagicMock/patch imports in test file - Add tests for invalid JSON config and tester exception error paths Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> Agent-Logs-Url: https://github.com/christianlouis/DocuElevate/sessions/daebb70e-059a-4601-8864-88eef49f99cf
129 lines
5.0 KiB
Python
129 lines
5.0 KiB
Python
"""
|
|
Tests to verify that all critical API endpoints are properly registered.
|
|
|
|
This test module serves as a regression prevention mechanism to ensure
|
|
that endpoints remain accessible after code refactoring or reorganization.
|
|
"""
|
|
|
|
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import pytest
|
|
|
|
# Test constants
|
|
TEST_URL = "https://example.com/test.pdf"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestEndpointRegistration:
|
|
"""Verify that critical API endpoints are registered in the FastAPI app"""
|
|
|
|
@patch("app.api.url_upload.httpx.AsyncClient.stream")
|
|
@patch("app.api.url_upload.process_document")
|
|
def test_process_url_endpoint_exists(self, mock_process_document, mock_stream, client):
|
|
"""Verify that /api/process-url endpoint is registered and accessible"""
|
|
# Mock successful download to prevent actual HTTP requests
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.headers = {"Content-Type": "application/pdf", "Content-Length": "1024"}
|
|
|
|
async def mock_aiter_bytes(chunk_size=None):
|
|
yield b"PDF content"
|
|
|
|
mock_response.aiter_bytes = mock_aiter_bytes
|
|
mock_response.raise_for_status = Mock()
|
|
|
|
mock_context = AsyncMock()
|
|
mock_context.__aenter__.return_value = mock_response
|
|
mock_stream.return_value = mock_context
|
|
|
|
# Mock Celery task
|
|
mock_task = Mock()
|
|
mock_task.id = "test-task-id"
|
|
mock_process_document.delay.return_value = mock_task
|
|
|
|
# Make a request to the endpoint - it should not return 404
|
|
response = client.post("/api/process-url", json={"url": TEST_URL})
|
|
|
|
# The endpoint exists if we don't get a 404
|
|
# We may get other errors (401, 400, 500, etc.) due to validation or missing mocks,
|
|
# but 404 specifically means the endpoint is not registered
|
|
assert response.status_code != 404, (
|
|
"Endpoint /api/process-url returned 404 (not found). "
|
|
"This indicates the router is not properly registered in the application. "
|
|
"Verify that url_upload_router is included in app/api/__init__.py"
|
|
)
|
|
|
|
@patch("app.api.url_upload.httpx.AsyncClient.stream")
|
|
@patch("app.api.url_upload.process_document")
|
|
def test_process_url_endpoint_accepts_post(self, mock_process_document, mock_stream, client):
|
|
"""Verify that /api/process-url accepts POST requests"""
|
|
# Mock successful download to prevent actual HTTP requests
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.headers = {"Content-Type": "application/pdf", "Content-Length": "1024"}
|
|
|
|
async def mock_aiter_bytes(chunk_size=None):
|
|
yield b"PDF content"
|
|
|
|
mock_response.aiter_bytes = mock_aiter_bytes
|
|
mock_response.raise_for_status = Mock()
|
|
|
|
mock_context = AsyncMock()
|
|
mock_context.__aenter__.return_value = mock_response
|
|
mock_stream.return_value = mock_context
|
|
|
|
# Mock Celery task
|
|
mock_task = Mock()
|
|
mock_task.id = "test-task-id"
|
|
mock_process_document.delay.return_value = mock_task
|
|
|
|
# Try POST request
|
|
response = client.post("/api/process-url", json={"url": TEST_URL})
|
|
|
|
# Should not return 405 (Method Not Allowed)
|
|
assert response.status_code != 405, (
|
|
"Endpoint /api/process-url returned 405 (Method Not Allowed) for POST. "
|
|
"Verify the endpoint is decorated with @router.post()"
|
|
)
|
|
|
|
@patch("app.api.url_upload.httpx.AsyncClient.stream")
|
|
@patch("app.api.url_upload.process_document")
|
|
def test_api_router_included_in_app(self, mock_process_document, mock_stream, client):
|
|
"""Verify that the main API router is included in the FastAPI app"""
|
|
# Mock successful download for /api/process-url test
|
|
mock_response = AsyncMock()
|
|
mock_response.status_code = 200
|
|
mock_response.headers = {"Content-Type": "application/pdf", "Content-Length": "1024"}
|
|
|
|
async def mock_aiter_bytes(chunk_size=None):
|
|
yield b"PDF content"
|
|
|
|
mock_response.aiter_bytes = mock_aiter_bytes
|
|
mock_response.raise_for_status = Mock()
|
|
|
|
mock_context = AsyncMock()
|
|
mock_context.__aenter__.return_value = mock_response
|
|
mock_stream.return_value = mock_context
|
|
|
|
# Mock Celery task
|
|
mock_task = Mock()
|
|
mock_task.id = "test-task-id"
|
|
mock_process_document.delay.return_value = mock_task
|
|
|
|
# Test a few known API endpoints to ensure the /api prefix works
|
|
endpoints_to_check = [
|
|
("/api/process-url", "post"),
|
|
]
|
|
|
|
for endpoint, method in endpoints_to_check:
|
|
if method == "get":
|
|
response = client.get(endpoint)
|
|
else:
|
|
response = client.post(endpoint, json={"url": TEST_URL})
|
|
|
|
# None of these should return 404
|
|
assert response.status_code != 404, (
|
|
f"Endpoint {endpoint} returned 404. "
|
|
f"Verify that api_router is included in app/main.py with prefix='/api'"
|
|
)
|