fix: harden search validation and release v0.7.1

This commit is contained in:
jackwener
2026-03-13 01:04:29 +08:00
parent dc832f2ee2
commit 7d1b519c85
13 changed files with 203 additions and 490 deletions

View File

@@ -44,6 +44,26 @@ def test_cli_feed_json_input_path(tmp_path, tweet_factory) -> None:
assert '"id": "1"' in result.output
def test_cli_feed_input_accepts_structured_json_envelope(tmp_path, tweet_factory) -> None:
json_path = tmp_path / "tweets.json"
json_path.write_text(
(
"{\n"
' "ok": true,\n'
' "schema_version": "1",\n'
' "data": %s\n'
"}\n"
)
% tweets_to_json([tweet_factory("1")]),
encoding="utf-8",
)
runner = CliRunner()
result = runner.invoke(cli, ["feed", "--input", str(json_path), "--json"])
assert result.exit_code == 0
assert '"id": "1"' in result.output
def test_print_tweet_table_truncates_text_by_default(tweet_factory) -> None:
long_text = "A" * 140
console = Console(record=True, width=400)
@@ -489,6 +509,24 @@ def test_cli_search_empty_query_no_options() -> None:
assert "Provide a QUERY" in result.output
def test_cli_search_invalid_date_rejected(monkeypatch) -> None:
monkeypatch.setattr("twitter_cli.cli._get_client", lambda config=None: None)
runner = CliRunner()
result = runner.invoke(cli, ["search", "python", "--since", "not-a-date"])
assert result.exit_code != 0
assert "--since must be in YYYY-MM-DD format" in result.output
def test_cli_search_rejects_reversed_date_range(monkeypatch) -> None:
monkeypatch.setattr("twitter_cli.cli._get_client", lambda config=None: None)
runner = CliRunner()
result = runner.invoke(cli, ["search", "python", "--since", "2026-03-02", "--until", "2026-03-01"])
assert result.exit_code != 0
assert "--since must be on or before --until" in result.output
def test_cli_compact_mode(tmp_path, tweet_factory) -> None:
json_path = tmp_path / "tweets.json"
json_path.write_text(tweets_to_json([tweet_factory("1")]), encoding="utf-8")

View File

@@ -24,6 +24,9 @@ class TestBuildSearchQuery:
def test_lang(self) -> None:
assert build_search_query("news", lang="fr") == "news lang:fr"
def test_lang_is_trimmed_and_lowercased(self) -> None:
assert build_search_query("news", lang=" EN ") == "news lang:en"
def test_since(self) -> None:
assert build_search_query("python", since="2026-01-01") == "python since:2026-01-01"
@@ -76,6 +79,30 @@ class TestBuildSearchQuery:
result = build_search_query("", from_user="elonmusk", since="2026-03-01")
assert result == "from:elonmusk since:2026-03-01"
def test_date_range_rejects_reversed_order(self) -> None:
try:
build_search_query("python", since="2026-03-02", until="2026-03-01")
except ValueError as exc:
assert "--since must be on or before --until" in str(exc)
else:
raise AssertionError("Expected ValueError for reversed date range")
def test_invalid_since_rejected(self) -> None:
try:
build_search_query("python", since="not-a-date")
except ValueError as exc:
assert "--since must be in YYYY-MM-DD format" in str(exc)
else:
raise AssertionError("Expected ValueError for invalid since date")
def test_negative_min_likes_rejected(self) -> None:
try:
build_search_query("python", min_likes=-1)
except ValueError as exc:
assert "--min-likes must be greater than or equal to 0" in str(exc)
else:
raise AssertionError("Expected ValueError for negative min_likes")
def test_whitespace_query_trimmed(self) -> None:
assert build_search_query(" python ", lang="en") == "python lang:en"

View File

@@ -22,6 +22,21 @@ def test_tweets_json_roundtrip(tweet_factory) -> None:
assert restored[1].lang == "zh"
def test_tweets_from_json_accepts_structured_success_envelope(tweet_factory) -> None:
tweets = [tweet_factory("1")]
raw = (
"{\n"
' "ok": true,\n'
' "schema_version": "1",\n'
' "data": %s\n'
"}\n"
) % tweets_to_json(tweets)
restored = tweets_from_json(raw)
assert [tweet.id for tweet in restored] == ["1"]
def test_compact_serialization(tweet_factory) -> None:
from twitter_cli.serialization import tweet_to_compact_dict, tweets_to_compact_json
import json