chartbastan/backend/tests/test_sentiment_service.py
2026-02-01 09:31:38 +01:00

305 lines
10 KiB
Python

"""
Tests for Sentiment Service
This module tests sentiment service functionality including database operations.
"""
import pytest
from sqlalchemy.orm import Session
from datetime import datetime
from app.services.sentiment_service import (
process_tweet_sentiment,
process_tweet_batch,
process_reddit_post_sentiment,
process_reddit_post_batch,
get_sentiment_by_entity,
get_sentiments_by_match,
calculate_match_sentiment_metrics,
get_global_sentiment_metrics
)
from app.models.sentiment_score import SentimentScore
from app.models.tweet import Tweet
from app.models.reddit_post import RedditPost
@pytest.fixture
def sample_tweet(db: Session):
"""Create a sample tweet for testing."""
tweet = Tweet(
tweet_id="test_tweet_1",
text="This is a test tweet about a great game!",
created_at=datetime.utcnow(),
retweet_count=5,
like_count=10,
match_id=1,
source="twitter"
)
db.add(tweet)
db.commit()
db.refresh(tweet)
return tweet
@pytest.fixture
def sample_tweets(db: Session):
"""Create sample tweets for batch testing."""
tweets = []
texts = [
"I love this team! Best performance ever!",
"Terrible game today. Worst performance.",
"It was an okay match. Nothing special.",
"Amazing comeback! What a victory!",
"Disappointed with the result."
]
for i, text in enumerate(texts):
tweet = Tweet(
tweet_id=f"test_tweet_{i}",
text=text,
created_at=datetime.utcnow(),
retweet_count=i * 2,
like_count=i * 3,
match_id=1,
source="twitter"
)
db.add(tweet)
tweets.append(tweet)
db.commit()
for tweet in tweets:
db.refresh(tweet)
return tweets
@pytest.fixture
def sample_reddit_post(db: Session):
"""Create a sample Reddit post for testing."""
post = RedditPost(
post_id="test_post_1",
title="This is a test post about a great game!",
text="The team played amazingly well today!",
upvotes=15,
created_at=datetime.utcnow(),
match_id=1,
subreddit="test_subreddit",
source="reddit"
)
db.add(post)
db.commit()
db.refresh(post)
return post
@pytest.fixture
def sample_reddit_posts(db: Session):
"""Create sample Reddit posts for batch testing."""
posts = []
titles = [
"Great game today!",
"Terrible performance",
"Okay match",
"Amazing victory",
"Disappointed result"
]
texts = [
"The team was amazing!",
"Worst game ever",
"Nothing special",
"What a comeback!",
"Not good enough"
]
for i, (title, text) in enumerate(zip(titles, texts)):
post = RedditPost(
post_id=f"test_post_{i}",
title=title,
text=text,
upvotes=i * 5,
created_at=datetime.utcnow(),
match_id=1,
subreddit="test_subreddit",
source="reddit"
)
db.add(post)
posts.append(post)
db.commit()
for post in posts:
db.refresh(post)
return posts
class TestProcessTweetSentiment:
"""Test processing single tweet sentiment."""
def test_process_tweet_sentiment_creates_record(self, db: Session, sample_tweet: Tweet):
"""Test that processing tweet sentiment creates a database record."""
sentiment = process_tweet_sentiment(db, sample_tweet.tweet_id, sample_tweet.text)
assert sentiment.id is not None
assert sentiment.entity_id == sample_tweet.tweet_id
assert sentiment.entity_type == 'tweet'
assert sentiment.sentiment_type in ['positive', 'negative', 'neutral']
assert -1 <= sentiment.score <= 1
assert 0 <= sentiment.positive <= 1
assert 0 <= sentiment.negative <= 1
assert 0 <= sentiment.neutral <= 1
def test_process_tweet_sentiment_positive(self, db: Session, sample_tweet: Tweet):
"""Test processing positive tweet sentiment."""
positive_text = "I absolutely love this team! Best performance ever!"
sentiment = process_tweet_sentiment(db, "test_pos", positive_text)
assert sentiment.sentiment_type == 'positive'
assert sentiment.score > 0.05
def test_process_tweet_sentiment_negative(self, db: Session, sample_tweet: Tweet):
"""Test processing negative tweet sentiment."""
negative_text = "This is terrible! Worst performance ever!"
sentiment = process_tweet_sentiment(db, "test_neg", negative_text)
assert sentiment.sentiment_type == 'negative'
assert sentiment.score < -0.05
class TestProcessTweetBatch:
"""Test batch processing of tweet sentiments."""
def test_process_tweet_batch(self, db: Session, sample_tweets: list[Tweet]):
"""Test processing multiple tweets in batch."""
sentiments = process_tweet_batch(db, sample_tweets)
assert len(sentiments) == 5
assert all(s.entity_type == 'tweet' for s in sentiments)
assert all(s.id is not None for s in sentiments)
def test_process_tweet_batch_empty(self, db: Session):
"""Test processing empty batch."""
sentiments = process_tweet_batch(db, [])
assert sentiments == []
def test_process_tweet_batch_sentiments_calculated(self, db: Session, sample_tweets: list[Tweet]):
"""Test that sentiments are correctly calculated for batch."""
sentiments = process_tweet_batch(db, sample_tweets)
# Check that at least one positive and one negative sentiment exists
sentiment_types = [s.sentiment_type for s in sentiments]
assert 'positive' in sentiment_types
assert 'negative' in sentiment_types
class TestProcessRedditPostSentiment:
"""Test processing single Reddit post sentiment."""
def test_process_reddit_post_sentiment_creates_record(self, db: Session, sample_reddit_post: RedditPost):
"""Test that processing Reddit post sentiment creates a database record."""
sentiment = process_reddit_post_sentiment(
db,
sample_reddit_post.post_id,
f"{sample_reddit_post.title} {sample_reddit_post.text}"
)
assert sentiment.id is not None
assert sentiment.entity_id == sample_reddit_post.post_id
assert sentiment.entity_type == 'reddit_post'
assert sentiment.sentiment_type in ['positive', 'negative', 'neutral']
class TestProcessRedditPostBatch:
"""Test batch processing of Reddit post sentiments."""
def test_process_reddit_post_batch(self, db: Session, sample_reddit_posts: list[RedditPost]):
"""Test processing multiple Reddit posts in batch."""
sentiments = process_reddit_post_batch(db, sample_reddit_posts)
assert len(sentiments) == 5
assert all(s.entity_type == 'reddit_post' for s in sentiments)
assert all(s.id is not None for s in sentiments)
def test_process_reddit_post_batch_empty(self, db: Session):
"""Test processing empty batch."""
sentiments = process_reddit_post_batch(db, [])
assert sentiments == []
class TestGetSentimentByEntity:
"""Test retrieving sentiment by entity."""
def test_get_sentiment_by_entity_found(self, db: Session, sample_tweet: Tweet):
"""Test retrieving existing sentiment by entity."""
process_tweet_sentiment(db, sample_tweet.tweet_id, sample_tweet.text)
sentiment = get_sentiment_by_entity(
db,
sample_tweet.tweet_id,
'tweet'
)
assert sentiment is not None
assert sentiment.entity_id == sample_tweet.tweet_id
def test_get_sentiment_by_entity_not_found(self, db: Session):
"""Test retrieving non-existent sentiment."""
sentiment = get_sentiment_by_entity(db, "nonexistent_id", "tweet")
assert sentiment is None
class TestGetSentimentsByMatch:
"""Test retrieving sentiments by match."""
def test_get_sentiments_by_match(self, db: Session, sample_tweets: list[Tweet]):
"""Test retrieving all sentiments for a match."""
process_tweet_batch(db, sample_tweets)
sentiments = get_sentiments_by_match(db, 1)
assert len(sentiments) == 5
assert all(s.entity_type == 'tweet' for s in sentiments)
def test_get_sentiments_by_match_empty(self, db: Session):
"""Test retrieving sentiments for match with no data."""
sentiments = get_sentiments_by_match(db, 999)
assert sentiments == []
class TestCalculateMatchSentimentMetrics:
"""Test calculating sentiment metrics for a match."""
def test_calculate_match_sentiment_metrics(self, db: Session, sample_tweets: list[Tweet]):
"""Test calculating metrics for a match."""
process_tweet_batch(db, sample_tweets)
metrics = calculate_match_sentiment_metrics(db, 1)
assert metrics['match_id'] == 1
assert metrics['total_count'] == 5
assert metrics['positive_count'] + metrics['negative_count'] + metrics['neutral_count'] == 5
assert metrics['positive_ratio'] + metrics['negative_ratio'] + metrics['neutral_ratio'] == 1.0
assert -1 <= metrics['average_compound'] <= 1
def test_calculate_match_sentiment_metrics_empty(self, db: Session):
"""Test calculating metrics for match with no data."""
metrics = calculate_match_sentiment_metrics(db, 999)
assert metrics['match_id'] == 999
assert metrics['total_count'] == 0
assert metrics['average_compound'] == 0.0
class TestGetGlobalSentimentMetrics:
"""Test calculating global sentiment metrics."""
def test_get_global_sentiment_metrics(self, db: Session, sample_tweets: list[Tweet]):
"""Test calculating global metrics."""
process_tweet_batch(db, sample_tweets)
metrics = get_global_sentiment_metrics(db)
assert metrics['total_count'] == 5
assert metrics['positive_count'] + metrics['negative_count'] + metrics['neutral_count'] == 5
assert metrics['positive_ratio'] + metrics['negative_ratio'] + metrics['neutral_ratio'] == 1.0
def test_get_global_sentiment_metrics_empty(self, db: Session):
"""Test calculating global metrics with no data."""
metrics = get_global_sentiment_metrics(db)
assert metrics['total_count'] == 0
assert metrics['average_compound'] == 0.0