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
98 changes: 98 additions & 0 deletions tests/unit/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import json
from types import SimpleNamespace

import pytest

import app.core.gemini_client as gc_module


# Fake DB que nos permite controlar get_best_passage
class FakeDB:
def __init__(self, return_value):
self._return = return_value

def get_best_passage(self, query_vec_str):
# comprobamos que nos llega un JSON serializado de la lista
_ = json.loads(query_vec_str)
return self._return


# Fake client.models.embed_content
class FakeEmbedResult:
def __init__(self, embeddings):
# embeddings: lista de objetos con atributo values
self.embeddings = [SimpleNamespace(values=vals) for vals in embeddings]


class FakeModels:
def __init__(self, embed_return):
self._embed_return = embed_return

def embed_content(self, model, contents, config):
return FakeEmbedResult(self._embed_return)


class FakeClient:
def __init__(self, embed_return):
self.models = FakeModels(embed_return)


@pytest.fixture(autouse=True)
def reset_module_state(monkeypatch):
"""
Antes de cada test, reseteamos el client y DB en gemini_client
a valores por defecto.
"""
# Referencias originales
monkeypatch.setenv("DATABASE_URL", "postgres://fake")
# Quitamos cualquier client anterior
monkeypatch.setattr(gc_module, "client", None)
yield
# Al final, restaurar client a None
monkeypatch.setattr(gc_module, "client", None)


def test_no_client_returns_empty():
# client es None por fixture
assert gc_module.find_best_passage("hola") == ""


def test_empty_embeddings_return_empty(monkeypatch):
# client existe, pero devuelve embeddings vacíos o sin valores
fake = FakeClient(embed_return=[[]])
monkeypatch.setattr(gc_module, "client", fake)

# embed devuelve lista con un objeto cuyas values es []
assert gc_module.find_best_passage("query") == ""


def test_db_returns_empty(monkeypatch):
# cliente válido y embedding no vacío
fake = FakeClient(embed_return=[[0.1, 0.2, 0.3]])
monkeypatch.setattr(gc_module, "client", fake)
# get_best_passage devuelve cadena vacía
monkeypatch.setattr(gc_module, "DB", lambda *args, **kw: FakeDB(""))

assert gc_module.find_best_passage("otra") == ""


def test_below_threshold_returns_idx(monkeypatch):
fake = FakeClient(embed_return=[[1, 2, 3]])
monkeypatch.setattr(gc_module, "client", fake)
# get_best_passage devuelve [idx, sim<0.7]
monkeypatch.setattr(gc_module, "DB", lambda *args, **kw: FakeDB([42, 0.5]))

result = gc_module.find_best_passage("texto")
# sim=0.5<0.7, debe devolver el índice
assert result == 42


def test_above_threshold_returns_empty(monkeypatch):
fake = FakeClient(embed_return=[[1, 2, 3]])
monkeypatch.setattr(gc_module, "client", fake)
# get_best_passage devuelve [idx, sim>=0.7]
monkeypatch.setattr(gc_module, "DB", lambda *args, **kw: FakeDB([99, 0.75]))

result = gc_module.find_best_passage("texto")
# sim=0.75>=0.7, debe devolver cadena vacía
assert result == ""
19 changes: 19 additions & 0 deletions tests/unit/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,22 @@ class CustomError(Exception):
)
# A pesar de la excepción, el finally debe devolver la conexión
assert db.pool.putconn.called


def test_get_answers_with_votes_on_error_returns_empty(monkeypatch):
"""
Simula que execute() lanza una excepción y comprueba
que la función atrapa el error y devuelve [].
"""
db = DB()
fake_conn = db.pool.getconn()
fake_cursor = fake_conn.cursor_obj

# Hacemos que execute() falle
fake_cursor.execute.side_effect = Exception("fail query")

result = db.get_answers_with_votes(page_size=5, offset=0)
assert result == [], "En caso de excepción debe devolver lista vacía"

# La conexión se recupera en el finally
assert db.pool.putconn.called, "No se devolvió conexión al pool tras el error"