sentiment-api / tests /test_api.py
Syed Arfan
Add GitHub Actions CI/CD pipeline
08367f3
raw
history blame
7.37 kB
"""
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