import sys import unittest from pathlib import Path from unittest.mock import patch ROOT = Path(__file__).resolve().parent if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) PROJECT_ROOT = ROOT.parents[1] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from tools.ad_creation import AdCandidate, build_ad_request_body from tools.delivery_config import ( BID_MODE_AVERAGE_COST, BID_MODE_MAX_CONVERSION, parse_bid_scene, parse_placement_config, ) def _candidate() -> AdCandidate: return AdCandidate( account_id=123, adgroup_name="test-ad", site_set=["SITE_SET_WECHAT"], custom_audience=None, bid_amount_fen=35, audience_tier_label="泛人群", age=[{"min": 45, "max": 66}], location_types=["LIVE_IN"], region_ids=[1], daily_budget_fen=10000, time_series="1" * 48, delivery_version="miniapp_user_growth_v1", fingerprint="fp", wechat_position=None, ) class AdCreationStatusTest(unittest.TestCase): def test_ad_request_defaults_to_normal_status(self): with patch("tools.ad_creation.get_account_feedback_id", return_value=6700001): body = build_ad_request_body(_candidate(), begin_date="2026-07-01") self.assertEqual(body["configured_status"], "AD_STATUS_NORMAL") def test_max_conversion_uses_systematic_bid_with_custom_cost_cap(self): candidate = _candidate() candidate.bid_scene = BID_MODE_MAX_CONVERSION candidate.custom_cost_cap_fen = 48 with patch("tools.ad_creation.get_account_feedback_id", return_value=6700001): body = build_ad_request_body(candidate, begin_date="2026-07-01") self.assertEqual("SMART_BID_TYPE_SYSTEMATIC", body["smart_bid_type"]) self.assertEqual(0, body["bid_amount"]) self.assertEqual("COST_CONSTRAINT_SCENE_OPEN", body["cost_constraint_scene"]) self.assertEqual(48, body["custom_cost_cap"]) self.assertNotIn("bid_scene", body) def test_aim_placement_uses_automatic_site_without_site_set(self): candidate = _candidate() candidate.automatic_site_enabled = True candidate.site_set = [] with patch("tools.ad_creation.get_account_feedback_id", return_value=6700001): body = build_ad_request_body(candidate, begin_date="2026-07-01") self.assertTrue(body["automatic_site_enabled"]) self.assertNotIn("site_set", body) def test_parse_feishu_bid_scene_and_placement(self): self.assertEqual(BID_MODE_AVERAGE_COST, parse_bid_scene("")) self.assertEqual(BID_MODE_MAX_CONVERSION, parse_bid_scene("最大转化量")) placement = parse_placement_config( "微信朋友圈,微信公众号与小程序,腾讯平台与内容媒体,腾讯营销联盟" ) self.assertFalse(placement.automatic_site_enabled) self.assertEqual( [ "SITE_SET_MOMENTS", "SITE_SET_WECHAT", "SITE_SET_WECHAT_PLUGIN", "SITE_SET_TENCENT_NEWS", "SITE_SET_TENCENT_VIDEO", "SITE_SET_KANDIAN", "SITE_SET_QQ_MUSIC_GAME", "SITE_SET_MOBILE_UNION", ], placement.site_set, ) aim = parse_placement_config("AIM+") self.assertTrue(aim.automatic_site_enabled) self.assertEqual([], aim.site_set) if __name__ == "__main__": unittest.main()