# backend/tests/test_models.py # Tests for Pydantic request/response models. # These run instantly — no network, no mocks needed. import pytest from pydantic import ValidationError from app.models.chat import ChatRequest, SourceRef, ChatResponse VALID_UUID = "a1b2c3d4-e5f6-4789-8abc-def012345678" class TestChatRequest: def test_valid_request(self): req = ChatRequest(message="What is TextOps?", session_id=VALID_UUID) assert req.message == "What is TextOps?" assert req.session_id == VALID_UUID def test_message_empty_rejected(self): with pytest.raises(ValidationError) as exc_info: ChatRequest(message="", session_id=VALID_UUID) assert "min_length" in str(exc_info.value).lower() or "1" in str(exc_info.value) def test_message_too_long_rejected(self): with pytest.raises(ValidationError): ChatRequest(message="x" * 501, session_id=VALID_UUID) def test_message_at_max_length_allowed(self): req = ChatRequest(message="x" * 500, session_id=VALID_UUID) assert len(req.message) == 500 def test_invalid_session_id_rejected(self): # Contains spaces and special chars not in [a-zA-Z0-9_-] with pytest.raises(ValidationError): ChatRequest(message="hello", session_id="invalid id with spaces!") def test_session_id_with_special_chars_rejected(self): # @ and # are not in the allowed set [a-zA-Z0-9_-] with pytest.raises(ValidationError): ChatRequest(message="hello", session_id="bad@session#id") def test_missing_message_rejected(self): with pytest.raises(ValidationError): ChatRequest(session_id=VALID_UUID) def test_missing_session_id_rejected(self): with pytest.raises(ValidationError): ChatRequest(message="hello") def test_message_whitespace_only_rejected(self): # An all-whitespace string has length > 0 so passes min_length, # but we want to document the current behaviour explicitly. # If the model adds a strip validator later this test should be updated. req = ChatRequest(message=" ", session_id=VALID_UUID) assert req.message == " " class TestSourceRef: def test_valid_source(self): src = SourceRef( title="TextOps", url="https://darshanchheda.com/projects/textops", section="Overview", ) assert src.title == "TextOps" def test_missing_field_rejected(self): with pytest.raises(ValidationError): SourceRef(title="TextOps", url="https://example.com") class TestChatResponse: def test_valid_response(self): resp = ChatResponse( answer="TextOps is a Go-based NLP gateway.", sources=[ SourceRef( title="TextOps", url="https://darshanchheda.com/projects/textops", section="Overview", ) ], cached=False, latency_ms=312, ) assert resp.cached is False assert resp.latency_ms == 312 assert len(resp.sources) == 1