| """Tests for /rag endpoints (Phase 7)."""
|
|
|
| import io
|
| import pytest
|
| from unittest.mock import patch, AsyncMock
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_documents_empty(client, auth_headers):
|
| resp = await client.get("/api/v1/rag/documents", headers=auth_headers)
|
| assert resp.status_code == 200
|
| data = resp.json()
|
| assert "documents" in data
|
| assert len(data["documents"]) == 0
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_documents_unauthenticated(client):
|
| resp = await client.get("/api/v1/rag/documents")
|
| assert resp.status_code == 403
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_ingest_text_file(client, auth_headers):
|
| content = b"This is a test document for RAG ingestion. It contains enough text to be chunked."
|
| with patch("mac.services.rag_service._store_embeddings", new_callable=AsyncMock, return_value=None):
|
| resp = await client.post(
|
| "/api/v1/rag/ingest",
|
| headers=auth_headers,
|
| files={"file": ("test.txt", io.BytesIO(content), "text/plain")},
|
| data={"title": "Test Document"},
|
| )
|
| assert resp.status_code == 200
|
| data = resp.json()
|
| assert data["title"] == "Test Document"
|
| assert data["status"] in ("processing", "ready")
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_ingest_unsupported_type(client, auth_headers):
|
| content = b"\x00\x01\x02\x03"
|
| resp = await client.post(
|
| "/api/v1/rag/ingest",
|
| headers=auth_headers,
|
| files={"file": ("test.exe", io.BytesIO(content), "application/x-executable")},
|
| data={"title": "Bad File"},
|
| )
|
| assert resp.status_code == 400
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_collections_empty(client, auth_headers):
|
| resp = await client.get("/api/v1/rag/collections", headers=auth_headers)
|
| assert resp.status_code == 200
|
| data = resp.json()
|
| assert "collections" in data
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_create_collection_requires_admin(client, auth_headers):
|
| resp = await client.post("/api/v1/rag/collections", headers=auth_headers,
|
| json={"name": "test", "description": "A test collection"})
|
| assert resp.status_code == 403
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_create_collection_as_admin(client, admin_headers):
|
| resp = await client.post("/api/v1/rag/collections", headers=admin_headers,
|
| json={"name": "test-collection", "description": "A test collection"})
|
| assert resp.status_code == 200
|
| data = resp.json()
|
| assert data["name"] == "test-collection"
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_query(client, auth_headers):
|
| with patch("mac.services.rag_service.query_rag", new_callable=AsyncMock, return_value=[]):
|
| with patch("mac.services.llm_service.chat_completion", new_callable=AsyncMock, return_value={
|
| "id": "mac-chat-test", "object": "chat.completion", "created": 0,
|
| "model": "qwen2.5-coder:7b",
|
| "choices": [{"index": 0, "message": {"role": "assistant", "content": "Test answer"}, "finish_reason": "stop"}],
|
| "usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
|
| "_latency_ms": 100,
|
| }):
|
| resp = await client.post("/api/v1/rag/query", headers=auth_headers,
|
| json={"question": "What is machine learning?"})
|
| assert resp.status_code == 200
|
| data = resp.json()
|
| assert "answer" in data
|
| assert "sources" in data
|
|
|
|
|
| @pytest.mark.asyncio
|
| async def test_rag_delete_requires_admin(client, auth_headers):
|
| resp = await client.delete("/api/v1/rag/documents/some-id", headers=auth_headers)
|
| assert resp.status_code == 403
|
|
|