| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- """
- Tests unitarios del endpoint /visit con mocks para no depender de Camoufox.
- Importamos app.main dentro del contexto del patch para asegurar que todos
- los módulos se carguen con AsyncCamoufox mockeado.
- """
- from contextlib import contextmanager
- from unittest.mock import AsyncMock, patch
- @contextmanager
- def mocked_client(mock_response=None):
- """Context manager que crea un TestClient con AsyncCamoufox mockeado.
- Importa app.main dentro del contexto del patch."""
- if mock_response is None:
- mock_response = {"html": "<html>Example</html>", "screenshot": None}
- with patch("app.use_cases.camoufox.wrapper.AsyncCamoufox") as MockClass:
- mock_browser = AsyncMock()
- mock_page = AsyncMock()
- mock_page.content = AsyncMock(return_value=mock_response["html"])
- mock_page.screenshot = AsyncMock(
- return_value=b"fake_screenshot_bytes" if mock_response.get("screenshot") else None
- )
- mock_page.set_extra_http_headers = AsyncMock()
- mock_page.goto = AsyncMock()
- mock_page.wait_for_load_state = AsyncMock()
- mock_page.set_default_navigation_timeout = AsyncMock()
- mock_browser.new_page = AsyncMock(return_value=mock_page)
- MockClass.return_value.__aenter__ = AsyncMock(return_value=mock_browser)
- MockClass.return_value.__aexit__ = AsyncMock(return_value=False)
- # Importar app DENTRO del patch para que se cargue con AsyncCamoufox mockeado
- from app.main import app
- from fastapi.testclient import TestClient
- yield TestClient(app)
- def test_visit_missing_url():
- from app.main import app
- from fastapi.testclient import TestClient
- client = TestClient(app)
- response = client.post("/api/v1/visit", json={})
- assert response.status_code == 422
- class TestVisitEndpoint:
- """Tests que mockean AsyncCamoufox para evitar llamadas reales al browser."""
- def test_visit_invalid_url(self):
- with mocked_client() as client:
- response = client.post("/api/v1/visit", json={"url": "not-a-url"})
- assert response.status_code == 422
- def test_visit_valid_url(self):
- with mocked_client() as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "screenshot": False},
- )
- assert response.status_code == 200
- body = response.json()
- assert body["status"] == "OK"
- assert "html" in body["data"]
- def test_visit_with_screenshot(self):
- with mocked_client(
- {"html": "<html>Example</html>", "screenshot": "base64data"}
- ) as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "screenshot": True},
- )
- assert response.status_code == 200
- body = response.json()
- assert body["status"] == "OK"
- assert "screenshot" in body["data"]
- def test_visit_with_invalid_waituntil(self):
- with mocked_client() as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "waitUntil": "invalid"},
- )
- assert response.status_code == 422
- def test_visit_with_valid_waituntil(self):
- with mocked_client() as client:
- for wait_until in ["load", "domcontentloaded", "networkidle", "commit"]:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "waitUntil": wait_until},
- )
- assert response.status_code == 200
- def test_visit_response_format(self):
- with mocked_client() as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com"},
- )
- body = response.json()
- assert "status" in body
- assert "message" in body
- assert "data" in body
- assert "code" in body
- assert body["code"] == "UNKNOWN_CODE"
- def test_visit_with_wait_time_valid(self):
- with mocked_client() as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "waitTime": 10},
- )
- assert response.status_code == 200
- def test_visit_with_wait_time_invalid_negative(self):
- with mocked_client() as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "waitTime": -5},
- )
- assert response.status_code == 422
- def test_visit_with_wait_time_invalid_above_max(self):
- with mocked_client() as client:
- response = client.post(
- "/api/v1/visit",
- json={"url": "https://example.com", "waitTime": 120},
- )
- assert response.status_code == 422
- class TestVisitEndpointController:
- """Tests que verifican el controller directamente con VisitUseCase mockeado."""
- def test_controller_visit_uses_visit_use_case(self):
- """Test que cubre la llamada a use_case.execute."""
- from app.controllers.api_controller import ApiController
- from app.models.schemas import VisitRequest
- from fastapi import Request
- from unittest.mock import MagicMock
- controller = ApiController()
- mock_request = MagicMock(spec=Request)
- mock_request.scope = {"raw_headers_dict": {"user-agent": "test"}}
- with patch.object(controller, "visit_use_case") as mock_use_case:
- async def async_execute(*args, **kwargs):
- return {"html": "<html>Controller</html>", "screenshot": None}
- mock_use_case.execute = async_execute
- import asyncio
- result = asyncio.run(
- controller.visit(mock_request, VisitRequest(url="https://example.com"))
- )
- assert result["status"] == "OK"
- assert result["data"]["html"] == "<html>Controller</html>"
- def test_controller_visit_error_handling(self):
- """Test que cubre el manejo de errores."""
- from app.controllers.api_controller import ApiController
- from app.models.schemas import VisitRequest
- from fastapi import Request
- from unittest.mock import MagicMock
- controller = ApiController()
- mock_request = MagicMock(spec=Request)
- mock_request.scope = {"raw_headers_dict": {"user-agent": "test"}}
- with patch.object(controller, "visit_use_case") as mock_use_case:
- async def async_execute(*args, **kwargs):
- raise Exception("Test error")
- mock_use_case.execute = async_execute
- import asyncio
- result = asyncio.run(
- controller.visit(mock_request, VisitRequest(url="https://example.com"))
- )
- assert result["status"] == "NO_OK"
- assert "Test error" in result["message"]
|