aimprabu's picture
fix(ui): gracefully format dictionary error payloads to prevent TypeError
55a76a3
Raw History Blame Contribute Delete
6.24 kB
import io
from app.workflow.state import DocumentChunk
# 1. Test GET /health
def test_health_check(client):
response = client.get("/health")
assert response.status_code == 200
data = response.json()
assert data["status"] == "healthy"
assert data["components"]["vector_store"] == "ok"
assert data["components"]["document_registry"] == "ok"
# 2. Test POST /query
def test_query_assistant(client, test_vector_store, mock_embeddings):
# Add mock chunk to the vector store so retrieval succeeds
mock_chunk = DocumentChunk(
content="FastAPI is a modern web framework.",
source_file="fastapi.md",
document_id="doc_fastapi",
chunk_index=0
)
test_vector_store.add_chunks([mock_chunk], mock_embeddings.embed_documents([mock_chunk.content]))
payload = {
"question": "What is FastAPI?",
"session_id": "12345678-1234-5678-1234-567812345678"
}
response = client.post("/query", json=payload)
assert response.status_code == 200
data = response.json()
assert "answer" in data
assert "sources" in data
assert data["answer"] == "FastAPI is a modern web framework."
def test_query_validation_errors(client):
# Empty query
response = client.post("/query", json={"question": ""})
assert response.status_code == 422 # Pydantic min_length validation raises 422
assert "detail" in response.json() or "error" in response.json()
# Too long query
response = client.post("/query", json={"question": "a" * 2001})
assert response.status_code == 422
assert "detail" in response.json() or "error" in response.json()
# Invalid UUID session_id format
response = client.post("/query", json={"question": "What is FastAPI?", "session_id": "invalid-uuid"})
assert response.status_code == 422
assert "detail" in response.json() or "error" in response.json()
# 3. Test POST /ingest
def test_ingest_document_validation(client):
# Neither URL nor file
# We pass an empty files dict to force multipart encoding
response = client.post("/ingest", files={})
assert response.status_code == 422
assert response.json()["error"]["code"] == "VALIDATION_ERROR"
# Both URL and file
file_data = ("test.txt", io.BytesIO(b"Hello world"))
response = client.post(
"/ingest",
files={
"url": (None, "http://example.com"),
"file": file_data
}
)
assert response.status_code == 422
assert response.json()["error"]["code"] == "VALIDATION_ERROR"
# Invalid URL scheme
response = client.post("/ingest", files={"url": (None, "ftp://example.com")})
assert response.status_code == 422
assert response.json()["error"]["code"] == "VALIDATION_ERROR"
# Invalid file extension
file_data = ("test.exe", io.BytesIO(b"Hello world"))
response = client.post("/ingest", files={"file": file_data})
assert response.status_code == 422
assert response.json()["error"]["code"] == "VALIDATION_ERROR"
def test_ingest_url_success(client):
response = client.post("/ingest", files={"url": (None, "https://fastapi.tiangolo.com/")})
assert response.status_code == 201
data = response.json()
assert "document_id" in data
assert data["status"] == "indexed"
def test_ingest_file_success(client):
file_data = ("sample.md", io.BytesIO(b"# Sample Header\nTest file content."))
response = client.post("/ingest", files={"file": file_data})
assert response.status_code == 201
data = response.json()
assert "document_id" in data
assert data["status"] == "indexed"
# 4. Test GET /documents
def test_list_indexed_documents(client):
response = client.get("/documents")
assert response.status_code == 200
data = response.json()
assert "documents" in data
assert "total" in data
# 5. Test POST /feedback
def test_submit_feedback(client):
feedback_payload = {
"query": "What is FastAPI?",
"answer": "FastAPI is a modern web framework.",
"rating": "thumbs_up",
"comment": "Super helpful!",
"session_id": "12345678-1234-5678-1234-567812345678"
}
response = client.post("/feedback", json=feedback_payload)
assert response.status_code == 200
data = response.json()
assert "feedback_id" in data
assert data["status"] == "recorded"
def test_list_feedback(client):
# Submit one feedback first
feedback_payload = {
"query": "What is FastAPI?",
"answer": "FastAPI is a modern web framework.",
"rating": "thumbs_down",
"comment": "Not what I expected.",
"session_id": "12345678-1234-5678-1234-567812345678"
}
client.post("/feedback", json=feedback_payload)
# Get list
response = client.get("/feedback?rating=thumbs_down")
assert response.status_code == 200
data = response.json()
assert "feedback" in data
assert len(data["feedback"]) > 0
assert data["feedback"][0]["rating"] == "thumbs_down"
# 6. Test DELETE /documents/{id}
def test_delete_document(client):
# Ingest a document first to delete it
file_data = ("todelete.md", io.BytesIO(b"# To Delete\nContent to delete."))
ingest_res = client.post("/ingest", files={"file": file_data})
doc_id = ingest_res.json()["document_id"]
# Delete it
del_res = client.delete(f"/documents/{doc_id}")
assert del_res.status_code == 200
assert del_res.json()["status"] == "deleted"
# 7. Test conversational query
def test_conversational_query(client, mock_llm_client):
# Setup mock LLM responses specifically for this test
mock_llm_client.responses = {
"Classify the query type": '{"rewritten_query": "Hello", "query_type": "conversational"}',
"The user is greeting you": "Hello! I am your Technical Documentation Copilot. How can I help you today?"
}
payload = {
"question": "Hello!",
"session_id": "12345678-1234-5678-1234-567812345678"
}
response = client.post("/query", json=payload)
assert response.status_code == 200
data = response.json()
assert "answer" in data
assert "Hello! I am your Technical Documentation Copilot." in data["answer"]
assert len(data["sources"]) == 0