Skip to content

Commit 6a0b3ff

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 6a0b3ff

File tree

8 files changed

+538
-0
lines changed

8 files changed

+538
-0
lines changed

examples/planning/tests/.keep

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# This file is intentionally left blank.
2+
# It is used to ensure that the 'tests' directory is tracked by Git.

examples/planning/tests/__init__.py

Whitespace-only changes.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import unittest
2+
from unittest.mock import MagicMock, patch
3+
4+
# Assuming forecast_reach.py is structured to be importable and its main logic is in functions.
5+
# We might need to adjust this if the script's structure is different.
6+
# For example, if main() directly calls other functions, we might patch those.
7+
from examples.planning import forecast_reach
8+
9+
class TestForecastReach(unittest.TestCase):
10+
11+
@patch('examples.planning.forecast_reach.GoogleAdsClient.load_from_storage')
12+
def test_main_forecast_reach(self, mock_load_from_storage):
13+
# Mock the GoogleAdsClient and its services
14+
mock_google_ads_client = MagicMock()
15+
mock_load_from_storage.return_value = mock_google_ads_client
16+
17+
# Mock the ReachPlanService
18+
mock_reach_plan_service = MagicMock()
19+
mock_google_ads_client.get_service.return_value = mock_reach_plan_service
20+
21+
# Mock the responses from ReachPlanService
22+
# list_plannable_locations response
23+
mock_plannable_locations_response = MagicMock()
24+
mock_plannable_locations_response.plannable_locations = [
25+
MagicMock(name="Location1", id="123", parent_country_id="321"),
26+
MagicMock(name="Location2", id="456", parent_country_id="654")
27+
]
28+
# list_plannable_products response
29+
mock_plannable_products_response = MagicMock()
30+
mock_product_metadata = MagicMock()
31+
mock_product_metadata.plannable_product_code = "YOUTUBE_REACH"
32+
mock_product_metadata.plannable_product_name = "YouTube Reach"
33+
mock_product_metadata.plannable_targeting.age_ranges = [MagicMock(name="AGE_RANGE_18_24")]
34+
mock_product_metadata.plannable_targeting.genders = [MagicMock(type_=MagicMock(name="GENDER_FEMALE"))]
35+
mock_product_metadata.plannable_targeting.devices = [MagicMock(type_=MagicMock(name="DEVICE_MOBILE"))]
36+
mock_plannable_products_response.product_metadata = [mock_product_metadata]
37+
# generate_reach_forecast response
38+
mock_reach_forecast_response = MagicMock()
39+
mock_reach_curve = MagicMock()
40+
mock_forecast_point = MagicMock()
41+
mock_forecast_point.cost_micros = 10000000 # e.g., 10 units of currency
42+
mock_forecast_point.forecast.on_target_reach = 100000
43+
mock_forecast_point.forecast.on_target_impressions = 150000
44+
mock_forecast_point.forecast.total_reach = 120000
45+
mock_forecast_point.forecast.total_impressions = 180000
46+
mock_planned_product_reach_forecast = MagicMock()
47+
mock_planned_product_reach_forecast.plannable_product_code = "YOUTUBE_REACH"
48+
mock_planned_product_reach_forecast.cost_micros = 10000000
49+
mock_forecast_point.planned_product_reach_forecasts = [mock_planned_product_reach_forecast]
50+
mock_reach_curve.reach_forecasts = [mock_forecast_point]
51+
mock_reach_forecast_response.reach_curve = mock_reach_curve
52+
53+
# Configure side_effects or return_values for service calls
54+
mock_reach_plan_service.list_plannable_locations.return_value = mock_plannable_locations_response
55+
mock_reach_plan_service.list_plannable_products.return_value = mock_plannable_products_response
56+
mock_reach_plan_service.generate_reach_forecast.return_value = mock_reach_forecast_response
57+
58+
# Mock enums (this might be more complex depending on actual usage)
59+
mock_enums = MagicMock()
60+
mock_enums.ReachPlanAgeRangeEnum.AGE_RANGE_18_65_UP = "AGE_RANGE_18_65_UP"
61+
mock_enums.GenderTypeEnum.FEMALE = "FEMALE"
62+
mock_enums.GenderTypeEnum.MALE = "MALE"
63+
mock_enums.DeviceEnum.DESKTOP = "DESKTOP"
64+
mock_enums.DeviceEnum.MOBILE = "MOBILE"
65+
mock_enums.DeviceEnum.TABLET = "TABLET"
66+
mock_google_ads_client.enums = mock_enums
67+
68+
# Call the main function of forecast_reach.py
69+
# We need a customer_id for this test
70+
test_customer_id = "1234567890"
71+
72+
# Capture stdout to check output
73+
with patch('sys.stdout', new_callable=MagicMock) as mock_stdout:
74+
forecast_reach.main(mock_google_ads_client, test_customer_id)
75+
76+
# Assert that the main logic was called (e.g., services were used)
77+
# mock_load_from_storage.assert_called_once_with(version="v19") # This is removed as client is injected
78+
mock_google_ads_client.get_service.assert_any_call("ReachPlanService")
79+
80+
# Assert that the service methods were called
81+
mock_reach_plan_service.list_plannable_locations.assert_called_once()
82+
mock_reach_plan_service.list_plannable_products.assert_called_once()
83+
# generate_reach_forecast is called by request_reach_curve, which is called by forecast_manual_mix
84+
mock_reach_plan_service.generate_reach_forecast.assert_called_once()
85+
86+
# Example assertion on output (very basic, might need more specific checks)
87+
# This checks if "Plannable Locations" was printed by show_plannable_locations
88+
self.assertTrue(any("Plannable Locations" in call_args[0][0] for call_args in mock_stdout.write.call_args_list if call_args[0]))
89+
# This checks if "Plannable Products for Location ID" was printed by show_plannable_products
90+
self.assertTrue(any("Plannable Products for Location ID" in call_args[0][0] for call_args in mock_stdout.write.call_args_list if call_args[0]))
91+
# This checks if "Currency, Cost, On-Target Reach" header was printed by request_reach_curve
92+
self.assertTrue(any("Currency, Cost, On-Target Reach" in call_args[0][0] for call_args in mock_stdout.write.call_args_list if call_args[0]))
93+
94+
95+
if __name__ == "__main__":
96+
unittest.main()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import unittest
2+
from unittest.mock import MagicMock, patch
3+
from datetime import datetime, timedelta
4+
5+
from examples.planning import generate_forecast_metrics
6+
7+
class TestGenerateForecastMetrics(unittest.TestCase):
8+
9+
@patch('examples.planning.generate_forecast_metrics.GoogleAdsClient.load_from_storage')
10+
def test_main_generate_forecast_metrics(self, mock_load_from_storage):
11+
mock_google_ads_client = MagicMock()
12+
mock_load_from_storage.return_value = mock_google_ads_client
13+
14+
mock_keyword_plan_idea_service = MagicMock()
15+
mock_google_ads_service = MagicMock()
16+
17+
# Configure get_service to return the correct mock based on the service name
18+
def get_service_side_effect(service_name, version=None):
19+
if service_name == "KeywordPlanIdeaService":
20+
return mock_keyword_plan_idea_service
21+
elif service_name == "GoogleAdsService":
22+
return mock_google_ads_service
23+
return MagicMock() # Default mock for other services if any
24+
25+
mock_google_ads_client.get_service.side_effect = get_service_side_effect
26+
27+
# Mock responses
28+
mock_forecast_response = MagicMock()
29+
mock_forecast_response.campaign_forecast_metrics.clicks = 100.0
30+
mock_forecast_response.campaign_forecast_metrics.impressions = 1000.0
31+
mock_forecast_response.campaign_forecast_metrics.average_cpc_micros = 1230000
32+
mock_keyword_plan_idea_service.generate_keyword_forecast_metrics.return_value = mock_forecast_response
33+
34+
# Mock path methods
35+
mock_google_ads_service.geo_target_constant_path.return_value = "geoTargetConstants/2840"
36+
mock_google_ads_service.language_constant_path.return_value = "languageConstants/1000"
37+
38+
# Mock enums (adjust as per actual usage in the script)
39+
mock_enums = MagicMock()
40+
mock_enums.KeywordPlanNetworkEnum.GOOGLE_SEARCH = "GOOGLE_SEARCH"
41+
mock_enums.KeywordMatchTypeEnum.BROAD = "BROAD"
42+
mock_enums.KeywordMatchTypeEnum.PHRASE = "PHRASE"
43+
mock_enums.KeywordMatchTypeEnum.EXACT = "EXACT"
44+
mock_google_ads_client.enums = mock_enums
45+
46+
# Mock types for request construction
47+
mock_google_ads_client.get_type.side_effect = lambda name: MagicMock(name=name)
48+
49+
50+
test_customer_id = "1234567890"
51+
52+
with patch('sys.stdout', new_callable=MagicMock) as mock_stdout:
53+
generate_forecast_metrics.main(mock_google_ads_client, test_customer_id)
54+
55+
# mock_load_from_storage.assert_called_once_with(version="v19") # This is removed as client is injected
56+
mock_google_ads_client.get_service.assert_any_call("KeywordPlanIdeaService")
57+
mock_google_ads_client.get_service.assert_any_call("GoogleAdsService")
58+
mock_keyword_plan_idea_service.generate_keyword_forecast_metrics.assert_called_once()
59+
60+
# Check if the output contains expected metrics
61+
output = "".join(call_args[0][0] for call_args in mock_stdout.write.call_args_list if call_args[0])
62+
self.assertIn("Estimated daily clicks: 100.0", output)
63+
self.assertIn("Estimated daily impressions: 1000.0", output)
64+
self.assertIn("Estimated daily average CPC: 1230000", output)
65+
66+
if __name__ == "__main__":
67+
unittest.main()
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import unittest
2+
from unittest.mock import MagicMock, patch
3+
4+
from examples.planning import generate_historical_metrics
5+
6+
class TestGenerateHistoricalMetrics(unittest.TestCase):
7+
8+
@patch('examples.planning.generate_historical_metrics.GoogleAdsClient.load_from_storage')
9+
def test_main_generate_historical_metrics(self, mock_load_from_storage):
10+
mock_google_ads_client = MagicMock()
11+
mock_load_from_storage.return_value = mock_google_ads_client
12+
13+
mock_keyword_plan_idea_service = MagicMock()
14+
mock_google_ads_service = MagicMock()
15+
16+
def get_service_side_effect(service_name, version=None):
17+
if service_name == "KeywordPlanIdeaService":
18+
return mock_keyword_plan_idea_service
19+
elif service_name == "GoogleAdsService":
20+
return mock_google_ads_service
21+
return MagicMock()
22+
23+
mock_google_ads_client.get_service.side_effect = get_service_side_effect
24+
25+
# Mock responses
26+
mock_historical_response = MagicMock()
27+
mock_result = MagicMock()
28+
mock_result.text = "mars cruise"
29+
mock_result.close_variants = ["mars ship", "mars journey"]
30+
mock_result.keyword_metrics.avg_monthly_searches = 1000
31+
mock_result.keyword_metrics.competition = "HIGH" # This would be an enum actually
32+
mock_result.keyword_metrics.competition_index = 80
33+
mock_result.keyword_metrics.low_top_of_page_bid_micros = 1000000
34+
mock_result.keyword_metrics.high_top_of_page_bid_micros = 5000000
35+
36+
mock_monthly_search_volume = MagicMock()
37+
mock_monthly_search_volume.year = 2023
38+
mock_month_enum = MagicMock()
39+
mock_month_enum.name = "JANUARY" # Simulate enum's .name attribute
40+
mock_monthly_search_volume.month = mock_month_enum
41+
mock_monthly_search_volume.monthly_searches = 1200
42+
mock_result.keyword_metrics.monthly_search_volumes = [mock_monthly_search_volume]
43+
44+
mock_historical_response.results = [mock_result]
45+
mock_keyword_plan_idea_service.generate_keyword_historical_metrics.return_value = mock_historical_response
46+
47+
# Mock path methods
48+
mock_google_ads_service.geo_target_constant_path.return_value = "geoTargetConstants/2840"
49+
mock_google_ads_service.language_constant_path.return_value = "languageConstants/1000"
50+
51+
# Mock enums
52+
mock_enums = MagicMock()
53+
mock_enums.KeywordPlanNetworkEnum.GOOGLE_SEARCH = "GOOGLE_SEARCH"
54+
# Assuming competition is an enum, e.g., KeywordPlanCompetitionLevelEnum
55+
mock_enums.KeywordPlanCompetitionLevelEnum.HIGH = "HIGH"
56+
mock_google_ads_client.enums = mock_enums
57+
58+
# Mock types for request construction
59+
mock_google_ads_client.get_type.side_effect = lambda name: MagicMock(name=name)
60+
61+
test_customer_id = "1234567890"
62+
63+
with patch('sys.stdout', new_callable=MagicMock) as mock_stdout:
64+
generate_historical_metrics.main(mock_google_ads_client, test_customer_id)
65+
66+
# mock_load_from_storage.assert_called_once_with(version="v19") # This is removed as client is injected
67+
mock_google_ads_client.get_service.assert_any_call("KeywordPlanIdeaService")
68+
mock_google_ads_client.get_service.assert_any_call("GoogleAdsService")
69+
mock_keyword_plan_idea_service.generate_keyword_historical_metrics.assert_called_once()
70+
71+
output = "".join(call_args[0][0] for call_args in mock_stdout.write.call_args_list if call_args[0])
72+
self.assertIn("The search query 'mars cruise'", output)
73+
self.assertIn("Approximate monthly searches: 1000", output)
74+
self.assertIn("Competition level: HIGH", output) # This will depend on how enum is converted to string
75+
self.assertIn("Approximately 1200 searches in JANUARY, 2023", output)
76+
77+
78+
if __name__ == "__main__":
79+
unittest.main()

0 commit comments

Comments
 (0)