Spaces:
Running
Running
| """ | |
| Tests for sentiment aggregation logic. | |
| """ | |
| import pytest | |
| import numpy as np | |
| from datetime import datetime, timezone | |
| def calculate_recency_weights(hours_list: list[float], tau: float = 12.0) -> list[float]: | |
| """ | |
| Calculate recency weights for testing. | |
| Mirrors the logic in ai_engine.py | |
| """ | |
| weights = [np.exp(h / tau) for h in hours_list] | |
| total = sum(weights) | |
| return [w / total for w in weights] | |
| class TestRecencyWeighting: | |
| def test_later_gets_higher_weight(self): | |
| """Articles later in the day should get higher weight.""" | |
| hours = [9.0, 12.0, 16.0] # 9am, noon, 4pm | |
| weights = calculate_recency_weights(hours) | |
| # Later hour should have higher weight | |
| assert weights[2] > weights[1] > weights[0] | |
| def test_weights_sum_to_one(self): | |
| """Normalized weights should sum to 1.""" | |
| hours = [9.0, 12.0, 15.0, 18.0] | |
| weights = calculate_recency_weights(hours) | |
| assert abs(sum(weights) - 1.0) < 0.0001 | |
| def test_single_article_weight_one(self): | |
| """Single article should have weight of 1.""" | |
| weights = calculate_recency_weights([12.0]) | |
| assert weights[0] == 1.0 | |
| def test_tau_affects_spread(self): | |
| """Lower tau should increase weight spread.""" | |
| hours = [9.0, 16.0] | |
| weights_high_tau = calculate_recency_weights(hours, tau=24.0) | |
| weights_low_tau = calculate_recency_weights(hours, tau=6.0) | |
| # With lower tau, the difference should be larger | |
| spread_high = weights_high_tau[1] - weights_high_tau[0] | |
| spread_low = weights_low_tau[1] - weights_low_tau[0] | |
| assert spread_low > spread_high | |
| class TestSentimentIndex: | |
| def test_weighted_average(self): | |
| """Test weighted average calculation.""" | |
| scores = [0.5, -0.2, 0.3] | |
| hours = [9.0, 14.0, 16.0] | |
| weights = calculate_recency_weights(hours) | |
| weighted_avg = sum(s * w for s, w in zip(scores, weights)) | |
| # Should be between min and max scores | |
| assert min(scores) <= weighted_avg <= max(scores) | |
| def test_all_positive_yields_positive(self): | |
| """All positive scores should yield positive index.""" | |
| scores = [0.3, 0.5, 0.4] | |
| hours = [9.0, 12.0, 15.0] | |
| weights = calculate_recency_weights(hours) | |
| weighted_avg = sum(s * w for s, w in zip(scores, weights)) | |
| assert weighted_avg > 0 | |
| def test_all_negative_yields_negative(self): | |
| """All negative scores should yield negative index.""" | |
| scores = [-0.3, -0.5, -0.4] | |
| hours = [9.0, 12.0, 15.0] | |
| weights = calculate_recency_weights(hours) | |
| weighted_avg = sum(s * w for s, w in zip(scores, weights)) | |
| assert weighted_avg < 0 | |
| def test_equal_positive_negative_near_zero(self): | |
| """Equal positive and negative with same timing should be near zero.""" | |
| scores = [0.5, -0.5] | |
| hours = [12.0, 12.0] # Same time | |
| weights = calculate_recency_weights(hours) | |
| weighted_avg = sum(s * w for s, w in zip(scores, weights)) | |
| assert abs(weighted_avg) < 0.1 | |
| class TestSentimentLabel: | |
| def test_bullish_threshold(self): | |
| """Positive sentiment above 0.1 should be Bullish.""" | |
| from app.inference import get_sentiment_label | |
| assert get_sentiment_label(0.15) == "Bullish" | |
| assert get_sentiment_label(0.5) == "Bullish" | |
| def test_bearish_threshold(self): | |
| """Negative sentiment below -0.1 should be Bearish.""" | |
| from app.inference import get_sentiment_label | |
| assert get_sentiment_label(-0.15) == "Bearish" | |
| assert get_sentiment_label(-0.5) == "Bearish" | |
| def test_neutral_range(self): | |
| """Sentiment between -0.1 and 0.1 should be Neutral.""" | |
| from app.inference import get_sentiment_label | |
| assert get_sentiment_label(0.0) == "Neutral" | |
| assert get_sentiment_label(0.05) == "Neutral" | |
| assert get_sentiment_label(-0.05) == "Neutral" | |