| import pytest
|
| from fastapi.testclient import TestClient
|
| from unittest.mock import patch, Mock
|
| from constants import MAX_COMMENT_LENGTH
|
| from main import app
|
|
|
| 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"}
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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": " "}
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| 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
|
|
|
|
|
| mock_add_task.assert_called_once()
|
|
|
|
|
| call_args = mock_add_task.call_args
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
| @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)
|
|
|
|
|
| responses = []
|
| for i in range(21):
|
| response = rate_limit_client.post("/comment", json=valid_payload)
|
| responses.append(response)
|
|
|
|
|
| assert responses[-1].status_code == 429
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
| assert response.status_code == 422
|
|
|