Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* 1.4.1
- Fixed minor bugs in tests for api_examples.
- Added test api_examples/tests/test_get_conversion_upload_summary.py

* 1.4.0
- Modified ChangeLog so changes are listed in reverse chronological order.
- Added step_by_step custom command.
Expand Down
8 changes: 3 additions & 5 deletions api_examples/remove_automatically_created_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@

from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.v22.enums.asset_field_type_enum.asset_field_type import (
AssetFieldTypeEnum,
)
from google.ads.googleads.v22.enums import AssetFieldTypeEnum


def main(
Expand Down Expand Up @@ -54,11 +52,11 @@ def main(
# removing (e.g., TEXT, IMAGE, VIDEO).

try:
field_type_enum = getattr(AssetFieldTypeEnum, field_type.upper())
field_type_enum = getattr(AssetFieldTypeEnum.AssetFieldType, field_type.upper())
except AttributeError:
print(
f"Error: Invalid field type '{field_type}'. "
f"Please use one of: {[e.name for e in AssetFieldTypeEnum if e.name not in ('UNSPECIFIED', 'UNKNOWN')]}"
f"Please use one of: {[e.name for e in AssetFieldTypeEnum.AssetFieldType if e.name not in ('UNSPECIFIED', 'UNKNOWN')]}"
)
sys.exit(1)

Expand Down
107 changes: 107 additions & 0 deletions api_examples/tests/test_get_conversion_upload_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import os
import unittest
from unittest.mock import MagicMock, call
from io import StringIO

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../")))

from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.client import GoogleAdsClient
from api_examples.get_conversion_upload_summary import main


class TestGetConversionUploadSummary(unittest.TestCase):
def setUp(self):
self.mock_client = MagicMock(spec=GoogleAdsClient)
self.mock_ga_service = MagicMock()
self.mock_client.get_service.return_value = self.mock_ga_service
self.customer_id = "1234567890"

self.captured_output = StringIO()
sys.stdout = self.captured_output

def tearDown(self):
sys.stdout = sys.__stdout__

def test_main_success(self):
# Mock responses for search_stream
mock_batch_1 = MagicMock()
mock_row_1 = MagicMock()
mock_summary_1 = MagicMock()
mock_summary_1.resource_name = "customers/123/offlineConversionUploadClientSummaries/1"
mock_summary_1.status.name = "SUCCESS"
mock_summary_1.total_event_count = 10
mock_summary_1.successful_event_count = 10
mock_summary_1.success_rate = 1.0
mock_summary_1.last_upload_date_time = "2024-01-01 12:00:00"
mock_summary_1.alerts = []
mock_summary_1.daily_summaries = []
mock_summary_1.job_summaries = []
mock_row_1.offline_conversion_upload_client_summary = mock_summary_1
mock_batch_1.results = [mock_row_1]

mock_batch_2 = MagicMock()
mock_row_2 = MagicMock()
mock_summary_2 = MagicMock()
mock_summary_2.resource_name = "customers/123/offlineConversionUploadConversionActionSummaries/1"
mock_summary_2.conversion_action_name = "My Conversion Action"
mock_summary_2.status.name = "SUCCESS"
mock_summary_2.total_event_count = 5
mock_summary_2.successful_event_count = 5
mock_summary_2.alerts = []
mock_summary_2.daily_summaries = []
mock_summary_2.job_summaries = []
mock_row_2.offline_conversion_upload_conversion_action_summary = mock_summary_2
mock_batch_2.results = [mock_row_2]

# The first call returns client summary, second call returns conversion action summary
self.mock_ga_service.search_stream.side_effect = [[mock_batch_1], [mock_batch_2]]

main(self.mock_client, self.customer_id)

# Check output
output = self.captured_output.getvalue()
self.assertIn("Offline Conversion Upload Client Summary:", output)
self.assertIn("Resource Name: customers/123/offlineConversionUploadClientSummaries/1", output)
self.assertIn("Offline Conversion Upload Conversion Action Summary:", output)
self.assertIn("Conversion Action Name: My Conversion Action", output)

self.assertEqual(self.mock_ga_service.search_stream.call_count, 2)

def test_main_google_ads_exception(self):
mock_error = MagicMock()
mock_error.code.return_value.name = "INTERNAL_ERROR"
mock_failure = MagicMock()
mock_failure.errors = [MagicMock(message="Internal error")]

self.mock_ga_service.search_stream.side_effect = GoogleAdsException(
error=mock_error,
call=MagicMock(),
failure=mock_failure,
request_id="test_request_id"
)

with self.assertRaises(SystemExit) as cm:
main(self.mock_client, self.customer_id)

self.assertEqual(cm.exception.code, 1)
output = self.captured_output.getvalue()
self.assertIn('Request with ID "test_request_id" failed with status "INTERNAL_ERROR"', output)

if __name__ == "__main__":
unittest.main()
13 changes: 7 additions & 6 deletions api_examples/tests/test_remove_automatically_created_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ def setUp(self):

self.mock_client.get_service.side_effect = self._get_mock_service

self.patcher = unittest.mock.patch(
"api_examples.remove_automatically_created_assets.AssetFieldTypeEnum"
)
self.mock_asset_field_type_enum = self.patcher.start()

class MockAssetFieldType:
UNSPECIFIED = MagicMock()
UNSPECIFIED.name = "UNSPECIFIED"
Expand Down Expand Up @@ -67,13 +72,8 @@ def __iter__(self):
]
)

self.mock_real_asset_field_type = MockAssetFieldType()
self.mock_asset_field_type_enum.AssetFieldType = MockAssetFieldType()

self.mock_client.enums = MagicMock()
self.mock_client.enums.AssetFieldTypeEnum = MagicMock()
self.mock_client.enums.AssetFieldTypeEnum.AssetFieldType = (
self.mock_real_asset_field_type
)

self.customer_id = "1234567890"
self.campaign_id = 12345
Expand All @@ -92,6 +92,7 @@ def _get_mock_service(self, service_name):

def tearDown(self):
sys.stdout = sys.__stdout__
self.patcher.stop()

def test_main_successful_removal(self):
mock_response = MagicMock()
Expand Down