@@ -74,56 +74,68 @@ def suggest_keyword_themes_side_effect(request):
7474
7575 mock_suggest_service .suggest_keyword_themes .side_effect = suggest_keyword_themes_side_effect # Keep existing side effect logic for now
7676
77- # --- Mocking client.get_type for various types used in the script ---
78- mock_google_ads_client .get_type = MagicMock () # Reset to a simple mock first
79-
80- # Mock for KeywordTheme objects produced by KeywordTheme()
81- # This instance will be returned when KeywordTheme() is called in the script.
82- mock_keyword_theme_instance = MagicMock (spec = ['keyword_theme_constant' , 'free_form_keyword_theme' ])
83- mock_keyword_theme_instance .keyword_theme_constant = None
84- mock_keyword_theme_instance .free_form_keyword_theme = None
85- # This is the mock for the KeywordTheme "class" or "constructor"
86- mock_keyword_theme_constructor = MagicMock (return_value = mock_keyword_theme_instance )
87-
88- # This is the mock for the type that *contains* KeywordTheme
89- # (i.e., what client.get_type("SuggestKeywordThemesResponse") returns)
90- mock_suggest_keyword_themes_response_type = MagicMock ()
91- mock_suggest_keyword_themes_response_type .KeywordTheme = mock_keyword_theme_constructor
92-
93- # Mock for SmartCampaignSuggestionInfo
94- mock_smart_campaign_suggestion_info_instance = MagicMock ()
95- mock_smart_campaign_suggestion_info_instance .location_list = MagicMock ()
96- mock_smart_campaign_suggestion_info_instance .location_list .locations = [] # Must be a list for .append()
97- mock_smart_campaign_suggestion_info_instance .ad_schedules = [] # Must be a list for .append()
98-
99- # Mock for LocationInfo and AdScheduleInfo (simple mocks are fine)
100- mock_location_info_instance = MagicMock ()
101- mock_ad_schedule_info_instance = MagicMock ()
102-
103- def new_get_type_side_effect (type_name ):
77+ # --- Start of client.get_type mocking ---
78+ mock_google_ads_client .get_type = MagicMock () # Reset get_type
79+
80+ def create_mock_keyword_theme_instance_func (): # Renamed to avoid clash if tests run in same scope
81+ instance = MagicMock (spec = ['keyword_theme_constant' , 'free_form_keyword_theme' ])
82+ instance .keyword_theme_constant = None
83+ instance .free_form_keyword_theme = None
84+ return instance
85+
86+ mock_keyword_theme_constructor_main = MagicMock (side_effect = create_mock_keyword_theme_instance_func )
87+ mock_suggest_response_type_main = MagicMock ()
88+ mock_suggest_response_type_main .KeywordTheme = mock_keyword_theme_constructor_main
89+
90+ mock_si_instance_main = MagicMock () # SmartCampaignSuggestionInfo instance
91+ mock_si_instance_main .location_list = MagicMock ()
92+ mock_si_instance_main .location_list .locations = []
93+ mock_si_instance_main .ad_schedules = []
94+
95+ def new_get_type_side_effect_main (type_name ):
10496 if type_name == "SuggestKeywordThemesResponse" :
105- return mock_suggest_keyword_themes_response_type
97+ return mock_suggest_response_type_main
10698 elif type_name == "SmartCampaignSuggestionInfo" :
107- # Important: return the instance directly if the script does client.get_type("SmartCampaignSuggestionInfo")
108- # and expects to set attributes on it. If it calls it like a constructor, then this should return a mock constructor.
109- # The script does: suggestion_info = client.get_type("SmartCampaignSuggestionInfo")
110- # then suggestion_info.location_list = ...
111- # So, returning the instance directly is correct.
112- return mock_smart_campaign_suggestion_info_instance
99+ return mock_si_instance_main
113100 elif type_name == "LocationInfo" :
114- # Script does: location = client.get_type("LocationInfo")
115- return mock_location_info_instance
101+ return MagicMock ()
116102 elif type_name == "AdScheduleInfo" :
117- # Script does: ad_schedule = client.get_type("AdScheduleInfo")
118- return mock_ad_schedule_info_instance
119- else :
120- # Fallback for any other type
121103 return MagicMock ()
122-
123- mock_google_ads_client .get_type .side_effect = new_get_type_side_effect
124- # --- End of get_type mocking ---
125-
126- # Suggest Budget Options
104+ elif type_name == "CampaignBudgetOperation" : return MagicMock ()
105+ elif type_name == "CampaignOperation" : return MagicMock ()
106+ elif type_name == "SmartCampaignSettingOperation" :
107+ op_mock = MagicMock (); op_mock .update = MagicMock (); return op_mock
108+ elif type_name == "CampaignCriterionOperation" : return MagicMock ()
109+ elif type_name == "AdGroupOperation" : return MagicMock ()
110+ elif type_name == "AdGroupAdOperation" : return MagicMock ()
111+ elif type_name == "AdTextAsset" : return MagicMock ()
112+ else : return MagicMock ()
113+
114+ mock_google_ads_client .get_type .side_effect = new_get_type_side_effect_main
115+ # --- End of client.get_type mocking ---
116+
117+ # Refined SmartCampaignSuggestService.suggest_keyword_themes mock
118+ # This is called by _get_keyword_theme_infos for free_form_keyword_text
119+ mock_suggest_themes_response_main = MagicMock ()
120+ free_form_theme_instance = create_mock_keyword_theme_instance_func ()
121+ free_form_theme_instance .free_form_keyword_theme = "mocked free form text" # From free_form_keyword_text
122+ mock_suggest_themes_response_main .keyword_themes = [free_form_theme_instance ]
123+ # The side_effect for suggest_keyword_themes needs to handle different request types if script calls it multiple ways.
124+ # For free_form_text path:
125+ mock_suggest_service .suggest_keyword_themes .return_value = mock_suggest_themes_response_main
126+ # If suggest_keyword_themes was also called for keyword_seed (it's not, suggest_keyword_theme_constants is),
127+ # the side_effect would need to distinguish. Since it's only for free_form_text:
128+ # Simplified: if called, it's for free_form_text.
129+
130+ # Refined KeywordThemeConstantService.suggest_keyword_theme_constants mock
131+ # This is called by _get_keyword_theme_auto_suggestions for keyword_text
132+ mock_ktc_response_main = MagicMock ()
133+ ktc_proto_mock_main = MagicMock () # Simulates KeywordThemeConstant proto
134+ ktc_proto_mock_main .resource_name = "keywordThemeConstants/from_text_search_auto1"
135+ mock_ktc_response_main .keyword_theme_constants = [ktc_proto_mock_main ]
136+ mock_ktc_service .suggest_keyword_theme_constants .return_value = mock_ktc_response_main
137+
138+ # Suggest Budget Options (remains largely the same)
127139 mock_budget_options_response = MagicMock ()
128140 mock_recommended_budget = MagicMock ()
129141 mock_recommended_budget .daily_amount_micros = 50000000 # 50 units of currency
@@ -251,41 +263,60 @@ def test_main_with_business_location_runs_successfully(mock_google_ads_client: M
251263 mock_empty_kw_theme_response .keyword_themes = [] # No themes from this service for this test path
252264 mock_suggest_service .suggest_keyword_themes .return_value = mock_empty_kw_theme_response
253265
254- # --- Mocking client.get_type for various types (similar to the first test) ---
255- mock_google_ads_client .get_type = MagicMock () # Reset to a simple mock
266+ # --- Start of client.get_type mocking for biz test ---
267+ mock_google_ads_client .get_type = MagicMock () # Reset get_type
256268
257- mock_keyword_theme_instance_biz = MagicMock (spec = ['keyword_theme_constant' , 'free_form_keyword_theme' ])
258- mock_keyword_theme_instance_biz .keyword_theme_constant = None
259- mock_keyword_theme_instance_biz .free_form_keyword_theme = None
260- mock_keyword_theme_constructor_biz = MagicMock (return_value = mock_keyword_theme_instance_biz )
269+ def create_mock_keyword_theme_instance_func_biz (): # Unique name
270+ instance = MagicMock (spec = ['keyword_theme_constant' , 'free_form_keyword_theme' ])
271+ instance .keyword_theme_constant = None
272+ instance .free_form_keyword_theme = None
273+ return instance
261274
262- mock_suggest_keyword_themes_response_type_biz = MagicMock ()
263- mock_suggest_keyword_themes_response_type_biz .KeywordTheme = mock_keyword_theme_constructor_biz
275+ mock_keyword_theme_constructor_biz = MagicMock (side_effect = create_mock_keyword_theme_instance_func_biz )
276+ mock_suggest_response_type_biz = MagicMock ()
277+ mock_suggest_response_type_biz .KeywordTheme = mock_keyword_theme_constructor_biz
264278
265- mock_smart_campaign_suggestion_info_instance_biz = MagicMock ()
266- mock_smart_campaign_suggestion_info_instance_biz .location_list = MagicMock ()
267- mock_smart_campaign_suggestion_info_instance_biz .location_list .locations = []
268- mock_smart_campaign_suggestion_info_instance_biz .ad_schedules = []
269-
270- mock_location_info_instance_biz = MagicMock ()
271- mock_ad_schedule_info_instance_biz = MagicMock ()
279+ mock_si_instance_biz = MagicMock () # SmartCampaignSuggestionInfo instance
280+ mock_si_instance_biz .location_list = MagicMock ()
281+ mock_si_instance_biz .location_list .locations = []
282+ mock_si_instance_biz .ad_schedules = []
272283
273- def new_get_type_side_effect_biz (type_name ):
284+ def new_get_type_side_effect_biz_func (type_name ): # Unique name
274285 if type_name == "SuggestKeywordThemesResponse" :
275- return mock_suggest_keyword_themes_response_type_biz
286+ return mock_suggest_response_type_biz
276287 elif type_name == "SmartCampaignSuggestionInfo" :
277- return mock_smart_campaign_suggestion_info_instance_biz
288+ return mock_si_instance_biz
278289 elif type_name == "LocationInfo" :
279- return mock_location_info_instance_biz
290+ return MagicMock ()
280291 elif type_name == "AdScheduleInfo" :
281- return mock_ad_schedule_info_instance_biz
282- else :
283292 return MagicMock ()
284-
285- mock_google_ads_client .get_type .side_effect = new_get_type_side_effect_biz
286- # --- End of get_type mocking for biz test ---
287-
288- mock_budget_options_response = MagicMock ()
293+ elif type_name == "CampaignBudgetOperation" : return MagicMock ()
294+ elif type_name == "CampaignOperation" : return MagicMock ()
295+ elif type_name == "SmartCampaignSettingOperation" :
296+ op_mock = MagicMock (); op_mock .update = MagicMock (); return op_mock
297+ elif type_name == "CampaignCriterionOperation" : return MagicMock ()
298+ elif type_name == "AdGroupOperation" : return MagicMock ()
299+ elif type_name == "AdGroupAdOperation" : return MagicMock ()
300+ elif type_name == "AdTextAsset" : return MagicMock ()
301+ else : return MagicMock ()
302+
303+ mock_google_ads_client .get_type .side_effect = new_get_type_side_effect_biz_func
304+ # --- End of client.get_type mocking for biz test ---
305+
306+ # Refined SmartCampaignSuggestService.suggest_keyword_themes mock for biz test
307+ # free_form_keyword_text is None, so _get_keyword_theme_infos won't call suggest_keyword_themes for it.
308+ mock_suggest_themes_response_biz = MagicMock ()
309+ mock_suggest_themes_response_biz .keyword_themes = [] # No themes from this call path
310+ mock_suggest_service .suggest_keyword_themes .return_value = mock_suggest_themes_response_biz
311+
312+ # Refined KeywordThemeConstantService.suggest_keyword_theme_constants mock for biz test
313+ mock_ktc_response_biz = MagicMock ()
314+ ktc_proto_mock_biz = MagicMock ()
315+ ktc_proto_mock_biz .resource_name = "keywordThemeConstants/from_text_search_auto_biz"
316+ mock_ktc_response_biz .keyword_theme_constants = [ktc_proto_mock_biz ]
317+ mock_ktc_service .suggest_keyword_theme_constants .return_value = mock_ktc_response_biz
318+
319+ # Suggest Budget Options (remains largely the same)
289320 mock_recommended_budget = MagicMock ()
290321 mock_recommended_budget .daily_amount_micros = 55000000
291322 mock_budget_options_response .recommended_daily_budget_options .high .daily_amount_micros = 65000000
0 commit comments