"""Tests for marketing_intelligence.mcp.tools.responses.""" from marketing_intelligence.evasion.detection import PageSignals from marketing_intelligence.mcp.tools.responses import ( AllVideoCommentsResponse, AllVideoSentimentsResponse, ArtistProfileResponse, BatchScrapeResponse, BatchVideoResult, BrowserCapability, BrowsersResponse, FindSoundIdResponse, PageToolResult, ProfilesResponse, RunRecordResponse, ScrapeTagCommentsResponse, SessionBehaviorResponse, SessionClosedResponse, SessionResponse, SoundPageResponse, SoundUrlResponse, StealthOption, StoreResponse, TagPageResponse, ToolResult, UrlScrapeResponse, VideoForSentiment, VideoScrapeResponse, VideoSentimentSummary, ) # ── ToolResult ──────────────────────────────────────────────────────────────── class TestToolResult: def test_default_success(self) -> None: r = ToolResult() assert r.success is True assert r.error is None def test_failure(self) -> None: r = ToolResult(success=False, error="boom") assert r.success is False assert r.error == "boom" # ── PageToolResult ──────────────────────────────────────────────────────────── class TestPageToolResult: def test_no_signals_by_default(self) -> None: r = PageToolResult() assert r.signals is None def test_accepts_signals(self) -> None: sig = PageSignals(block_signal="none") r = PageToolResult(signals=sig) assert r.signals is not None assert r.signals.block_signal == "none" def test_is_tool_result(self) -> None: assert issubclass(PageToolResult, ToolResult) # ── TagPageResponse ─────────────────────────────────────────────────────────── class TestTagPageResponse: def test_minimal(self) -> None: r = TagPageResponse(tag="hateou") assert r.tag == "hateou" assert r.videos == [] assert r.success is True def test_error_envelope(self) -> None: sig = PageSignals(block_signal="unknown") r = TagPageResponse(success=False, error="blocked", tag="hateou", signals=sig) assert r.success is False assert r.signals is not None assert r.signals.block_signal == "unknown" def test_is_page_tool_result(self) -> None: assert issubclass(TagPageResponse, PageToolResult) # ── SoundPageResponse ───────────────────────────────────────────────────────── class TestSoundPageResponse: def test_minimal(self) -> None: r = SoundPageResponse(sound_id="123456") assert r.sound_id == "123456" assert r.video_urls == [] assert r.success is True def test_full(self) -> None: r = SoundPageResponse( sound_id="123456", sound_page_url="https://tiktok.com/music/x-123456", title="My Song", video_count_text="1.2M videos", video_urls=["https://tiktok.com/@a/video/1"], ) assert r.title == "My Song" assert len(r.video_urls) == 1 # ── VideoScrapeResponse ─────────────────────────────────────────────────────── class TestVideoScrapeResponse: def test_minimal(self) -> None: r = VideoScrapeResponse(url="https://tiktok.com/@a/video/1") assert r.url == "https://tiktok.com/@a/video/1" assert r.comment_texts == [] assert r.success is True def test_has_video_data_fields(self) -> None: r = VideoScrapeResponse( url="https://tiktok.com/@a/video/1", views=100_000, likes=5_000, sound_id="999", ) assert r.views == 100_000 assert r.likes == 5_000 assert r.sound_id == "999" def test_is_page_tool_result(self) -> None: assert issubclass(VideoScrapeResponse, PageToolResult) # ── UrlScrapeResponse ───────────────────────────────────────────────────────── class TestUrlScrapeResponse: def test_defaults(self) -> None: r = UrlScrapeResponse(url="https://tiktok.com/tag/x") assert r.content == "" assert r.success is True # ── SoundUrlResponse ────────────────────────────────────────────────────────── class TestSoundUrlResponse: def test_fields(self) -> None: r = SoundUrlResponse(sound_id="42", url="https://tiktok.com/music/x-42") assert r.sound_id == "42" assert r.url == "https://tiktok.com/music/x-42" assert r.success is True # ── ArtistProfileResponse ───────────────────────────────────────────────────── class TestArtistProfileResponse: def test_minimal(self) -> None: r = ArtistProfileResponse(handle="mylessmithuk") assert r.handle == "mylessmithuk" assert r.followers is None assert r.success is True def test_full(self) -> None: r = ArtistProfileResponse( handle="mylessmithuk", nickname="Myles Smith", followers=500_000, following=300, likes=10_000_000, video_count=42, ) assert r.nickname == "Myles Smith" assert r.followers == 500_000 def test_is_page_tool_result(self) -> None: assert issubclass(ArtistProfileResponse, PageToolResult) # ── FindSoundIdResponse ─────────────────────────────────────────────────────── class TestFindSoundIdResponse: def test_success(self) -> None: r = FindSoundIdResponse( artist_handle="mylessmithuk", track_tag="hateou", sound_id="111" ) assert r.sound_id == "111" assert r.success is True def test_failure(self) -> None: r = FindSoundIdResponse( success=False, artist_handle="mylessmithuk", track_tag="hateou", error="not found", ) assert r.sound_id is None assert r.success is False # ── ScrapeTagCommentsResponse ───────────────────────────────────────────────── class TestScrapeTagCommentsResponse: def test_defaults(self) -> None: r = ScrapeTagCommentsResponse(tag="hateou") assert r.videos_processed == 0 assert r.errors == 0 assert r.video_ids == [] assert r.success is True def test_populated(self) -> None: r = ScrapeTagCommentsResponse( tag="hateou", videos_processed=5, errors=1, video_ids=["a", "b"] ) assert r.videos_processed == 5 assert len(r.video_ids) == 2 # ── BatchVideoResult / BatchScrapeResponse ──────────────────────────────────── class TestBatchVideoResult: def test_defaults(self) -> None: r = BatchVideoResult(url="https://tiktok.com/@a/video/1") assert r.success is True assert r.video_id is None assert r.error is None def test_failure(self) -> None: r = BatchVideoResult( url="https://tiktok.com/@a/video/1", success=False, error="blocked" ) assert r.success is False class TestBatchScrapeResponse: def test_defaults(self) -> None: r = BatchScrapeResponse() assert r.results == [] assert r.count == 0 assert r.failed_count == 0 def test_with_results(self) -> None: items = [ BatchVideoResult(url="https://tiktok.com/@a/video/1"), BatchVideoResult( url="https://tiktok.com/@a/video/2", success=False, error="x" ), ] r = BatchScrapeResponse(results=items, count=2, failed_count=1) assert r.count == 2 assert r.failed_count == 1 assert r.success is True # ── Session / Browser responses ─────────────────────────────────────────────── class TestSessionResponse: def test_defaults(self) -> None: r = SessionResponse() assert r.session_id is None assert r.human_behavior is True assert r.delay_factor == 1.0 assert r.scroll_speed == "normal" class TestSessionClosedResponse: def test_required_field(self) -> None: r = SessionClosedResponse(session_id="abc-123") assert r.session_id == "abc-123" assert r.success is True class TestBrowsersResponse: def test_defaults(self) -> None: r = BrowsersResponse() assert r.browsers == [] assert r.tip is None def test_with_capabilities(self) -> None: cap = BrowserCapability( name="playwright", stealths=[StealthOption(name="stealth_sync", description="sync stealth")], ) r = BrowsersResponse(browsers=[cap]) assert len(r.browsers) == 1 assert r.browsers[0].name == "playwright" class TestProfilesResponse: def test_defaults(self) -> None: r = ProfilesResponse() assert r.profiles == [] # ── Storage-adjacent responses ──────────────────────────────────────────────── class TestStoreResponse: def test_defaults(self) -> None: r = StoreResponse() assert r.mission == "unspecified" assert r.count == 0 class TestRunRecordResponse: def test_defaults(self) -> None: r = RunRecordResponse() assert r.run_id is None assert r.success is True # ── Sentiment responses ─────────────────────────────────────────────────────── class TestVideoForSentiment: def test_defaults(self) -> None: r = VideoForSentiment() assert r.video_id is None assert r.comment_texts == [] assert r.is_original_sound is False class TestAllVideoCommentsResponse: def test_defaults(self) -> None: r = AllVideoCommentsResponse() assert r.videos == [] assert r.total_videos == 0 class TestAllVideoSentimentsResponse: def test_defaults(self) -> None: r = AllVideoSentimentsResponse() assert r.videos == [] assert r.total_videos == 0 class TestVideoSentimentSummary: def test_defaults(self) -> None: r = VideoSentimentSummary() assert r.key_themes == [] assert r.sentiment is None class TestSessionBehaviorResponse: def test_fields(self) -> None: r = SessionBehaviorResponse( session_id="abc", delay_factor=1.5, human_behavior=False ) assert r.delay_factor == 1.5 assert r.human_behavior is False