Skip to content

Commit fef4405

Browse files
Jules was unable to complete the task in time. Please review the work done so far and provide feedback for Jules to continue.
1 parent 4f350a8 commit fef4405

File tree

7 files changed

+700
-0
lines changed

7 files changed

+700
-0
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock, call # Ensure call is imported
3+
4+
from examples.basic_operations import add_ad_groups
5+
6+
class TestAddAdGroups(unittest.TestCase):
7+
8+
@patch("examples.basic_operations.add_ad_groups.argparse.ArgumentParser")
9+
@patch("examples.basic_operations.add_ad_groups.GoogleAdsClient.load_from_storage")
10+
def test_main(self, mock_load_from_storage, mock_argument_parser):
11+
# Mock the GoogleAdsClient
12+
mock_google_ads_client = MagicMock()
13+
mock_load_from_storage.return_value = mock_google_ads_client
14+
15+
# Mock the GoogleAdsService
16+
mock_ga_service = MagicMock()
17+
mock_google_ads_client.get_service.return_value = mock_ga_service
18+
19+
# Mock the AdGroupService
20+
mock_ad_group_service = MagicMock()
21+
# Ensure get_service is flexible enough to return different mocks based on input
22+
def get_service_side_effect(service_name, version=None): # Added version to match signature
23+
if service_name == "GoogleAdsService":
24+
return mock_ga_service
25+
elif service_name == "AdGroupService":
26+
return mock_ad_group_service
27+
elif service_name == "CampaignService":
28+
mock_campaign_service = MagicMock()
29+
# Configure campaign_path mock
30+
mock_campaign_service.campaign_path.return_value = f"customers/{mock_args.customer_id}/campaigns/{mock_args.campaign_id}"
31+
return mock_campaign_service
32+
raise ValueError(f"Unexpected service: {service_name}")
33+
mock_google_ads_client.get_service.side_effect = get_service_side_effect
34+
35+
36+
# Mock command line arguments
37+
mock_args = MagicMock()
38+
mock_args.customer_id = "1234567890"
39+
mock_args.campaign_id = "9876543210"
40+
mock_argument_parser.return_value.parse_args.return_value = mock_args
41+
42+
# Mock the responses for service calls
43+
# For AdGroupService mutate
44+
mock_ad_group_operation_response = MagicMock()
45+
# Simulate that one ad group was created
46+
mock_ad_group_result = MagicMock()
47+
mock_ad_group_result.resource_name = "customers/1234567890/adGroups/AD_GROUP_ID_1"
48+
mock_ad_group_operation_response.results = [mock_ad_group_result]
49+
mock_ad_group_service.mutate_ad_groups.return_value = mock_ad_group_operation_response
50+
51+
# Call the main function of the example script
52+
with patch("builtins.print") as mock_print:
53+
add_ad_groups.main(mock_google_ads_client, mock_args.customer_id, mock_args.campaign_id)
54+
55+
# Assertions
56+
# mock_load_from_storage.assert_called_once_with(version="v19") # Removed this line
57+
mock_google_ads_client.get_service.assert_any_call("AdGroupService")
58+
mock_google_ads_client.get_service.assert_any_call("CampaignService")
59+
60+
61+
self.assertEqual(mock_ad_group_service.mutate_ad_groups.call_count, 1)
62+
# Check the first argument of the first call to mutate_ad_groups
63+
args, kwargs = mock_ad_group_service.mutate_ad_groups.call_args
64+
self.assertEqual(kwargs['customer_id'], "1234567890")
65+
operations = kwargs['operations']
66+
self.assertEqual(len(operations), 1) # Expecting one ad group operation
67+
68+
# Check the created ad group operation
69+
created_ad_group = operations[0].create
70+
# The name is dynamic with uuid, so we can't assert exact name.
71+
# We can check if it starts with the expected prefix.
72+
self.assertTrue(created_ad_group.name.startswith("Earth to Mars cruises "))
73+
self.assertEqual(created_ad_group.status, mock_google_ads_client.enums.AdGroupStatusEnum.ENABLED)
74+
self.assertEqual(created_ad_group.campaign, "customers/1234567890/campaigns/9876543210")
75+
self.assertEqual(created_ad_group.type_, mock_google_ads_client.enums.AdGroupTypeEnum.SEARCH_STANDARD)
76+
self.assertEqual(created_ad_group.cpc_bid_micros, 10000000)
77+
78+
79+
# Verify print output
80+
expected_print_calls = [
81+
call(f"Created ad group customers/1234567890/adGroups/AD_GROUP_ID_1.")
82+
]
83+
mock_print.assert_has_calls(expected_print_calls, any_order=False)
84+
85+
if __name__ == "__main__":
86+
unittest.main()
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock, call
3+
4+
from examples.basic_operations import add_campaigns
5+
6+
class TestAddCampaigns(unittest.TestCase):
7+
8+
@patch("examples.basic_operations.add_campaigns.argparse.ArgumentParser")
9+
@patch("examples.basic_operations.add_campaigns.GoogleAdsClient.load_from_storage")
10+
def test_main(self, mock_load_from_storage, mock_argument_parser):
11+
# Mock the GoogleAdsClient
12+
mock_google_ads_client = MagicMock()
13+
mock_load_from_storage.return_value = mock_google_ads_client
14+
15+
# Mock the CampaignService
16+
mock_campaign_service = MagicMock()
17+
# Mock the BudgetService (even if not directly used in main, it might be called by helpers)
18+
mock_campaign_budget_service = MagicMock()
19+
20+
def get_service_side_effect(service_name, version=None):
21+
if service_name == "CampaignService":
22+
return mock_campaign_service
23+
elif service_name == "CampaignBudgetService":
24+
return mock_campaign_budget_service
25+
# Add other services if the example uses them
26+
raise ValueError(f"Unexpected service: {service_name}")
27+
mock_google_ads_client.get_service.side_effect = get_service_side_effect
28+
29+
# Mock command line arguments
30+
mock_args = MagicMock()
31+
mock_args.customer_id = "1234567890"
32+
# mock_args.number_of_campaigns = 1 # This argument is not used by main
33+
34+
mock_argument_parser.return_value.parse_args.return_value = mock_args
35+
36+
# Mock the responses for service calls
37+
# For CampaignBudgetService mutate (if we were testing budget creation)
38+
mock_budget_operation_response = MagicMock()
39+
mock_budget_result = MagicMock()
40+
mock_budget_result.resource_name = "customers/1234567890/campaignBudgets/BUDGET_ID_1"
41+
mock_budget_operation_response.results = [mock_budget_result]
42+
mock_campaign_budget_service.mutate_campaign_budgets.return_value = mock_budget_operation_response
43+
44+
# For CampaignService mutate
45+
mock_campaign_operation_response = MagicMock()
46+
mock_campaign_result = MagicMock()
47+
mock_campaign_result.resource_name = "customers/1234567890/campaigns/CAMPAIGN_ID_1"
48+
mock_campaign_operation_response.results = [mock_campaign_result]
49+
# Configure mutate_campaigns to return a list of results if multiple operations are sent
50+
mock_campaign_service.mutate_campaigns.return_value = mock_campaign_operation_response
51+
52+
# Mock enums
53+
mock_google_ads_client.enums.CampaignStatusEnum.PAUSED = "PAUSED"
54+
mock_google_ads_client.enums.AdvertisingChannelTypeEnum.SEARCH = "SEARCH"
55+
mock_google_ads_client.enums.BiddingStrategyTypeEnum.MANUAL_CPC = "MANUAL_CPC"
56+
mock_google_ads_client.enums.BudgetDeliveryMethodEnum.STANDARD = "STANDARD"
57+
58+
59+
# Call the main function of the example script
60+
with patch("builtins.print") as mock_print:
61+
add_campaigns.main(mock_google_ads_client, mock_args.customer_id)
62+
63+
# Assertions
64+
# mock_load_from_storage.assert_called_once_with(version="v19") # main doesn't call this directly
65+
mock_google_ads_client.get_service.assert_any_call("CampaignService")
66+
mock_google_ads_client.get_service.assert_any_call("CampaignBudgetService")
67+
68+
# Ensure CampaignBudgetService.mutate_campaign_budgets was called
69+
mock_campaign_budget_service.mutate_campaign_budgets.assert_called_once()
70+
budget_args, budget_kwargs = mock_campaign_budget_service.mutate_campaign_budgets.call_args
71+
self.assertEqual(budget_kwargs['customer_id'], mock_args.customer_id)
72+
self.assertEqual(len(budget_kwargs['operations']), 1)
73+
# Add more assertions for budget operation if needed
74+
75+
self.assertEqual(mock_campaign_service.mutate_campaigns.call_count, 1)
76+
args_list, kwargs_list = mock_campaign_service.mutate_campaigns.call_args
77+
self.assertEqual(kwargs_list['customer_id'], "1234567890")
78+
operations = kwargs_list['operations']
79+
self.assertEqual(len(operations), 1) # Script creates one campaign
80+
81+
# Check properties of the campaign operation
82+
campaign_op = operations[0].create
83+
self.assertTrue(campaign_op.name.startswith("Interplanetary Cruise ")) # Adjusted prefix
84+
self.assertEqual(campaign_op.status, mock_google_ads_client.enums.CampaignStatusEnum.PAUSED)
85+
self.assertEqual(campaign_op.advertising_channel_type, mock_google_ads_client.enums.AdvertisingChannelTypeEnum.SEARCH)
86+
# In the actual script, manual_cpc is initialized but enhanced_cpc_enabled is not explicitly set.
87+
# We should check what is actually being set.
88+
# self.assertEqual(campaign_op.manual_cpc.enhanced_cpc_enabled, True)
89+
self.assertTrue(hasattr(campaign_op, 'manual_cpc')) # Check if manual_cpc is set
90+
self.assertEqual(campaign_op.campaign_budget, "customers/1234567890/campaignBudgets/BUDGET_ID_1")
91+
92+
93+
# Verify print output
94+
expected_print_calls = [
95+
call(f"Created campaign customers/1234567890/campaigns/CAMPAIGN_ID_1.")
96+
]
97+
mock_print.assert_has_calls(expected_print_calls, any_order=False)
98+
99+
if __name__ == "__main__":
100+
unittest.main()
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock, call
3+
4+
from examples.basic_operations import get_campaigns
5+
6+
class TestGetCampaigns(unittest.TestCase):
7+
8+
@patch("examples.basic_operations.get_campaigns.argparse.ArgumentParser")
9+
@patch("examples.basic_operations.get_campaigns.GoogleAdsClient.load_from_storage")
10+
def test_main(self, mock_load_from_storage, mock_argument_parser):
11+
# Mock the GoogleAdsClient
12+
mock_google_ads_client = MagicMock()
13+
mock_load_from_storage.return_value = mock_google_ads_client
14+
15+
# Mock the GoogleAdsService
16+
mock_ga_service = MagicMock()
17+
mock_google_ads_client.get_service.return_value = mock_ga_service
18+
19+
# Mock command line arguments
20+
mock_args = MagicMock()
21+
mock_args.customer_id = "1234567890"
22+
mock_argument_parser.return_value.parse_args.return_value = mock_args
23+
24+
# Mock the response from search_stream
25+
mock_row1 = MagicMock()
26+
mock_row1.campaign.id = "CAMPAIGN_ID_1"
27+
mock_row1.campaign.name = "Test Campaign 1"
28+
29+
mock_row2 = MagicMock()
30+
mock_row2.campaign.id = "CAMPAIGN_ID_2"
31+
mock_row2.campaign.name = "Test Campaign 2"
32+
33+
mock_batch = MagicMock()
34+
mock_batch.results = [mock_row1, mock_row2]
35+
36+
# search_stream returns an iterable of batches
37+
mock_ga_service.search_stream.return_value = [mock_batch]
38+
39+
# Call the main function of the example script
40+
with patch("builtins.print") as mock_print:
41+
get_campaigns.main(mock_google_ads_client, mock_args.customer_id)
42+
43+
# Assertions
44+
# mock_load_from_storage.assert_called_once_with(version="v19") # main doesn't call this
45+
mock_google_ads_client.get_service.assert_called_once_with("GoogleAdsService")
46+
47+
expected_query = """
48+
SELECT
49+
campaign.id,
50+
campaign.name
51+
FROM campaign
52+
ORDER BY campaign.id"""
53+
mock_ga_service.search_stream.assert_called_once_with(
54+
customer_id=mock_args.customer_id, query=expected_query
55+
)
56+
57+
# Verify print output
58+
expected_print_calls = [
59+
call(f"Campaign with ID {mock_row1.campaign.id} and name \"{mock_row1.campaign.name}\" was found."),
60+
call(f"Campaign with ID {mock_row2.campaign.id} and name \"{mock_row2.campaign.name}\" was found.")
61+
]
62+
mock_print.assert_has_calls(expected_print_calls, any_order=False)
63+
64+
if __name__ == "__main__":
65+
unittest.main()
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock, call
3+
4+
from examples.basic_operations import get_responsive_search_ads
5+
6+
class TestGetResponsiveSearchAds(unittest.TestCase):
7+
8+
@patch("examples.basic_operations.get_responsive_search_ads.argparse.ArgumentParser")
9+
@patch("examples.basic_operations.get_responsive_search_ads.GoogleAdsClient.load_from_storage")
10+
def test_main(self, mock_load_from_storage, mock_argument_parser):
11+
# Mock the GoogleAdsClient
12+
mock_google_ads_client = MagicMock()
13+
mock_load_from_storage.return_value = mock_google_ads_client
14+
15+
# Mock the GoogleAdsService
16+
mock_ga_service = MagicMock()
17+
mock_google_ads_client.get_service.return_value = mock_ga_service
18+
19+
# Mock command line arguments
20+
mock_args = MagicMock()
21+
mock_args.customer_id = "1234567890"
22+
mock_args.ad_group_id = "ADGROUPID_01" # Optional, so test with it present
23+
mock_argument_parser.return_value.parse_args.return_value = mock_args
24+
25+
# Mock the response from search
26+
# This is a row from the SearchGoogleAdsResponse, not AdGroupAd directly
27+
mock_row1 = MagicMock()
28+
mock_ad_group_ad = mock_row1.ad_group_ad # This is the AdGroupAd object
29+
30+
mock_ad_group_ad.ad.resource_name = "customers/1234567890/ads/AD_ID_1"
31+
mock_ad_group_ad.status.name = "ENABLED" # Example status
32+
33+
# RSA has headlines and descriptions (AdTextAsset)
34+
mock_headline1 = MagicMock()
35+
mock_headline1.text = "Cruise to Mars"
36+
mock_headline1.pinned_field.name = "HEADLINE_1" # Script uses asset.pinned_field.name
37+
38+
mock_description1 = MagicMock()
39+
mock_description1.text = "Visit the Red Planet in style."
40+
mock_description1.pinned_field.name = "DESCRIPTION_1"
41+
42+
mock_ad_group_ad.ad.responsive_search_ad.headlines = [mock_headline1]
43+
mock_ad_group_ad.ad.responsive_search_ad.descriptions = [mock_description1]
44+
# The script doesn't print final_urls for RSA, it prints headlines and descriptions
45+
46+
# search returns an iterable of these rows
47+
mock_ga_service.search.return_value = [mock_row1]
48+
49+
# Enums are compared by their .name attribute by the script, so no need to mock them as strings here
50+
# if the attributes being accessed on them (like .name) are already strings or MagicMocks.
51+
# The script uses ad.responsive_search_ad.headlines which are AdTextAssets.
52+
# ad_text_assets_to_strs accesses asset.text and asset.pinned_field.name.
53+
54+
55+
# Call the main function of the example script
56+
with patch("builtins.print") as mock_print:
57+
get_responsive_search_ads.main(mock_google_ads_client, mock_args.customer_id, mock_args.ad_group_id)
58+
59+
# Assertions
60+
mock_google_ads_client.get_service.assert_called_once_with("GoogleAdsService")
61+
62+
# Check that search was called
63+
mock_ga_service.search.assert_called_once()
64+
call_args = mock_ga_service.search.call_args
65+
66+
# The first argument to search is the request object
67+
request_arg = call_args[1]['request'] # request is a keyword argument
68+
self.assertEqual(request_arg.customer_id, mock_args.customer_id)
69+
# Verify query construction based on ad_group_id presence
70+
self.assertIn(f"AND ad_group.id = {mock_args.ad_group_id}", request_arg.query)
71+
72+
73+
# Verify print output - this will be complex due to loops and formatting
74+
# We'll check for key pieces of information.
75+
# Note: The exact formatting of pinned fields might require more specific mocking if it's complex.
76+
# This example assumes simple text output for headlines and descriptions.
77+
78+
# Expected calls based on the mocked ad structure
79+
# The script's print format is:
80+
# print(f"Responsive search ad with resource name \"{ad.resource_name}\", status {row.ad_group_ad.status.name} was found.")
81+
# print(f"Headlines:\n{headlines_str}\nDescriptions:\n{descriptions_str}\n")
82+
83+
expected_ad_info_print = call(
84+
f"Responsive search ad with resource name \"{mock_ad_group_ad.ad.resource_name}\", status {mock_ad_group_ad.status.name} was found."
85+
)
86+
87+
# Output from ad_text_assets_to_strs
88+
expected_headline_str = f"\t {mock_headline1.text} pinned to {mock_headline1.pinned_field.name}"
89+
expected_description_str = f"\t {mock_description1.text} pinned to {mock_description1.pinned_field.name}"
90+
91+
expected_details_print = call(f"Headlines:\n{expected_headline_str}\nDescriptions:\n{expected_description_str}\n")
92+
93+
expected_calls = [expected_ad_info_print, expected_details_print]
94+
95+
mock_print.assert_has_calls(expected_calls, any_order=False)
96+
97+
98+
@patch("examples.basic_operations.get_responsive_search_ads.argparse.ArgumentParser")
99+
@patch("examples.basic_operations.get_responsive_search_ads.GoogleAdsClient.load_from_storage")
100+
def test_main_no_ad_group_id(self, mock_load_from_storage, mock_argument_parser):
101+
# Test the case where ad_group_id is None
102+
mock_google_ads_client = MagicMock()
103+
mock_load_from_storage.return_value = mock_google_ads_client
104+
mock_ga_service = MagicMock()
105+
mock_google_ads_client.get_service.return_value = mock_ga_service
106+
107+
mock_args = MagicMock()
108+
mock_args.customer_id = "1234567890"
109+
mock_args.ad_group_id = None # Test this path
110+
mock_argument_parser.return_value.parse_args.return_value = mock_args
111+
112+
mock_ga_service.search.return_value = [] # No results needed for this query check path
113+
114+
with patch("builtins.print"): # Suppress print for this test
115+
get_responsive_search_ads.main(mock_google_ads_client, mock_args.customer_id, mock_args.ad_group_id)
116+
117+
mock_ga_service.search.assert_called_once()
118+
request_arg = mock_ga_service.search.call_args[1]['request']
119+
self.assertNotIn("AND ad_group.id", request_arg.query)
120+
121+
122+
if __name__ == "__main__":
123+
unittest.main()

0 commit comments

Comments
 (0)