File size: 7,368 Bytes
08367f3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
"""
Unit tests for sentiment analysis API

These tests verify:
- All endpoints work correctly
- Sentiment analysis returns expected results
- Input validation catches errors
- Error handling works properly
"""

from fastapi.testclient import TestClient
from src.main import app
import pytest

# Create test client
client = TestClient(app)


# ============================================
# Health Check Tests
# ============================================

def test_root_endpoint():
    """Test the root endpoint returns health status"""
    response = client.get("/")
    
    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "healthy"
    assert data["service"] == "sentiment-api"
    assert "version" in data


def test_health_endpoint():
    """Test the /health endpoint for Kubernetes"""
    response = client.get("/health")
    
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}


# ============================================
# Sentiment Analysis Tests
# ============================================

def test_analyze_positive_sentiment():
    """Test sentiment analysis with clearly positive text"""
    response = client.post(
        "/analyze",
        json={"text": "I absolutely love this product! It's amazing and wonderful!"}
    )
    
    assert response.status_code == 200
    data = response.json()
    
    # Verify response structure
    assert "text" in data
    assert "sentiment" in data
    assert "confidence" in data
    assert "processing_time_ms" in data
    
    # Verify sentiment detection
    assert data["sentiment"] == "POSITIVE"
    assert data["confidence"] > 0.9  # Should be very confident
    assert data["processing_time_ms"] > 0  # Should take some time


def test_analyze_negative_sentiment():
    """Test sentiment analysis with clearly negative text"""
    response = client.post(
        "/analyze",
        json={"text": "This is terrible, horrible, and awful. I hate it."}
    )
    
    assert response.status_code == 200
    data = response.json()
    
    assert data["sentiment"] == "NEGATIVE"
    assert data["confidence"] > 0.9


def test_analyze_neutral_text():
    """Test sentiment analysis with neutral text"""
    response = client.post(
        "/analyze",
        json={"text": "The item is blue."}
    )
    
    assert response.status_code == 200
    data = response.json()
    
    # Neutral text might be classified as either POSITIVE or NEGATIVE
    # with lower confidence
    assert data["sentiment"] in ["POSITIVE", "NEGATIVE"]
    assert "confidence" in data


# ============================================
# Input Validation Tests
# ============================================

def test_empty_text_rejected():
    """Test that empty text is rejected"""
    response = client.post(
        "/analyze",
        json={"text": ""}
    )
    
    assert response.status_code == 422  # Validation error
    # FastAPI returns detailed validation errors


def test_missing_text_field():
    """Test that missing text field is rejected"""
    response = client.post(
        "/analyze",
        json={}
    )
    
    assert response.status_code == 422


def test_text_too_long():
    """Test that text exceeding max length is rejected"""
    long_text = "a" * 513  # Max is 512 characters
    
    response = client.post(
        "/analyze",
        json={"text": long_text}
    )
    
    assert response.status_code == 422


def test_non_string_text():
    """Test that non-string text is rejected"""
    response = client.post(
        "/analyze",
        json={"text": 12345}  # Number instead of string
    )
    
    assert response.status_code == 422


# ============================================
# Response Format Tests
# ============================================

def test_response_contains_all_fields():
    """Test that response has all required fields"""
    response = client.post(
        "/analyze",
        json={"text": "Great product!"}
    )
    
    assert response.status_code == 200
    data = response.json()
    
    # Check all required fields exist
    required_fields = ["text", "sentiment", "confidence", "processing_time_ms"]
    for field in required_fields:
        assert field in data, f"Missing field: {field}"


def test_confidence_is_valid_probability():
    """Test that confidence is between 0 and 1"""
    response = client.post(
        "/analyze",
        json={"text": "Excellent!"}
    )
    
    data = response.json()
    confidence = data["confidence"]
    
    assert 0 <= confidence <= 1, "Confidence must be between 0 and 1"


def test_sentiment_is_valid_label():
    """Test that sentiment is either POSITIVE or NEGATIVE"""
    response = client.post(
        "/analyze",
        json={"text": "Test text"}
    )
    
    data = response.json()
    sentiment = data["sentiment"]
    
    assert sentiment in ["POSITIVE", "NEGATIVE"], "Invalid sentiment label"


# ============================================
# Edge Case Tests
# ============================================

def test_special_characters():
    """Test handling of special characters"""
    response = client.post(
        "/analyze",
        json={"text": "Wow!!! This is #amazing 😊 @company"}
    )
    
    assert response.status_code == 200
    # Should handle emojis and special chars gracefully


def test_multiple_languages_english_only():
    """Test that non-English text still gets processed"""
    # Note: DistilBERT is trained on English
    # This test just verifies it doesn't crash
    response = client.post(
        "/analyze",
        json={"text": "Hola mundo"}
    )
    
    assert response.status_code == 200
    # May not be accurate, but shouldn't crash


def test_very_short_text():
    """Test analysis of very short text"""
    response = client.post(
        "/analyze",
        json={"text": "Good"}
    )
    
    assert response.status_code == 200
    data = response.json()
    assert data["sentiment"] == "POSITIVE"


def test_maximum_length_text():
    """Test analysis of text at maximum allowed length"""
    max_text = "a" * 512  # Exactly at max
    
    response = client.post(
        "/analyze",
        json={"text": max_text}
    )
    
    assert response.status_code == 200


# ============================================
# Performance Tests
# ============================================

def test_response_time_reasonable():
    """Test that response time is reasonable (< 5 seconds)"""
    import time
    
    start = time.time()
    response = client.post(
        "/analyze",
        json={"text": "Test performance"}
    )
    elapsed = time.time() - start
    
    assert response.status_code == 200
    assert elapsed < 5.0, f"Response took {elapsed}s, should be < 5s"


# ============================================
# API Documentation Tests
# ============================================

def test_openapi_docs_available():
    """Test that API documentation is available"""
    response = client.get("/docs")
    assert response.status_code == 200


def test_openapi_json_available():
    """Test that OpenAPI JSON schema is available"""
    response = client.get("/openapi.json")
    assert response.status_code == 200
    
    schema = response.json()
    assert "openapi" in schema
    assert "info" in schema
    assert "paths" in schema