11"""
2- Integration tests for the App Business Reviews integration (SerpApi) .
2+ End-to-end integration tests for the App Business Reviews integration.
33
4- Requires APP_BUSINESS_REVIEWS_API_KEY set in environment or .env file.
4+ These tests call the real SerpApi API and require a valid API key
5+ set in the APP_BUSINESS_REVIEWS_API_KEY environment variable (via .env or export).
56
67Run with:
78 pytest app-business-reviews/tests/test_app_business_reviews_integration.py -m integration
9+
10+ Never runs in CI -- the default pytest marker filter (-m unit) excludes these,
11+ and the file naming (test_*_integration.py) is not matched by python_files.
812"""
913
10- import importlib .util
1114import os
1215import sys
16+ import importlib
17+ import importlib .util
1318
1419_parent = os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." ))
1520_deps = os .path .abspath (os .path .join (os .path .dirname (__file__ ), "../dependencies" ))
16- os .chdir (_parent )
1721sys .path .insert (0 , _parent )
1822sys .path .insert (0 , _deps )
1923
2024import pytest # noqa: E402
21- from unittest .mock import AsyncMock , MagicMock # noqa: E402
22-
25+ from unittest .mock import MagicMock , AsyncMock # noqa: E402
2326from autohive_integrations_sdk import FetchResponse # noqa: E402
2427
2528_spec = importlib .util .spec_from_file_location ("abr_mod" , os .path .join (_parent , "app_business_reviews.py" ))
@@ -43,7 +46,7 @@ def live_context():
4346 async def real_fetch (url , * , method = "GET" , json = None , headers = None , params = None , ** kwargs ):
4447 async with aiohttp .ClientSession () as session :
4548 async with session .request (method , url , json = json , headers = headers or {}, params = params ) as resp :
46- data = await resp .json ()
49+ data = await resp .json (content_type = None )
4750 return FetchResponse (status = resp .status , headers = dict (resp .headers ), data = data )
4851
4952 ctx = MagicMock (name = "ExecutionContext" )
@@ -52,8 +55,10 @@ async def real_fetch(url, *, method="GET", json=None, headers=None, params=None,
5255 return ctx
5356
5457
58+ # ---- Read-Only Tests ----
59+
60+
5561class TestSearchAppsIos :
56- @pytest .mark .asyncio
5762 async def test_returns_apps (self , live_context ):
5863 result = await abr .execute_action (
5964 "search_apps_ios" ,
@@ -65,9 +70,18 @@ async def test_returns_apps(self, live_context):
6570 assert "apps" in data
6671 assert isinstance (data ["apps" ], list )
6772
73+ async def test_num_limit_respected (self , live_context ):
74+ result = await abr .execute_action (
75+ "search_apps_ios" ,
76+ {"term" : "Instagram" , "num" : 2 },
77+ live_context ,
78+ )
79+
80+ data = result .result .data
81+ assert data ["total_results" ] <= 2
82+
6883
6984class TestSearchAppsAndroid :
70- @pytest .mark .asyncio
7185 async def test_returns_apps (self , live_context ):
7286 result = await abr .execute_action (
7387 "search_apps_android" ,
@@ -79,9 +93,18 @@ async def test_returns_apps(self, live_context):
7993 assert "apps" in data
8094 assert isinstance (data ["apps" ], list )
8195
96+ async def test_limit_respected (self , live_context ):
97+ result = await abr .execute_action (
98+ "search_apps_android" ,
99+ {"query" : "Spotify" , "limit" : 2 },
100+ live_context ,
101+ )
102+
103+ data = result .result .data
104+ assert data ["total_results" ] <= 2
105+
82106
83107class TestSearchPlacesGoogleMaps :
84- @pytest .mark .asyncio
85108 async def test_returns_places (self , live_context ):
86109 result = await abr .execute_action (
87110 "search_places_google_maps" ,
@@ -92,3 +115,109 @@ async def test_returns_places(self, live_context):
92115 data = result .result .data
93116 assert "places" in data
94117 assert isinstance (data ["places" ], list )
118+
119+ async def test_location_filters_results (self , live_context ):
120+ result = await abr .execute_action (
121+ "search_places_google_maps" ,
122+ {"query" : "pizza" , "location" : "New York, NY" , "num_results" : 3 },
123+ live_context ,
124+ )
125+
126+ data = result .result .data
127+ assert "places" in data
128+ assert data ["total_results" ] <= 3
129+
130+
131+ class TestGetReviewsAppStore :
132+ async def test_returns_reviews_for_whatsapp (self , live_context ):
133+ # First get a real product ID from search
134+ search_result = await abr .execute_action (
135+ "search_apps_ios" , {"term" : "WhatsApp" , "num" : 1 }, live_context
136+ )
137+ apps = search_result .result .data ["apps" ]
138+ if not apps :
139+ pytest .skip ("No iOS apps returned from search" )
140+
141+ product_id = str (apps [0 ]["id" ])
142+
143+ result = await abr .execute_action (
144+ "get_reviews_app_store" , {"product_id" : product_id , "max_pages" : 1 }, live_context
145+ )
146+
147+ data = result .result .data
148+ assert "reviews" in data
149+ assert "total_reviews" in data
150+ assert "product_id" in data
151+
152+ async def test_auto_resolve_by_app_name (self , live_context ):
153+ result = await abr .execute_action (
154+ "get_reviews_app_store" , {"app_name" : "WhatsApp" , "max_pages" : 1 }, live_context
155+ )
156+
157+ data = result .result .data
158+ assert "reviews" in data
159+ assert "app_name" in data
160+
161+
162+ class TestGetReviewsGooglePlay :
163+ async def test_returns_reviews_for_whatsapp (self , live_context ):
164+ result = await abr .execute_action (
165+ "get_reviews_google_play" , {"product_id" : "com.whatsapp" , "max_pages" : 1 }, live_context
166+ )
167+
168+ data = result .result .data
169+ assert "reviews" in data
170+ assert "total_reviews" in data
171+ assert data ["product_id" ] == "com.whatsapp"
172+
173+ async def test_auto_resolve_by_app_name (self , live_context ):
174+ result = await abr .execute_action (
175+ "get_reviews_google_play" , {"app_name" : "WhatsApp" , "max_pages" : 1 }, live_context
176+ )
177+
178+ data = result .result .data
179+ assert "reviews" in data
180+ assert "product_id" in data
181+
182+
183+ class TestGetReviewsGoogleMaps :
184+ async def test_returns_reviews_chained_from_search (self , live_context ):
185+ # Get a real place_id from search
186+ search_result = await abr .execute_action (
187+ "search_places_google_maps" , {"query" : "Starbucks Sydney" , "num_results" : 1 }, live_context
188+ )
189+ places = search_result .result .data ["places" ]
190+ if not places :
191+ pytest .skip ("No places returned from search" )
192+
193+ place = places [0 ]
194+ identifier = {"place_id" : place ["place_id" ]} if place .get ("place_id" ) else {"data_id" : place ["data_id" ]}
195+
196+ result = await abr .execute_action (
197+ "get_reviews_google_maps" , {** identifier , "max_pages" : 1 }, live_context
198+ )
199+
200+ data = result .result .data
201+ assert "reviews" in data
202+ assert "total_reviews" in data
203+ assert "business_name" in data
204+
205+ async def test_response_structure (self , live_context ):
206+ search_result = await abr .execute_action (
207+ "search_places_google_maps" , {"query" : "McDonald's Melbourne" , "num_results" : 1 }, live_context
208+ )
209+ places = search_result .result .data ["places" ]
210+ if not places :
211+ pytest .skip ("No places returned from search" )
212+
213+ place = places [0 ]
214+ identifier = {"place_id" : place ["place_id" ]} if place .get ("place_id" ) else {"data_id" : place ["data_id" ]}
215+
216+ result = await abr .execute_action (
217+ "get_reviews_google_maps" , {** identifier , "max_pages" : 1 }, live_context
218+ )
219+
220+ data = result .result .data
221+ assert "reviews" in data
222+ assert "average_rating" in data
223+ assert "place_id" in data
0 commit comments