Files
twitter-cli-cookiefile/tests/test_filter.py
jackwener e496d8f870 refactor: unify exception handling, add ISO 8601 time, dedup commands, expand tests
- Replace _error_code_for_message() string matching with error_code attribute on exception classes
- Add error_code to all TwitterError subclasses (AuthenticationError, RateLimitError, etc.)
- Add InvalidInputError exception class
- TwitterAPIError derives error_code from HTTP status code automatically
- auth.py: use AuthenticationError instead of RuntimeError
- cli.py: catch (TwitterError, RuntimeError) for backward compat
- Extract _fetch_and_display_users() to deduplicate followers/following commands
- Add format_iso8601() to timeutil.py
- Add createdAtISO field to tweet and user profile serialization
- New test files: test_output.py, test_cache.py, test_timeutil.py
- Expand test_filter.py (topN, score mode, custom weights, empty input)
- Tests: 152 → 194 unit tests, all passing
2026-03-16 18:24:35 +08:00

89 lines
2.7 KiB
Python

"""Tests for twitter_cli.filter module."""
from __future__ import annotations
from twitter_cli.filter import filter_tweets, score_tweet
def test_filter_tweets_does_not_mutate_input(tweet_factory) -> None:
tweet = tweet_factory("1", score=0.0)
output = filter_tweets([tweet], {"mode": "all", "weights": {}})
assert tweet.score == 0.0
assert output[0].score > 0.0
assert output[0] is not tweet
def test_filter_tweets_applies_language_and_retweet_filters(tweet_factory) -> None:
tweets = [
tweet_factory("1", lang="en", is_retweet=False),
tweet_factory("2", lang="zh", is_retweet=False),
tweet_factory("3", lang="en", is_retweet=True),
]
output = filter_tweets(
tweets,
{
"mode": "all",
"lang": ["en"],
"excludeRetweets": True,
"weights": {},
},
)
assert [tweet.id for tweet in output] == ["1"]
def test_filter_topn_mode(tweet_factory) -> None:
tweets = [tweet_factory(str(i)) for i in range(10)]
output = filter_tweets(tweets, {"mode": "topN", "topN": 3})
assert len(output) == 3
def test_filter_topn_default(tweet_factory) -> None:
"""Default topN is 20, so 5 tweets should all be returned."""
tweets = [tweet_factory(str(i)) for i in range(5)]
output = filter_tweets(tweets, {"mode": "topN"})
assert len(output) == 5
def test_filter_score_mode(tweet_factory) -> None:
from twitter_cli.models import Metrics
tweets = [
tweet_factory("high", metrics=Metrics(likes=1000, retweets=500, replies=200, views=100000, bookmarks=50)),
tweet_factory("low", metrics=Metrics(likes=0, retweets=0, replies=0, views=1, bookmarks=0)),
]
output = filter_tweets(tweets, {"mode": "score", "minScore": 100.0})
assert len(output) == 1
assert output[0].id == "high"
def test_filter_empty_input() -> None:
output = filter_tweets([], {"mode": "all"})
assert output == []
def test_score_tweet_basic(tweet_factory) -> None:
tweet = tweet_factory("1")
score = score_tweet(tweet)
assert isinstance(score, float)
assert score > 0
def test_score_tweet_custom_weights(tweet_factory) -> None:
tweet = tweet_factory("1")
weights = {"likes": 0, "retweets": 0, "replies": 0, "bookmarks": 0, "views_log": 0}
assert score_tweet(tweet, weights) == 0.0
def test_filter_all_mode_sorts_by_score(tweet_factory) -> None:
from twitter_cli.models import Metrics
tweets = [
tweet_factory("low", metrics=Metrics(likes=1)),
tweet_factory("high", metrics=Metrics(likes=100)),
]
output = filter_tweets(tweets, {"mode": "all"})
assert output[0].id == "high"
assert output[1].id == "low"