diff --git a/tests/test_cli.py b/tests/test_cli.py index f4bec77..6e08493 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -333,6 +333,41 @@ def fake_run_client_action(ctx, action): assert called["kwargs"]["xsec_token"] == "token-abc" assert called["kwargs"]["xsec_source"] == "pc_search" + def test_sub_comments_passes_xsec_token(self, monkeypatch): + called = {} + + class FakeClient: + def get_sub_comments(self, note_id, comment_id, cursor="", xsec_token=""): + called["note_id"] = note_id + called["comment_id"] = comment_id + called["cursor"] = cursor + called["xsec_token"] = xsec_token + return {"comments": []} + + def fake_handle_command(ctx, action, render, as_json, as_yaml): + action(FakeClient()) + return None + + monkeypatch.setattr("xhs_cli.commands.reading.handle_command", fake_handle_command) + + result = runner.invoke(cli, [ + "sub-comments", + "note-123", + "comment-456", + "--cursor", + "cursor-789", + "--xsec-token", + "synthetic-token", + ]) + + assert result.exit_code == 0 + assert called == { + "note_id": "note-123", + "comment_id": "comment-456", + "cursor": "cursor-789", + "xsec_token": "synthetic-token", + } + def test_read_index_not_found_returns_usage_error(self, monkeypatch): monkeypatch.setattr("xhs_cli.note_refs.get_note_by_index", lambda idx: None) diff --git a/tests/test_client.py b/tests/test_client.py index eb55af7..a123671 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -102,6 +102,41 @@ def close(self): class TestReadingEndpointBehavior: + def test_get_sub_comments_sends_browser_query_parameters(self, monkeypatch): + captured = {} + + def fake_get(self, uri, params=None): + captured["uri"] = uri + captured["params"] = params + return {"comments": []} + + monkeypatch.setattr(XhsClient, "_main_api_get", fake_get) + + client = XhsClient({"a1": "cookie"}) + try: + result = client.get_sub_comments( + "note-123", + "comment-456", + cursor="cursor-789", + xsec_token="synthetic-token", + ) + finally: + client.close() + + assert result == {"comments": []} + assert captured == { + "uri": "/api/sns/web/v2/comment/sub/page", + "params": { + "note_id": "note-123", + "root_comment_id": "comment-456", + "num": 30, + "cursor": "cursor-789", + "image_formats": "jpg,webp,avif", + "top_comment_id": "", + "xsec_token": "synthetic-token", + }, + } + def test_get_note_detail_prefers_cached_xsec_source(self, monkeypatch): captured = {} diff --git a/xhs_cli/client_mixins.py b/xhs_cli/client_mixins.py index b4dada7..a1be28a 100644 --- a/xhs_cli/client_mixins.py +++ b/xhs_cli/client_mixins.py @@ -472,12 +472,16 @@ def get_sub_comments( root_comment_id: str, num: int = 30, cursor: str = "", + xsec_token: str = "", ) -> Any: return self._main_api_get("/api/sns/web/v2/comment/sub/page", { "note_id": note_id, "root_comment_id": root_comment_id, "num": num, "cursor": cursor, + "image_formats": "jpg,webp,avif", + "top_comment_id": "", + "xsec_token": xsec_token, }) diff --git a/xhs_cli/commands/reading.py b/xhs_cli/commands/reading.py index 33f5980..2aef61c 100644 --- a/xhs_cli/commands/reading.py +++ b/xhs_cli/commands/reading.py @@ -229,13 +229,27 @@ def topics(ctx, keyword: str, as_json: bool, as_yaml: bool): @click.argument("note_id") @click.argument("comment_id") @click.option("--cursor", default="", help="Pagination cursor") +@click.option("--xsec-token", default="", help="Security token") @structured_output_options @click.pass_context -def sub_comments(ctx, note_id: str, comment_id: str, cursor: str, as_json: bool, as_yaml: bool): +def sub_comments( + ctx, + note_id: str, + comment_id: str, + cursor: str, + xsec_token: str, + as_json: bool, + as_yaml: bool, +): """View replies to a specific comment.""" handle_command( ctx, - action=lambda client: client.get_sub_comments(note_id, comment_id, cursor=cursor), + action=lambda client: client.get_sub_comments( + note_id, + comment_id, + cursor=cursor, + xsec_token=xsec_token, + ), render=render_comments, as_json=as_json, as_yaml=as_yaml,