"""Minimal tests for marketing_intelligence.agent_workflows.discovery pure functions.""" from unittest.mock import patch from marketing_intelligence.agent_workflows.discovery import ( _build_params, collect_tools, default_stages, ) from marketing_intelligence.agent_workflows.stages import ( BROWSER_TOOLS, RUN_RECORD_TOOLS, ) from marketing_intelligence.core.models import Campaign def _campaign(**kwargs: object) -> Campaign: defaults: dict = dict( campaign_key="ck1", campaign_nm="Test Campaign", campaign_id="camp1", artist_key="ak1", artist_name="Artist", tiktok_handle="artist_handle", tags=["mytag"], track_names=["My Track"], questions=["What is trending?"], sample_size=50, ) defaults.update(kwargs) return Campaign(**defaults) class TestBuildParams: def test_uses_campaign_id_when_set(self) -> None: p = _build_params(_campaign(campaign_id="camp99"), "run1") assert p["campaign_id"] == "camp99" def test_falls_back_to_campaign_key_when_no_id(self) -> None: p = _build_params(_campaign(campaign_id=None), "run1") assert p["campaign_id"] == "ck1" def test_zips_tags_and_track_names(self) -> None: c = _campaign(tags=["t1", "t2"], track_names=["Track One", "Track Two"]) p = _build_params(c, "run1") assert p["tracks"] == [("t1", "Track One"), ("t2", "Track Two")] def test_uses_tag_as_name_when_no_track_names(self) -> None: c = _campaign(tags=["vibes"], track_names=[]) p = _build_params(c, "run1") assert p["tracks"] == [("vibes", "vibes")] def test_defaults_sample_size_to_10_when_zero(self) -> None: p = _build_params(_campaign(sample_size=0), "run1") assert p["sample_size"] == 10 def test_run_id_in_params(self) -> None: p = _build_params(_campaign(), "run_xyz") assert p["run_id"] == "run_xyz" def test_run_type_is_discovery(self) -> None: p = _build_params(_campaign(), "run1") assert p["run_type"] == "discovery" def test_stealth_hint_passed_through(self) -> None: p = _build_params(_campaign(), "run1", stealth_hint="aggressive") assert p["stealth_hint"] == "aggressive" class TestDefaultStages: def _params(self, **kwargs: object) -> dict: base = _build_params(_campaign(**kwargs), "run1") return base def test_always_has_posts_reasoning_sentiment(self) -> None: stages = default_stages(self._params()) names = [s.name for s in stages] assert "posts" in names assert "reasoning" in names assert "sentiment" in names def test_questions_stage_added_when_questions_present(self) -> None: stages = default_stages(self._params(questions=["Q1?"])) names = [s.name for s in stages] assert "questions" in names def test_no_questions_stage_when_no_questions(self) -> None: stages = default_stages(self._params(questions=[])) names = [s.name for s in stages] assert "questions" not in names class TestCollectTools: def test_always_includes_browser_and_run_record_tools(self) -> None: stages = default_stages(_build_params(_campaign(), "run1")) tools = collect_tools(stages) assert BROWSER_TOOLS.issubset(tools) assert RUN_RECORD_TOOLS.issubset(tools) def test_includes_stage_tools(self) -> None: stages = default_stages(_build_params(_campaign(), "run1")) tools = collect_tools(stages) assert "scrape_video" in tools assert "write_campaign_post" in tools def test_empty_stages_still_has_base_tools(self) -> None: tools = collect_tools([]) assert BROWSER_TOOLS.issubset(tools) assert RUN_RECORD_TOOLS.issubset(tools) class TestRunDiscovery: def test_calls_run_agent_with_correct_run_id(self) -> None: import asyncio from marketing_intelligence.agent_workflows.discovery import run_discovery with ( patch( "marketing_intelligence.agent_workflows.discovery.deactivate_posts", return_value=0, ), patch( "marketing_intelligence.agent_workflows.discovery.run_agent", return_value="done", ) as mock_run, ): result = asyncio.run(run_discovery(_campaign(), "run42")) assert result == "done" call_kwargs = mock_run.call_args assert call_kwargs[1]["run_id"] == "run42"