contextquery-api / tests /test_rag.py
RamshaAnwar's picture
test(eval): add RAGAS gate + retrieval evaluation (recall@5, MRR) (#16)
49206fb unverified
Raw History Blame Contribute Delete
3.54 kB
import pytest
from uuid import uuid4
from unittest.mock import patch
from app.models import Hub, Document, Chunk
def test_empty_retrieval_returns_ungrounded(client, db_session):
client.post("/api/v1/auth/signup", json={"email": "rag@test.com", "password": "Password123"})
token = client.post("/api/v1/auth/login", json={"email": "rag@test.com", "password": "Password123"}).json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
hub_res = client.post("/api/v1/hubs", headers={**headers, "X-Idempotency-Key": str(uuid4())}, json={"name": "Empty Hub"})
hub_id = hub_res.json()["id"]
with patch("app.routers.hubs.embed_texts", return_value=[[0.1]*768]), \
patch("app.routers.hubs.answer_question") as mock_answer:
res = client.post(f"/api/v1/hubs/{hub_id}/query", headers=headers, json={"question": "What is the policy?"})
assert res.status_code == 200
assert res.json()["grounded"] is False
mock_answer.assert_not_called()
def test_vector_search_is_scoped_to_owner(client, db_session):
client.post("/api/v1/auth/signup", json={"email": "usera@rag.com", "password": "Password123"})
token_a = client.post("/api/v1/auth/login", json={"email": "usera@rag.com", "password": "Password123"}).json()["access_token"]
client.post("/api/v1/auth/signup", json={"email": "userb@rag.com", "password": "Password123"})
token_b = client.post("/api/v1/auth/login", json={"email": "userb@rag.com", "password": "Password123"}).json()["access_token"]
hub_a = client.post("/api/v1/hubs", headers={"Authorization": f"Bearer {token_a}", "X-Idempotency-Key": str(uuid4())}, json={"name": "A's Secret Hub"}).json()
from app.models import User, Hub
user_a = db_session.query(User).filter(User.email == "usera@rag.com").first()
hub = db_session.query(Hub).filter(Hub.id == hub_a["id"]).first()
doc = Document(id=uuid4(), hub_id=hub.id, owner_id=user_a.id, origin="upload", filename="a.txt", mime_type="text/plain", size_bytes=10, checksum_sha256="0"*64, status="ready")
db_session.add(doc)
db_session.commit()
chunk = Chunk(id=uuid4(), document_id=doc.id, hub_id=hub.id, owner_id=user_a.id, chunk_index=0, text="The secret is 42.", token_count=5, embedding=[0.1]*768)
db_session.add(chunk)
db_session.commit()
with patch("app.routers.hubs.embed_texts", return_value=[[0.1]*768]):
res = client.post(f"/api/v1/hubs/{hub.id}/query", headers={"Authorization": f"Bearer {token_b}"}, json={"question": "What is the secret?"})
assert res.status_code == 404
def test_expansion_fallback_when_ai_fails(client, db_session):
client.post("/api/v1/auth/signup", json={"email": "fallback@test.com", "password": "Password123"})
token = client.post("/api/v1/auth/login", json={"email": "fallback@test.com", "password": "Password123"}).json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
hub_res = client.post("/api/v1/hubs", headers={**headers, "X-Idempotency-Key": str(uuid4())}, json={"name": "Fallback Hub"})
hub_id = hub_res.json()["id"]
with patch("app.services.groq_service._call", side_effect=Exception("AI Down")), \
patch("app.routers.hubs.embed_texts", return_value=[[0.1]*768]), \
patch("app.routers.hubs.answer_question", return_value={"answer": "Test", "evidence": None, "grounded": False}):
res = client.post(f"/api/v1/hubs/{hub_id}/query", headers=headers, json={"question": "Test question?"})
assert res.status_code == 200