Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/app/api/v1/endpoints/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ def get_assignments(course_id: str, request: Request) -> Optional[List[Dict[str,
return None
except requests.RequestException: # type: ignore
return None
except Exception:
return None


def get_sessions(page: int, request: Request) -> Optional[List[Dict[str, Any]]]: # type: ignore
Expand Down
104 changes: 104 additions & 0 deletions tests/unit/test_get_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import uuid
from types import SimpleNamespace

import pytest

import app.api.v1.endpoints.chat as chat_module # Ajusta si tu ruta es distinta


@pytest.fixture(autouse=True)
def patch_jwt_and_requests(monkeypatch):
"""
- Stub para jw_auth_middleware que siempre devuelve un UUID fijo.
- Sustitución de chat_module.requests por un objeto fake que define:
• get(url, timeout, headers) -> retorna lo que configuremos.
• HTTPError: excepción para simular errores de status.
"""
fake_id = uuid.UUID("12345678-1234-5678-1234-567812345678")
monkeypatch.setattr(chat_module, "jw_auth_middleware", lambda request=None: fake_id)

# Creamos un módulo fake para requests
class FakeRequestsModule:
class HTTPError(Exception):
pass

def __init__(self):
# Lo que devolverá get() en cada test
self._response = None

def configure_response(self, resp):
self._response = resp

def get(self, url, timeout, headers):
if isinstance(self._response, Exception):
raise self._response
return self._response

fake_requests = FakeRequestsModule()
monkeypatch.setattr(chat_module, "requests", fake_requests)

yield fake_requests # lo retornamos para que cada test lo configure


def make_req(token: str) -> SimpleNamespace:
"""Helper para crear un objeto con .headers."""
return SimpleNamespace(headers={"Authorization": token})


def test_get_profile_success(patch_jwt_and_requests):
# Configuramos una respuesta fake con .raise_for_status() OK y .json() con data
class DummyResp:
def __init__(self, payload):
self._payload = payload

def raise_for_status(self):
pass # simula 200 OK

def json(self):
return {"data": self._payload}

profile_data = {
"name": "Martín",
"email": "martin@example.com",
"education": "Eng.",
"bio": "Dev",
"location": "Argentina",
}
patch_jwt_and_requests.configure_response(DummyResp(profile_data))

req = make_req("Bearer tok")
out = chat_module.get_profile(req)

assert isinstance(out, dict)
assert out["id:"] == "12345678-1234-5678-1234-567812345678"
for k, v in profile_data.items():
assert out[k] == v


def test_get_profile_http_error(patch_jwt_and_requests):
# Simula que .raise_for_status() lanza HTTPError
class ErrResp:
def raise_for_status(self):
raise patch_jwt_and_requests.HTTPError("404")

def json(self):
return {}

patch_jwt_and_requests.configure_response(ErrResp())
req = make_req("Bearer bad")
assert chat_module.get_profile(req) is None


def test_get_profile_missing_data_key(patch_jwt_and_requests):
# Respuesta OK, pero .json() no trae "data"
class NoDataResp:
def raise_for_status(self):
pass

def json(self):
return {} # sin "data"

patch_jwt_and_requests.configure_response(NoDataResp())
req = make_req("Bearer y")
# Al hacer data = None, luego data.get -> AttributeError, cae a except y retorna None
assert chat_module.get_profile(req) is None
115 changes: 115 additions & 0 deletions tests/unit/test_get_sessions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import pytest
from requests import RequestException # type: ignore

import app.api.v1.endpoints.chat as chat_module


# Helper to create a dummy Request with headers
class DummyRequest:
def __init__(self, token=None):
self.headers = {}
if token:
self.headers["Authorization"] = token


@pytest.fixture(autouse=True)
def set_env(monkeypatch):
# Ensure BASE_API_URL is set to a dummy
monkeypatch.setenv("BASE_API_URL", "http://example.com")
yield


# Tests for get_assignments
class FakeResponse:
def __init__(self, json_data, status_code=200):
self._json = json_data
self.status_code = status_code

def raise_for_status(self):
if self.status_code >= 400:
raise RequestException(f"Status {self.status_code}")

def json(self):
return self._json


def test_get_assignments_success(monkeypatch):
# Mock requests.get to return a list of assignments
fake_data = [
{"title": "A1", "instructions": "Do A1", "deadline": "2025-07-01"},
{"title": "A2", "instructions": "Do A2", "deadline": "2025-07-02"},
]

def fake_get(url, params, timeout, headers):
return FakeResponse(fake_data, status_code=200)

monkeypatch.setattr(chat_module.requests, "get", fake_get) # type: ignore
req = DummyRequest(token="Bearer tok")
result = chat_module.get_assignments("course1", req)
assert result == fake_data


def test_get_assignments_http_error(monkeypatch):
def fake_get(url, params, timeout, headers):
raise RequestException("Network error")

monkeypatch.setattr(chat_module.requests, "get", fake_get) # type: ignore
req = DummyRequest(token="Bearer tok")
assert chat_module.get_assignments("course1", req) is None


def test_get_assignments_invalid_format(monkeypatch):
# Return a dict instead of list
def fake_get(url, params, timeout, headers):
return FakeResponse({"not": "a list"}, status_code=200)

monkeypatch.setattr(chat_module.requests, "get", fake_get) # type: ignore
req = DummyRequest(token="Bearer tok")
assert chat_module.get_assignments("course1", req) is None


# Tests for get_sessions


def test_get_sessions_success(monkeypatch):
# Mock the session list response
session_data = {
"data": [{"courses_id": "course1", "title": "Session1", "description": "Desc1"}]
}

def fake_get_sessions(url, params, timeout, headers):
return FakeResponse(session_data, status_code=200)

# Patch requests.get and get_assignments
monkeypatch.setenv("BASE_API_URL", "http://example.com")
monkeypatch.setattr(chat_module.requests, "get", fake_get_sessions) # type: ignore
# Patch assignments retrieval
monkeypatch.setattr(
chat_module, "get_assignments", lambda cid, req: [{"dummy": True}]
)

req = DummyRequest(token="Bearer tok")
result = chat_module.get_sessions(1, req)
assert isinstance(result, list)
assert result == [
{"title": "Session1", "description": "Desc1", "assignments": [{"dummy": True}]}
]


def test_get_sessions_request_error(monkeypatch):
def fake_get_error(url, params, timeout, headers):
raise RequestException("Fail")

monkeypatch.setattr(chat_module.requests, "get", fake_get_error) # type: ignore
req = DummyRequest(token="Bearer tok")
assert chat_module.get_sessions(1, req) is None


def test_get_sessions_unexpected_error(monkeypatch):
# Return malformed data to trigger exception
def fake_get_malformed(url, params, timeout, headers):
return FakeResponse({"no_data": []}, status_code=200)

monkeypatch.setattr(chat_module.requests, "get", fake_get_malformed) # type: ignore
req = DummyRequest(token="Bearer tok")
assert chat_module.get_sessions(1, req) is None