async def test_update_system_data_v3( event_loop, v3_server, v3_sensors_json, v3_subscriptions_json): """Test getting updated data for a v3 system.""" async with v3_server: v3_server.add( 'api.simplisafe.com', '/v1/users/{0}/subscriptions'.format(TEST_USER_ID), 'get', aresponses.Response( text=json.dumps(v3_subscriptions_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/ss3/subscriptions/{0}/sensors'.format(TEST_SUBSCRIPTION_ID), 'get', aresponses.Response(text=json.dumps(v3_sensors_json), status=200)) async with aiohttp.ClientSession(loop=event_loop) as websession: api = await API.login_via_credentials( TEST_EMAIL, TEST_PASSWORD, websession) [system] = await api.get_systems() await system.update() assert system.serial == TEST_SYSTEM_SERIAL_NO assert system.system_id == TEST_SYSTEM_ID assert system.api._access_token == TEST_ACCESS_TOKEN assert len(system.sensors) == 21
async def run_test(): async with aresponses.ResponsesMockServer(loop=self.loop) as arsps: arsps.add( API_HOST, endpoint, 'get', aresponses.Response(status=200, text="Look ma I'm an image", headers={'content-type': 'image/jpeg'})) arsps.add( API_HOST, endpoint, 'get', aresponses.Response(status=200, text="What a purdy picture", headers={'content-type': 'image/jpeg'})) # I am of course cheating here by returning text instead of an image # for image requests. get_image trusts that the Logi API will always # return a valid image for these requests so I don't want to overcomplicate # the test. # Test return of image img = await self.test_camera.live_stream.download_jpeg() self.assertEqual(img, b"Look ma I'm an image") # Test download of image to disk await self.test_camera.live_stream.download_jpeg( filename=TEMP_IMAGE) with open(TEMP_IMAGE, 'r') as test_file: data = test_file.read() self.assertEqual(data, "What a purdy picture")
async def test_401_refresh_token_failure(aresponses, event_loop, v2_server, v2_subscriptions_json): """Test that a generic error is thrown when a request fails.""" async with v2_server: v2_server.add( "api.simplisafe.com", f"/v1/users/{TEST_USER_ID}/subscriptions", "get", aresponses.Response(text=json.dumps(v2_subscriptions_json), status=200), ) v2_server.add( "api.simplisafe.com", f"/v1/subscriptions/{TEST_SUBSCRIPTION_ID}/settings", "get", aresponses.Response(text="", status=401), ) v2_server.add( "api.simplisafe.com", "/v1/api/token", "post", aresponses.Response(text="", status=401), ) async with aiohttp.ClientSession(loop=event_loop) as websession: with pytest.raises(InvalidCredentialsError): api = await API.login_via_credentials(TEST_EMAIL, TEST_PASSWORD, websession) systems = await api.get_systems() system = systems[TEST_SYSTEM_ID] await system.update()
async def run_test(): async with aresponses.ResponsesMockServer(loop=self.loop) as arsps: arsps.add(API_HOST, endpoint, 'put', aresponses.Response(status=200)) arsps.add(API_HOST, endpoint, 'put', aresponses.Response(status=200)) arsps.add(API_HOST, endpoint, 'put', aresponses.Response(status=200)) # Set streaming enabled property self.assertEqual(self.test_camera.streaming, True) # Prop should change when config successfully updated await self.test_camera.set_config('streaming', False) self.assertEqual(self.test_camera.streaming, False) # Disable recording self.assertEqual(self.test_camera.recording, True) # Prop should change when config successfully updated await self.test_camera.set_config('recording_disabled', True) self.assertEqual(self.test_camera.recording, False) # Enable recording # Prop should change when config successfully updated await self.test_camera.set_config('recording_disabled', False) self.assertEqual(self.test_camera.recording, True)
def v2_server( api_token_json, auth_check_json, event_loop, v2_settings_json, v2_subscriptions_json ): """Return a ready-to-query mocked v2 server.""" server = aresponses.ResponsesMockServer(loop=event_loop) server.add( "api.simplisafe.com", "/v1/api/token", "post", aresponses.Response(text=json.dumps(api_token_json), status=200), ) server.add( "api.simplisafe.com", "/v1/api/authCheck", "get", aresponses.Response(text=json.dumps(auth_check_json), status=200), ) server.add( "api.simplisafe.com", f"/v1/users/{TEST_USER_ID}/subscriptions", "get", aresponses.Response(text=json.dumps(v2_subscriptions_json), status=200), ) server.add( "api.simplisafe.com", f"/v1/subscriptions/{TEST_SUBSCRIPTION_ID}/settings", "get", aresponses.Response(text=json.dumps(v2_settings_json), status=200), ) return server
async def test_refresh_token_dirtiness(api_token_json, auth_check_json, event_loop, v2_server): """Test that the refresh token's dirtiness can be checked.""" async with v2_server: v2_server.add( "api.simplisafe.com", "/v1/api/token", "post", aresponses.Response(text=json.dumps(api_token_json), status=200), ) v2_server.add( "api.simplisafe.com", "/v1/api/authCheck", "get", aresponses.Response(text=json.dumps(auth_check_json), status=200), ) v2_server.add( "api.simplisafe.com", "/v1/api/authCheck", "get", aresponses.Response(text=json.dumps(auth_check_json), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: api = await API.login_via_credentials(TEST_EMAIL, TEST_PASSWORD, websession) systems = await api.get_systems() system = systems[TEST_SYSTEM_ID] system.api._access_token_expire = datetime.now() - timedelta( hours=1) await system.api.request("get", "api/authCheck") assert system.api.refresh_token_dirty assert system.api.refresh_token == TEST_REFRESH_TOKEN assert not system.api.refresh_token_dirty
def v2_server(v2_subscriptions_response): """Return a ready-to-query mocked v2 server.""" server = aresponses.ResponsesMockServer() server.add( "api.simplisafe.com", "/v1/api/token", "post", aresponses.Response(text=load_fixture("api_token_response.json"), status=200), ) server.add( "api.simplisafe.com", "/v1/api/authCheck", "get", aresponses.Response(text=load_fixture("auth_check_response.json"), status=200), ) server.add( "api.simplisafe.com", f"/v1/users/{TEST_USER_ID}/subscriptions", "get", aresponses.Response(text=v2_subscriptions_response, status=200), ) server.add( "api.simplisafe.com", f"/v1/subscriptions/{TEST_SUBSCRIPTION_ID}/settings", "get", aresponses.Response(text=load_fixture("v2_settings_response.json"), status=200), ) return server
def authenticated_local_client( apiver_json, auth_login_json, event_loop, provision_name_json, provision_wifi_json ): """Return an aresponses server for an authenticated local client.""" client = aresponses.ResponsesMockServer(loop=event_loop) client.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/auth/login", "post", aresponses.Response(text=json.dumps(auth_login_json), status=200), ) client.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/provision/name", "get", aresponses.Response(text=json.dumps(provision_name_json), status=200), ) client.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/provision/wifi", "get", aresponses.Response(text=json.dumps(provision_wifi_json), status=200), ) client.add( "{0}:{1}".format(TEST_HOST, TEST_PORT), "/api/4/apiVer", "get", aresponses.Response(text=json.dumps(apiver_json), status=200), ) return client
async def test_querystring_not_match(aresponses): aresponses.add('foo.com', '/path', 'get', aresponses.Response(text='hi'), match_querystring=True) aresponses.add('foo.com', aresponses.ANY, 'get', aresponses.Response(text='miss'), match_querystring=True) aresponses.add('foo.com', aresponses.ANY, 'get', aresponses.Response(text='miss'), match_querystring=True) url = 'http://foo.com/path?reply=42' async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == 'miss' url = 'http://foo.com/path?reply=43' async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == 'miss' url = 'http://foo.com/path' async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == 'hi'
def authenticated_remote_client( apiver_json, event_loop, remote_auth_login_1_json, remote_auth_login_2_json, remote_sprinklers_json, ): """Return an aresponses server for an authenticated remote client.""" client = aresponses.ResponsesMockServer(loop=event_loop) client.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response(text=json.dumps(remote_auth_login_1_json), status=200), ) client.add( "my.rainmachine.com", "/devices/get-sprinklers", "post", aresponses.Response(text=json.dumps(remote_sprinklers_json), status=200), ) client.add( "my.rainmachine.com", "/devices/login-sprinkler", "post", aresponses.Response(text=json.dumps(remote_auth_login_2_json), status=200), ) client.add( "api.rainmachine.com", "/{0}/api/4/apiVer".format(TEST_SPRINKLER_ID), "get", aresponses.Response(text=json.dumps(apiver_json), status=200), ) return client
def authenticated_local_client(): """Return an aresponses server for an authenticated local client.""" client = aresponses.ResponsesMockServer() client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/auth/login", "post", aresponses.Response(text=load_fixture("auth_login_response.json"), status=200), ) client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/provision/name", "get", aresponses.Response(text=load_fixture("provision_name_response.json"), status=200), ) client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/provision/wifi", "get", aresponses.Response(text=load_fixture("provision_wifi_response.json"), status=200), ) client.add( f"{TEST_HOST}:{TEST_PORT}", "/api/4/apiVer", "get", aresponses.Response(text=load_fixture("api_version_response.json"), status=200), ) return client
def authenticated_remote_client(): """Return an aresponses server for an authenticated remote client.""" client = aresponses.ResponsesMockServer() client.add( "my.rainmachine.com", "/login/auth", "post", aresponses.Response( text=load_fixture("remote_auth_login_1_response.json"), status=200), ) client.add( "my.rainmachine.com", "/devices/get-sprinklers", "post", aresponses.Response( text=load_fixture("remote_sprinklers_response.json"), status=200), ) client.add( "my.rainmachine.com", "/devices/login-sprinkler", "post", aresponses.Response( text=load_fixture("remote_auth_login_2_response.json"), status=200), ) client.add( "api.rainmachine.com", f"/{TEST_SPRINKLER_ID}/api/4/apiVer", "get", aresponses.Response(text=load_fixture("api_version_response.json"), status=200), ) return client
async def test_querystring_not_match(aresponses): aresponses.add("foo.com", "/path", "get", aresponses.Response(text="hi"), match_querystring=True) aresponses.add("foo.com", aresponses.ANY, "get", aresponses.Response(text="miss"), match_querystring=True) aresponses.add("foo.com", aresponses.ANY, "get", aresponses.Response(text="miss"), match_querystring=True) url = "http://foo.com/path?reply=42" async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == "miss" url = "http://foo.com/path?reply=43" async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == "miss" url = "http://foo.com/path" async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == "hi"
async def run_test(): async with aresponses.ResponsesMockServer(loop=self.loop) as arsps: arsps.add(API_HOST, '/api', 'get', aresponses.Response(status=200, text='{ "abc" : 123 }', headers={'content-type': 'application/json'})) arsps.add(API_HOST, '/api', 'post', aresponses.Response(status=200, text='{ "foo" : "bar" }', headers={'content-type': 'application/json'})) arsps.add(API_HOST, '/api', 'put', aresponses.Response(status=200, text='{ "success" : true }', headers={'content-type': 'application/json'})) arsps.add(API_HOST, '/api', 'delete', aresponses.Response(status=200, text='{ "success" : true }', headers={'content-type': 'application/json'})) get_result = await logi._fetch(url='/api') post_result = await logi._fetch(url='/api', method='POST') put_result = await logi._fetch(url='/api', method='PUT') delete_result = await logi._fetch(url='/api', method='DELETE') self.assertEqual(get_result['abc'], 123) self.assertEqual(post_result['foo'], 'bar') self.assertTrue(put_result['success']) self.assertTrue(delete_result['success'])
async def test_regex(aresponses): aresponses.add(aresponses.ANY, aresponses.ANY, aresponses.ANY, aresponses.Response(text="hi")) aresponses.add(aresponses.ANY, aresponses.ANY, aresponses.ANY, aresponses.Response(text="there")) async with aiohttp.ClientSession() as session: async with session.get("http://foo.com") as response: text = await response.text() assert text == "hi" async with session.get("http://bar.com") as response: text = await response.text() assert text == "there"
async def async_error_requests(hostname, bot_id): mock: aresponses.ResponsesMockServer async with aresponses.ResponsesMockServer() as mock: mock.add( hostname, BotXAPI.V2.token.url.split("{host}", 1)[1].format(bot_id=bot_id), BotXAPI.V2.token.method.lower(), aresponses.Response( body=json.dumps({ "status": "error", "message": " error response" }), status=500, ), ) mock.add( hostname, BotXAPI.V3.notification.url.split("{host}", 1)[1], BotXAPI.V3.notification.method.lower(), aresponses.Response( body=json.dumps({ "status": "error", "message": " error response" }), status=500, ), ) mock.add( hostname, BotXAPI.V3.command.url.split("{host}", 1)[1], BotXAPI.V3.command.method.lower(), aresponses.Response( body=json.dumps({ "status": "error", "message": " error response" }), status=500, ), ) mock.add( hostname, BotXAPI.V1.file.url.split("{host}", 1)[1], BotXAPI.V1.file.method.lower(), aresponses.Response( body=json.dumps({ "status": "error", "message": " error response" }), status=500, ), ) yield mock
async def test_401_refresh_token_success( api_token_json, auth_check_json, event_loop, v2_server, v2_settings_json, v2_subscriptions_json, ): """Test that a generic error is thrown when a request fails.""" async with v2_server: v2_server.add( "api.simplisafe.com", f"/v1/users/{TEST_USER_ID}/subscriptions", "get", aresponses.Response(text="", status=401), ) v2_server.add( "api.simplisafe.com", "/v1/api/token", "post", aresponses.Response(text=json.dumps(api_token_json), status=200), ) v2_server.add( "api.simplisafe.com", "/v1/api/authCheck", "get", aresponses.Response(text=json.dumps(auth_check_json), status=200), ) v2_server.add( "api.simplisafe.com", f"/v1/users/{TEST_USER_ID}/subscriptions", "get", aresponses.Response(text=json.dumps(v2_subscriptions_json), status=200), ) v2_server.add( "api.simplisafe.com", f"/v1/subscriptions/{TEST_SUBSCRIPTION_ID}/settings", "get", aresponses.Response(text=json.dumps(v2_settings_json), status=200), ) async with aiohttp.ClientSession(loop=event_loop) as websession: api = await API.login_via_credentials(TEST_EMAIL, TEST_PASSWORD, websession) systems = await api.get_systems() system = systems[TEST_SYSTEM_ID] await system.update() assert system.api.refresh_token_dirty assert system.api.refresh_token == TEST_REFRESH_TOKEN assert not system.api.refresh_token_dirty
async def run_test(): async with aresponses.ResponsesMockServer(loop=self.loop) as arsps: arsps.add(API_HOST, '/api', 'get', aresponses.Response(status=401)) arsps.add(AUTH_HOST, TOKEN_ENDPOINT, 'post', aresponses.Response(status=200, text=auth_fixture, headers={'content-type': 'application/json'})) arsps.add(API_HOST, '/api', 'get', aresponses.Response(status=401)) with self.assertRaises(AuthorizationFailed): await logi._fetch(url='/api')
async def test_get_systems_v3( api_token_json, auth_check_json, event_loop, v3_server, v3_sensors_json, v3_subscriptions_json): """Test the ability to get systems attached to a v3 account.""" async with v3_server: # Since this flow will call both three routes once more each (on top of # what instantiation does) and aresponses deletes matches each time, # we need to add additional routes: v3_server.add( 'api.simplisafe.com', '/v1/api/token', 'post', aresponses.Response(text=json.dumps(api_token_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/api/authCheck', 'get', aresponses.Response(text=json.dumps(auth_check_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/users/{0}/subscriptions'.format(TEST_USER_ID), 'get', aresponses.Response( text=json.dumps(v3_subscriptions_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/ss3/subscriptions/{0}/sensors'.format(TEST_SUBSCRIPTION_ID), 'get', aresponses.Response(text=json.dumps(v3_sensors_json), status=200)) async with aiohttp.ClientSession(loop=event_loop) as websession: credentials_api = await API.login_via_credentials( TEST_EMAIL, TEST_PASSWORD, websession) systems = await credentials_api.get_systems() assert len(systems) == 1 primary_system = systems[0] assert primary_system.serial == TEST_SYSTEM_SERIAL_NO assert primary_system.system_id == TEST_SYSTEM_ID assert primary_system.api._access_token == TEST_ACCESS_TOKEN assert len(primary_system.sensors) == 21 token_api = await API.login_via_token( TEST_REFRESH_TOKEN, websession) systems = await token_api.get_systems() assert len(systems) == 1 primary_system = systems[0] assert primary_system.serial == TEST_SYSTEM_SERIAL_NO assert primary_system.system_id == TEST_SYSTEM_ID assert primary_system.api._access_token == TEST_ACCESS_TOKEN assert len(primary_system.sensors) == 21
async def test_regex(aresponses): aresponses.add(aresponses.ANY, aresponses.ANY, aresponses.ANY, aresponses.Response(text='hi')) aresponses.add(aresponses.ANY, aresponses.ANY, aresponses.ANY, aresponses.Response(text='there')) async with aiohttp.ClientSession() as session: async with session.get('http://foo.com') as response: text = await response.text() assert text == 'hi' async with session.get('http://bar.com') as response: text = await response.text() assert text == 'there'
async def async_requests(hostname, bot_id): mock: aresponses.ResponsesMockServer async with aresponses.ResponsesMockServer() as mock: mock.add( hostname, BotXAPI.V2.token.url.split("{host}", 1)[1].format(bot_id=bot_id), BotXAPI.V2.token.method.lower(), aresponses.Response( body=json.dumps({ "status": "ok", "result": "token_for_operations" }), status=200, ), ) mock.add( hostname, BotXAPI.V3.notification.url.split("{host}", 1)[1], BotXAPI.V3.notification.method.lower(), aresponses.Response( body=json.dumps({ "status": "ok", "result": "notification_result_sent" }), status=200, ), ) mock.add( hostname, BotXAPI.V3.command.url.split("{host}", 1)[1], BotXAPI.V3.command.method.lower(), aresponses.Response( body=json.dumps({ "status": "ok", "result": "command_result_sent" }), status=200, ), ) mock.add( hostname, BotXAPI.V1.file.url.split("{host}", 1)[1], BotXAPI.V1.file.method.lower(), aresponses.Response(body=json.dumps({ "status": "ok", "result": "file_sent" }), status=200), ) yield mock
async def test_set_states_v3( event_loop, v3_server, v3_state_away_json, v3_state_home_json, v3_state_off_json): """Test the ability to set the state of the system.""" async with v3_server: # Since this flow will make the same API call four times and # aresponses deletes matches each time, we need to add four additional # routes: v3_server.add( 'api.simplisafe.com', '/v1/ss3/subscriptions/{0}/state/{1}'.format( TEST_SUBSCRIPTION_ID, 'away'), 'post', aresponses.Response( text=json.dumps(v3_state_away_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/ss3/subscriptions/{0}/state/{1}'.format( TEST_SUBSCRIPTION_ID, 'home'), 'post', aresponses.Response( text=json.dumps(v3_state_home_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/ss3/subscriptions/{0}/state/{1}'.format( TEST_SUBSCRIPTION_ID, 'off'), 'post', aresponses.Response( text=json.dumps(v3_state_off_json), status=200)) v3_server.add( 'api.simplisafe.com', '/v1/ss3/subscriptions/{0}/state/{1}'.format( TEST_SUBSCRIPTION_ID, 'off'), 'post', aresponses.Response( text=json.dumps(v3_state_off_json), status=200)) async with aiohttp.ClientSession(loop=event_loop) as websession: api = await API.login_via_credentials( TEST_EMAIL, TEST_PASSWORD, websession) [system] = await api.get_systems() await system.set_away() assert system.state == SystemStates.away await system.set_home() assert system.state == SystemStates.home await system.set_off() assert system.state == SystemStates.off await system.set_off() assert system.state == SystemStates.off
async def run_test(): async with aresponses.ResponsesMockServer(loop=self.loop) as arsps: arsps.add(API_HOST, '/api', 'get', aresponses.Response(status=401)) arsps.add(AUTH_HOST, TOKEN_ENDPOINT, 'post', aresponses.Response(status=200, text=auth_fixture, headers={'content-type': 'application/json'})) arsps.add(API_HOST, '/api', 'get', aresponses.Response(status=200, text='{ "foo" : "bar" }', headers={'content-type': 'application/json'})) get_result = await logi._fetch(url='/api') self.assertEqual(get_result['foo'], 'bar')
async def test_bad_redirect(aresponses): aresponses.add('foo.com', '/', 'get', aresponses.Response(text='hi', status=301)) url = 'http://foo.com' async with aiohttp.ClientSession() as session: response = await session.get(url) await response.text()
async def test_bad_redirect(aresponses): aresponses.add("foo.com", "/", "get", aresponses.Response(text="hi", status=301)) url = "http://foo.com" async with aiohttp.ClientSession() as session: response = await session.get(url) await response.text()
async def test_foo(aresponses): # text as response (defaults to status 200 response) aresponses.add("foo.com", "/", "get", "hi there!!") # custom status code response aresponses.add("foo.com", "/", "get", aresponses.Response(text="error", status=500)) # passthrough response (makes an actual network call) aresponses.add("httpstat.us", "/200", "get", aresponses.passthrough) # custom handler response def my_handler(request): return aresponses.Response(status=200, text=str(request.url)) aresponses.add("foo.com", "/", "get", my_handler) url = "http://foo.com" async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == "hi there!!" async with session.get(url) as response: text = await response.text() assert text == "error" assert response.status == 500 async with session.get("https://httpstat.us/200") as response: text = await response.text() assert text == "200 OK" async with session.get(url) as response: text = await response.text() assert text == "http://foo.com/"
async def test_foo(aresponses): # text as response (defaults to status 200 response) aresponses.add('foo.com', '/', 'get', 'hi there!!') # custom status code response aresponses.add('foo.com', '/', 'get', aresponses.Response(text='error', status=500)) # passthrough response (makes an actual network call) aresponses.add('httpstat.us', '/200', 'get', aresponses.passthrough) # custom handler response def my_handler(request): return aresponses.Response(status=200, text=str(request.url)) aresponses.add('foo.com', '/', 'get', my_handler) url = 'http://foo.com' async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == 'hi there!!' async with session.get(url) as response: text = await response.text() assert text == 'error' assert response.status == 500 async with session.get('https://httpstat.us/200') as response: text = await response.text() assert text == '200 OK' async with session.get(url) as response: text = await response.text() assert text == 'http://foo.com/'
async def test_fixture_body_json_failed(aresponses): aresponses.add("foo.com", "/", "post", aresponses.Response(text="hi"), body={"a": 2}) url = "http://foo.com" async with aiohttp.ClientSession() as session: async with session.post(url, json={"a": 1}) as _: pass
async def test_safe_mocked_error_response_eventually_fails(aresponses): # when all calls fail it should fail aresponses.add("blabla", "/", "GET", aresponses.Response(status=500, text="Error")) with pytest.raises(Exception): response = await safe_http_get_json('http://blabla/') aresponses.assert_called_in_order()
async def test_fixture(aresponses): aresponses.add('foo.com', '/', 'get', aresponses.Response(text='hi')) url = 'http://foo.com' async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() assert text == 'hi'