diff --git a/direct_cli/commands/ads.py b/direct_cli/commands/ads.py index 1dc96309..74cec805 100644 --- a/direct_cli/commands/ads.py +++ b/direct_cli/commands/ads.py @@ -24,7 +24,11 @@ def ads(): @click.option("--fetch-all", is_flag=True, help="Fetch all pages") @click.option("--format", "output_format", default="json", help="Output format") @click.option("--output", help="Output file") -@click.option("--fields", help="Comma-separated field names") +@click.option("--fields", help="Comma-separated top-level field names") +@click.option( + "--text-ad-fields", help="Comma-separated TextAd field names (e.g. Title,Text,Href)" +) +@click.option("--dry-run", is_flag=True, help="Show request without sending") @click.pass_context def get( ctx, @@ -37,6 +41,8 @@ def get( output_format, output, fields, + text_ad_fields, + dry_run, ): """Get ads""" try: @@ -52,6 +58,12 @@ def get( else ["Id", "CampaignId", "AdGroupId", "Status", "State", "Type"] ) + text_ad_field_names = ( + text_ad_fields.split(",") + if text_ad_fields + else ["Title", "Title2", "Text", "Href"] + ) + criteria = {} if ids: criteria["Ids"] = parse_ids(ids) @@ -62,13 +74,21 @@ def get( if status: criteria["Statuses"] = [status] - params = {"SelectionCriteria": criteria, "FieldNames": field_names} + params = { + "SelectionCriteria": criteria, + "FieldNames": field_names, + "TextAdFieldNames": text_ad_field_names, + } if limit: params["Page"] = {"Limit": limit} body = {"method": "get", "params": params} + if dry_run: + format_output(body, "json", None) + return + result = client.ads().post(data=body) if fetch_all: diff --git a/direct_cli/utils.py b/direct_cli/utils.py index e2f83323..15d075c2 100644 --- a/direct_cli/utils.py +++ b/direct_cli/utils.py @@ -89,7 +89,7 @@ def parse_date(date_str: str) -> str: "ClientInfo", ], "adgroups": ["Id", "Name", "CampaignId", "Status", "Type", "RegionIds"], - "ads": ["Id", "CampaignId", "AdGroupId", "Status", "State", "Type"], + "ads": ["Id", "CampaignId", "AdGroupId", "Status", "State", "Type", "TextAd"], "keywords": [ "Id", "Keyword", diff --git a/tests/test_dry_run.py b/tests/test_dry_run.py index 9e6068ac..03832346 100644 --- a/tests/test_dry_run.py +++ b/tests/test_dry_run.py @@ -115,6 +115,50 @@ def test_ads_update_extra_json_merges_into_payload(): assert ad["TextAd"] == {"Title": "Updated"} +def test_ads_get_default_fieldnames(): + """Default FieldNames includes basic top-level fields, plus TextAdFieldNames.""" + body = _dry_run("ads", "get", "--campaign-ids", "12345") + assert body["method"] == "get" + assert body["params"]["FieldNames"] == [ + "Id", + "CampaignId", + "AdGroupId", + "Status", + "State", + "Type", + ] + assert body["params"]["TextAdFieldNames"] == ["Title", "Title2", "Text", "Href"] + + +def test_ads_get_with_fields_overrides_defaults(): + """--fields and --text-ad-fields override the defaults.""" + body = _dry_run( + "ads", + "get", + "--campaign-ids", + "12345", + "--fields", + "Id,State", + "--text-ad-fields", + "Title", + ) + assert body["params"]["FieldNames"] == ["Id", "State"] + assert body["params"]["TextAdFieldNames"] == ["Title"] + + +def test_ads_get_with_ids_and_status(): + """Multiple selection criteria are combined correctly.""" + body = _dry_run( + "ads", "get", "--ids", "1,2,3", "--status", "ACCEPTED", "--limit", "10" + ) + assert body["params"]["SelectionCriteria"] == { + "Ids": [1, 2, 3], + "Statuses": ["ACCEPTED"], + } + assert body["params"]["Page"] == {"Limit": 10} + assert "TextAdFieldNames" in body["params"] + + # ---------------------------------------------------------------------- # adgroups # ---------------------------------------------------------------------- diff --git a/tests/test_integration.py b/tests/test_integration.py index b49650e5..3ab5cead 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -157,6 +157,35 @@ def test_get_ads(self): ) assert_success(result, "ads get") + def test_get_ads_returns_textad(self): + """TEXT_AD ads must include TextAd with Title and Text.""" + if not self.campaign_id: + self.skipTest("No campaigns found in account") + result = invoke_get( + "ads", + "get", + "--campaign-ids", + str(self.campaign_id), + "--limit", + "50", + "--format", + "json", + ) + assert_success(result, "ads get (TextAd check)") + data = json.loads(result.output) + text_ads = [ad for ad in data if ad.get("Type") == "TEXT_AD"] + if not text_ads: + self.skipTest("No TEXT_AD ads found in first 50 results") + for ad in text_ads: + self.assertIn( + "TextAd", + ad, + f"TEXT_AD {ad['Id']} missing TextAd — " + "TextAdFieldNames may not be sent", + ) + self.assertIn("Title", ad["TextAd"]) + self.assertIn("Text", ad["TextAd"]) + @pytest.mark.integration @skip_if_no_token