champ-chatbot / tests /api /test_comment_post.py
qyle's picture
deployment
8b9e569 verified
import pytest
from fastapi.testclient import TestClient
from unittest.mock import patch, Mock
from constants import MAX_COMMENT_LENGTH
from main import app # Adjust import based on your structure
client = TestClient(app)
class TestCommentEndpoint:
"""Test the POST /comment endpoint"""
@pytest.fixture
def base_required_fields(self):
"""Base fields required by IdentifierBase and ProfileBase"""
return {
"user_id": "test-user-123",
"participant_id": "participant-456",
"session_id": "test-session-123",
"consent": True,
"age_group": "25-34",
"gender": "M",
"roles": ["patient"],
}
@pytest.fixture
def valid_payload(self, base_required_fields):
return {**base_required_fields, "comment": "This is a test comment"}
# ==================== Successful Comment Tests ====================
def test_comment_success(self, valid_payload):
"""Test successful comment submission"""
with patch("main.log_event") as mock_log_event:
response = client.post("/comment", json=valid_payload)
assert response.status_code == 200
def test_comment_with_long_text(self, base_required_fields):
"""Test comment with very long text"""
payload = {**base_required_fields, "comment": "xb" * MAX_COMMENT_LENGTH}
response = client.post("/comment", json=payload)
assert response.status_code == 422
def test_comment_with_special_characters(self, base_required_fields):
"""Test comment with special characters and unicode"""
payload = {
**base_required_fields,
"comment": "Test with special chars: @#$%^&*() 你好 🎉\n\tNew line",
}
response = client.post("/comment", json=payload)
assert response.status_code == 200
def test_comment_with_multiline_text(self, base_required_fields):
"""Test comment with multiple lines"""
payload = {**base_required_fields, "comment": "Line 1\nLine 2\nLine 3"}
response = client.post("/comment", json=payload)
assert response.status_code == 200
# ==================== Empty Comment Tests ====================
def test_empty_comment_string(self, base_required_fields):
"""Test that empty string comment returns 400"""
payload = {**base_required_fields, "comment": ""}
response = client.post("/comment", json=payload)
assert response.status_code == 422
def test_whitespace_only_comment(self, base_required_fields):
"""Test comment with only whitespace"""
payload = {**base_required_fields, "comment": " "}
# Depends on how backend validates - might be accepted or rejected
response = client.post("/comment", json=payload)
assert response.status_code == 200
def test_missing_comment_field(self, base_required_fields):
"""Test that missing comment field returns validation error"""
payload = {**base_required_fields}
response = client.post("/comment", json=payload)
assert response.status_code == 422
# ==================== Request Validation Tests ====================
def test_missing_required_profile_fields(self, base_required_fields):
"""Test that missing required fields returns 422"""
payload = {**base_required_fields, "comment": "Test comment"}
del payload["consent"]
response = client.post("/comment", json=payload)
assert response.status_code == 422
def test_invalid_age_group(self, base_required_fields):
"""Test that invalid age group returns 422"""
payload = {
**base_required_fields,
"age_group": "invalid",
"comment": "Test comment",
}
response = client.post("/comment", json=payload)
assert response.status_code == 422
def test_invalid_gender(self, base_required_fields):
"""Test that invalid gender returns 422"""
payload = {**base_required_fields, "gender": "X", "comment": "Test comment"}
response = client.post("/comment", json=payload)
assert response.status_code == 422
def test_invalid_roles(self, base_required_fields):
"""Test that invalid roles return 422"""
payload = {
**base_required_fields,
"roles": ["invalid-role"],
"comment": "Test comment",
}
response = client.post("/comment", json=payload)
assert response.status_code == 422
def test_empty_roles(self, base_required_fields):
"""Test that empty roles set returns 422"""
payload = {**base_required_fields, "roles": [], "comment": "Test comment"}
response = client.post("/comment", json=payload)
assert response.status_code == 422
# ==================== Background Task Tests ====================
def test_background_task_receives_correct_data(self, valid_payload):
"""Test that background task is called with correct data structure"""
with patch("main.BackgroundTasks.add_task") as mock_add_task:
response = client.post("/comment", json=valid_payload)
assert response.status_code == 200
# Verify add_task was called
mock_add_task.assert_called_once()
# Check the arguments passed to add_task
call_args = mock_add_task.call_args
# First arg should be log_event function
# Kwargs should contain the data
assert "user_id" in call_args.kwargs
assert "session_id" in call_args.kwargs
assert "data" in call_args.kwargs
data = call_args.kwargs["data"]
assert data["comment"] == "This is a test comment"
assert data["consent"] == True
assert data["age_group"] == "25-34"
def test_different_comment_contents(self, base_required_fields):
"""Test various comment contents are accepted"""
comments = [
"Short",
"A longer comment with multiple words and punctuation!",
"123456789",
"Mixed 123 content with numbers",
]
for comment_text in comments:
payload = {**base_required_fields, "comment": comment_text}
response = client.post("/comment", json=payload)
assert response.status_code == 200
# ==================== Rate Limiting Tests ====================
@pytest.mark.enable_rate_limit
def test_rate_limiting(self, valid_payload):
"""Test that rate limiting works (20 requests per minute)"""
from fastapi.testclient import TestClient
from main import app
rate_limit_client = TestClient(app)
# Make 21 rapid requests
responses = []
for i in range(21):
response = rate_limit_client.post("/comment", json=valid_payload)
responses.append(response)
# 21st should be rate limited
assert responses[-1].status_code == 429
# ==================== Integration Tests ====================
def test_multiple_comments_same_session(self, base_required_fields):
"""Test submitting multiple comments from same session"""
comments = ["First comment", "Second comment", "Third comment"]
for comment_text in comments:
payload = {**base_required_fields, "comment": comment_text}
response = client.post("/comment", json=payload)
assert response.status_code == 200
def test_comments_from_different_sessions(self, base_required_fields):
"""Test comments from different sessions"""
sessions = ["session-1", "session-2", "session-3"]
for session_id in sessions:
payload = {
**base_required_fields,
"session_id": session_id,
"comment": f"Comment from {session_id}",
}
response = client.post("/comment", json=payload)
assert response.status_code == 200
def test_comment_none_value(self, base_required_fields):
"""Test that null/None comment is handled"""
payload = {**base_required_fields, "comment": None}
response = client.post("/comment", json=payload)
# Should return 400 or 422 depending on validation
assert response.status_code == 422