| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- import sys
- import unittest
- from pathlib import Path
- from unittest.mock import patch
- _HERE = Path(__file__).parent
- sys.path.insert(0, str(_HERE))
- import execute_creation_once # noqa: E402
- class _Strategy:
- def __init__(self, material_source):
- self.material_source = material_source
- self.use_ai_generated = material_source == "ai_generated"
- self.ai_fallback_to_history = False
- class LandingVideoDedupeTest(unittest.TestCase):
- def test_same_crowd_package_excludes_landing_video_across_accounts_in_one_run(self):
- calls = []
- def fake_prepare(account_id, adgroup_id, *, excluded_material_ids, excluded_landing_ids, **kwargs):
- calls.append((account_id, adgroup_id, set(excluded_landing_ids)))
- return {
- "account_id": account_id,
- "adgroup_id": adgroup_id,
- "audience_tier": "wx*商业",
- "landing_video_id": 1000 + account_id,
- "_material_id": f"m-{account_id}",
- "material_source": "history",
- }
- with patch.object(execute_creation_once, "get_creation_account_ids", return_value=[1, 2]), \
- patch.object(execute_creation_once, "load_excluded_ad_ids_from_adjustment", return_value=set()), \
- patch.object(execute_creation_once, "get_account_crowd_package", return_value="wx*商业"), \
- patch.object(execute_creation_once, "load_recent_landing_usage_counts", return_value={"history": {}, "ai_generated": {}}), \
- patch.object(execute_creation_once, "load_account_material_strategy", return_value=_Strategy("history")), \
- patch.object(execute_creation_once, "find_ads_needing_creatives", side_effect=[
- [{"adgroup_id": 101, "creative_count": 3}],
- [{"adgroup_id": 201, "creative_count": 3}],
- ]), \
- patch.object(execute_creation_once, "build_landing_candidate_pool", return_value=object()), \
- patch.object(execute_creation_once, "prepare_one_creative_for_ad", side_effect=fake_prepare), \
- patch.object(execute_creation_once, "record_prepared_material_usage"):
- records = execute_creation_once.phase1_prepare(target_creatives=4)
- self.assertEqual(2, len(records))
- self.assertEqual(set(), calls[0][2])
- self.assertEqual({1001}, calls[1][2])
- def test_ai_landing_dedupe_is_independent_from_history_and_limits_once_per_ai_pool(self):
- calls = []
- def fake_prepare(account_id, adgroup_id, *, excluded_material_ids, excluded_landing_ids, **kwargs):
- calls.append(set(excluded_landing_ids))
- return {
- "account_id": account_id,
- "adgroup_id": adgroup_id,
- "audience_tier": "wx*商业",
- "landing_video_id": 777,
- "_material_id": f"ai:{account_id}-{adgroup_id}-{len(calls)}",
- "material_source": "ai_generated",
- }
- with patch.object(execute_creation_once, "get_creation_account_ids", return_value=[1, 2]), \
- patch.object(execute_creation_once, "load_excluded_ad_ids_from_adjustment", return_value=set()), \
- patch.object(execute_creation_once, "get_account_crowd_package", return_value="wx*商业"), \
- patch.object(
- execute_creation_once,
- "load_recent_landing_usage_counts",
- return_value={"history": {777: 1}, "ai_generated": {}},
- ), \
- patch.object(execute_creation_once, "load_account_material_strategy", return_value=_Strategy("ai_generated")), \
- patch.object(execute_creation_once, "find_ads_needing_creatives", side_effect=[
- [{"adgroup_id": 101, "creative_count": 11}],
- [{"adgroup_id": 201, "creative_count": 11}],
- ]), \
- patch.object(execute_creation_once, "build_landing_candidate_pool", return_value=object()), \
- patch.object(execute_creation_once, "prepare_one_creative_for_ad", side_effect=fake_prepare), \
- patch.object(execute_creation_once, "record_prepared_material_usage"):
- records = execute_creation_once.phase1_prepare(target_creatives=12)
- self.assertEqual(1, len(records))
- self.assertEqual(set(), calls[0])
- self.assertEqual({777}, calls[1])
- def test_phase1_reuses_landing_candidate_pool_for_multiple_creative_attempts(self):
- pool = object()
- build_calls = []
- prepare_calls = []
- def fake_build_pool(account_id):
- build_calls.append(account_id)
- return pool
- def fake_prepare(
- account_id,
- adgroup_id,
- *,
- excluded_material_ids,
- excluded_landing_ids,
- landing_candidates,
- failed_landing_ids,
- ):
- prepare_calls.append((landing_candidates, failed_landing_ids))
- return {
- "account_id": account_id,
- "adgroup_id": adgroup_id,
- "audience_tier": "R330",
- "landing_video_id": 1000 + len(prepare_calls),
- "_material_id": f"ai:{len(prepare_calls)}",
- "material_source": "ai_generated",
- }
- with patch.object(execute_creation_once, "get_creation_account_ids", return_value=[1]), \
- patch.object(execute_creation_once, "load_excluded_ad_ids_from_adjustment", return_value=set()), \
- patch.object(execute_creation_once, "get_account_crowd_package", return_value="R330"), \
- patch.object(execute_creation_once, "load_recent_landing_usage_counts", return_value={"history": {}, "ai_generated": {}}), \
- patch.object(execute_creation_once, "load_account_material_strategy", return_value=_Strategy("ai_generated")), \
- patch.object(execute_creation_once, "find_ads_needing_creatives", return_value=[
- {"adgroup_id": 101, "creative_count": 0},
- ]), \
- patch.object(execute_creation_once, "build_landing_candidate_pool", side_effect=fake_build_pool), \
- patch.object(execute_creation_once, "prepare_one_creative_for_ad", side_effect=fake_prepare), \
- patch.object(execute_creation_once, "record_prepared_material_usage"):
- records = execute_creation_once.phase1_prepare(target_creatives=2)
- self.assertEqual(2, len(records))
- self.assertEqual([1], build_calls)
- self.assertEqual([(pool, set()), (pool, set())], prepare_calls)
- def test_phase1_carries_failed_landing_ids_between_attempts(self):
- calls = []
- def fake_prepare(
- account_id,
- adgroup_id,
- *,
- excluded_material_ids,
- excluded_landing_ids,
- landing_candidates,
- failed_landing_ids,
- ):
- calls.append(set(failed_landing_ids))
- if len(calls) == 1:
- failed_landing_ids.add(777)
- return None
- return {
- "account_id": account_id,
- "adgroup_id": adgroup_id,
- "audience_tier": "R330",
- "landing_video_id": 888,
- "_material_id": "ai:ok",
- "material_source": "ai_generated",
- }
- with patch.object(execute_creation_once, "get_creation_account_ids", return_value=[1]), \
- patch.object(execute_creation_once, "load_excluded_ad_ids_from_adjustment", return_value=set()), \
- patch.object(execute_creation_once, "get_account_crowd_package", return_value="R330"), \
- patch.object(execute_creation_once, "load_recent_landing_usage_counts", return_value={"history": {}, "ai_generated": {}}), \
- patch.object(execute_creation_once, "load_account_material_strategy", return_value=_Strategy("ai_generated")), \
- patch.object(execute_creation_once, "find_ads_needing_creatives", return_value=[
- {"adgroup_id": 101, "creative_count": 0},
- ]), \
- patch.object(execute_creation_once, "build_landing_candidate_pool", return_value=object()), \
- patch.object(execute_creation_once, "prepare_one_creative_for_ad", side_effect=fake_prepare), \
- patch.object(execute_creation_once, "record_prepared_material_usage"):
- records = execute_creation_once.phase1_prepare(target_creatives=2)
- self.assertEqual(1, len(records))
- self.assertEqual([set(), {777}], calls)
- if __name__ == "__main__":
- unittest.main()
|