import traceback from collections.abc import Iterator import httpx import pytest import respx from dmp.adapters.tiktok import TikTokClient from dmp.adapters.tiktok.models import ( AccessToken, AdAccount, BusinessCenter, BusinessCenterInfo, Campaign, ListData, PageInfo, Response, User, ) from tests.unit.faker import FakerTyped from tests.unit.types import BuildModel class TestTikTokClient: @pytest.fixture def client(self) -> Iterator[TikTokClient]: with TikTokClient( client_id="client", client_secret="secret", ) as client: yield client def test_get_user( self, client: TikTokClient, respx_mock: respx.MockRouter, build_model: BuildModel, fake: FakerTyped, ) -> None: user = build_model(User) response = build_model(Response, code=0, data=user) user_access_token = fake.plain_token() respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/user/info/", headers={ "Access-Token": user_access_token, }, ).mock(return_value=httpx.Response(status_code=200, json=response.model_dump())) result = client.get_user( access_token=user_access_token, ) assert result == user def test_get_oauth_access_token( self, client: TikTokClient, respx_mock: respx.MockRouter, build_model: BuildModel, fake: FakerTyped, ) -> None: access_token = build_model(AccessToken) response = build_model(Response, code=0, data=access_token) auth_code = fake.plain_token() respx_mock.post( "https://business-api.tiktok.com/open_api/v1.3/oauth2/access_token/", json={ "app_id": client.client_id, "secret": client.client_secret, "auth_code": auth_code, }, ).mock(return_value=httpx.Response(status_code=200, json=response.model_dump())) result = client.get_oauth_access_token( tmp_token=auth_code, ) assert result == access_token def test_get_ad_accounts_details( self, client: TikTokClient, respx_mock: respx.MockRouter, build_model: BuildModel, fake: FakerTyped, ) -> None: user_access_token = fake.plain_token() ad_account1 = build_model(AdAccount) ad_account2 = build_model(AdAccount) list_data = build_model(ListData, list=[ad_account1, ad_account2]) response = build_model(Response, code=0, data=list_data) respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/advertiser/info/", headers={ "Access-Token": user_access_token, }, params={ "advertiser_ids": f'["{ad_account1.advertiser_id}","{ad_account2.advertiser_id}"]' }, ).mock(return_value=httpx.Response(status_code=200, json=response.model_dump())) result = client.get_ad_accounts_details( access_token=user_access_token, advertiser_ids=[ad_account1.advertiser_id, ad_account2.advertiser_id], ) assert result == [ad_account1, ad_account2] def test_get_ad_account_campaign_count( self, client: TikTokClient, respx_mock: respx.MockRouter, build_model: BuildModel, fake: FakerTyped, ) -> None: user_access_token = fake.plain_token() ad_account_id = fake.pystr() campaign1 = build_model(Campaign, advertiser_id=ad_account_id) campaign2 = build_model(Campaign, advertiser_id=ad_account_id) page_info = build_model( PageInfo, page=1, total_page=1, page_size=2, total_number=2 ) list_data = build_model( ListData, list=[campaign1, campaign2], page_info=page_info ) response_page = build_model(Response, code=0, data=list_data) json = response_page.model_dump() json["extra_field"] = fake.pystr() respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/campaign/get/", headers={ "Access-Token": user_access_token, }, params={ "advertiser_id": ad_account_id, }, ).mock(return_value=httpx.Response(status_code=200, json=json)) result = client.get_ad_account_campaigns_count( access_token=user_access_token, advertiser_id=ad_account_id, ) assert result == 2 def test_get_ad_account_campaign_count_empty( self, client: TikTokClient, respx_mock: respx.MockRouter, build_model: BuildModel, fake: FakerTyped, ) -> None: user_access_token = fake.plain_token() ad_account_id = fake.pystr() page_info = build_model( PageInfo, page=1, total_page=0, page_size=10, total_number=0 ) list_data = build_model(ListData, list=[], page_info=page_info) response_page = build_model(Response, code=0, data=list_data) respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/campaign/get/", headers={ "Access-Token": user_access_token, }, params={ "advertiser_id": ad_account_id, }, ).mock( return_value=httpx.Response( status_code=200, json=response_page.model_dump() ) ) result = client.get_ad_account_campaigns_count( access_token=user_access_token, advertiser_id=ad_account_id, ) assert result == 0 def test_get_business_center_details( self, client: TikTokClient, respx_mock: respx.MockRouter, build_model: BuildModel, fake: FakerTyped, ) -> None: user_access_token = fake.plain_token() business_center_id = fake.pystr() business_center_info = build_model(BusinessCenterInfo, bc_id=business_center_id) business_center = build_model(BusinessCenter, bc_info=business_center_info) list_data = build_model( ListData, list=[business_center], ) response = build_model(Response, code=0, data=list_data) respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/bc/get/", headers={ "Access-Token": user_access_token, }, params={ "bc_id": business_center_id, }, ).mock(return_value=httpx.Response(status_code=200, json=response.model_dump())) result = client.get_business_center_details( access_token=user_access_token, business_center_id=business_center_id, ) assert result assert result.name == business_center.bc_info.name def test_get_business_center_details_plain_response_mock( self, client: TikTokClient, respx_mock: respx.MockRouter, fake: FakerTyped, ) -> None: user_access_token = fake.plain_token() business_center_id = fake.pystr() business_center_name = fake.pystr() response = { "code": 0, "data": { "list": [ { "bc_info": { "bc_id": business_center_id, "company": None, "currency": "UAH", "name": business_center_name, "registered_area": None, "status": "ENABLE", "type": "NORMAL", }, "ext_user_role": {"finance_role": None}, "user_role": "ADMIN", } ], "page_info": { "page": 1, "page_size": 10, "total_number": 1, "total_page": 1, }, }, "message": "OK", "request_id": "20240704121549A34D3931D18712199F7B", } respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/bc/get/", headers={ "Access-Token": user_access_token, }, params={ "bc_id": business_center_id, }, ).mock(return_value=httpx.Response(status_code=200, json=response)) result = client.get_business_center_details( access_token=user_access_token, business_center_id=business_center_id, ) assert result assert result.name == business_center_name def test_request_error( self, client: TikTokClient, respx_mock: respx.MockRouter, fake: FakerTyped ) -> None: access_token = fake.plain_token() client_secret = fake.uuid4_string() params = { "secret": client_secret, } headers = { "Access-Token": access_token, } respx_mock.get( "https://business-api.tiktok.com/open_api/v1.3/test", params=params, headers=headers, ).mock(return_value=httpx.Response(status_code=500)) try: client.request( "GET", "https://business-api.tiktok.com/open_api/v1.3/test", params=params, headers=headers, ) output = "" except Exception as exc: output = "".join(traceback.format_exception(exc)) assert access_token not in output assert client_secret not in output