def stop(self): """Stop the current request and future ones. This avoids an exception if there is someone waiting when exiting test. """ self.stopping = True self.queue_response(exc=ClientError())
async def test_binary_sensor_device(hass, aioclient_mock): # noqa """Test a binary sensor device.""" config = { 'qwikswitch': { 'sensors': { 'name': 's1', 'id': '@a00001', 'channel': 1, 'type': 'imod', } } } await async_setup_component(hass, QWIKSWITCH, config) await hass.async_block_till_done() state_obj = hass.states.get('binary_sensor.s1') assert state_obj.state == 'off' hass.bus.async_fire(EVENT_HOMEASSISTANT_START) LISTEN.append('{"id":"@a00001","cmd":"","data":"4e0e1601","rssi":"61%"}') LISTEN.append(ClientError()) # Will cause a sleep await hass.async_block_till_done() state_obj = hass.states.get('binary_sensor.s1') assert state_obj.state == 'on' LISTEN.append('{"id":"@a00001","cmd":"","data":"4e0e1701","rssi":"61%"}') hass.data[QWIKSWITCH]._sleep_task.cancel() await LISTEN.wait_till_empty(hass) state_obj = hass.states.get('binary_sensor.s1') assert state_obj.state == 'off'
async def __call__(self, method, url, data): """Fetch the next response from the queue or wait until the queue has items.""" if self.stopping: raise ClientError() await self.semaphore.acquire() kwargs = self.response_list.pop(0) return AiohttpClientMockResponse(method=method, url=url, **kwargs)
async def test_binary_sensor_device(hass, aioclient_mock): # noqa: F811 """Test a binary sensor device.""" config = { "qwikswitch": { "sensors": {"name": "s1", "id": "@a00001", "channel": 1, "type": "imod"} } } await async_setup_component(hass, QWIKSWITCH, config) await hass.async_block_till_done() state_obj = hass.states.get("binary_sensor.s1") assert state_obj.state == "off" hass.bus.async_fire(EVENT_HOMEASSISTANT_START) LISTEN.append('{"id":"@a00001","cmd":"","data":"4e0e1601","rssi":"61%"}') LISTEN.append(ClientError()) # Will cause a sleep await hass.async_block_till_done() state_obj = hass.states.get("binary_sensor.s1") assert state_obj.state == "on" LISTEN.append('{"id":"@a00001","cmd":"","data":"4e0e1701","rssi":"61%"}') hass.data[QWIKSWITCH]._sleep_task.cancel() await LISTEN.wait_till_empty(hass) state_obj = hass.states.get("binary_sensor.s1") assert state_obj.state == "off"
async def test_add_event_failure( hass: HomeAssistant, component_setup: ComponentSetup, mock_calendars_list: ApiResult, test_api_calendar: dict[str, Any], mock_events_list: ApiResult, mock_insert_event: Callable[[..., dict[str, Any]], None], setup_config_entry: MockConfigEntry, add_event_call_service: Callable[dict[str, Any], Awaitable[None]], ) -> None: """Test service calls with incorrect fields.""" mock_calendars_list({"items": [test_api_calendar]}) mock_events_list({}) assert await component_setup() mock_insert_event( calendar_id=CALENDAR_ID, exc=ClientError(), ) with pytest.raises(HomeAssistantError): await add_event_call_service( {"start_date": "2022-05-01", "end_date": "2022-05-01"} )
async def fetch_url(session, url, timeout): with async_timeout.timeout(timeout): async with session.get(url) as response: if response.status == 200: log.debug("Got url {!r}".format(url)) return await response.read() else: log.exception("Failed getting url {!r}".format(url)) raise ClientError("Failed getting url {!r}".format(url))
async def test_failed_update_devices(hass, aioclient_mock): """Test that code behaves correctly when unable to get the devices.""" config = {"qwikswitch": {}} aioclient_mock.get("http://127.0.0.1:2020/&device", exc=ClientError()) listen_mock = MockLongPollSideEffect() aioclient_mock.get("http://127.0.0.1:2020/&listen", side_effect=listen_mock) assert not await async_setup_component(hass, QWIKSWITCH, config) await hass.async_start() await hass.async_block_till_done() listen_mock.stop()
async def test_scan_calendar_error( hass, component_setup, test_api_calendar, mock_calendars_list, ): """Test that the calendar update handles a server error.""" mock_calendars_list({}, exc=ClientError()) assert await component_setup() assert not hass.states.get(TEST_ENTITY)
async def test_authenticate_on_network_error(self): self.authenticator._authenticate = mock.CoroutineMock( side_effect=ClientError()) with self.assertRaisesRegex(AuthenticationError, "Network request failed"): await self.authenticator.authenticate() self.assertIsNone(self.authenticator.id) self.assertIsNone(self.authenticator.issued_at) self.assertIsNone(self.authenticator.instance_url) self.assertIsNone(self.authenticator.signature) self.assertIsNone(self.authenticator.access_token) self.assertIsNone(self.authenticator.token_type)
async def test_http_event_api_failure( hass, hass_client, component_setup, mock_calendars_list, mock_events_list, aioclient_mock, ): """Test the Rest API response during a calendar failure.""" mock_events_list({}) assert await component_setup() client = await hass_client() aioclient_mock.clear_requests() mock_events_list({}, exc=ClientError()) response = await client.get(upcoming_event_url()) assert response.status == HTTPStatus.OK # A failure to talk to the server results in an empty list of events events = await response.json() assert events == []
async def test_http_event_api_failure( hass, hass_client, component_setup, mock_calendars_list, mock_events_list, aioclient_mock, ): """Test the Rest API response during a calendar failure.""" mock_events_list({}) assert await component_setup() client = await hass_client() aioclient_mock.clear_requests() mock_events_list({}, exc=ClientError()) response = await client.get(upcoming_event_url()) assert response.status == HTTPStatus.INTERNAL_SERVER_ERROR state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "unavailable"
async def test_update_error( hass, component_setup, mock_calendars_list, mock_events_list, test_api_calendar, aioclient_mock, ): """Test that the calendar update handles a server error.""" now = dt_util.now() mock_calendars_list({"items": [test_api_calendar]}) mock_events_list({ "items": [{ **TEST_EVENT, "start": { "dateTime": (now + datetime.timedelta(minutes=-30)).isoformat() }, "end": { "dateTime": (now + datetime.timedelta(minutes=30)).isoformat() }, }] }) assert await component_setup() state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "on" # Advance time to avoid throttling now += datetime.timedelta(minutes=30) aioclient_mock.clear_requests() mock_events_list({}, exc=ClientError()) with patch("homeassistant.util.utcnow", return_value=now): async_fire_time_changed(hass, now) await hass.async_block_till_done() # No change state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "on" # Advance time beyond update/throttle point now += datetime.timedelta(minutes=30) aioclient_mock.clear_requests() mock_events_list({ "items": [{ **TEST_EVENT, "start": { "dateTime": (now + datetime.timedelta(minutes=30)).isoformat() }, "end": { "dateTime": (now + datetime.timedelta(minutes=60)).isoformat() }, }] }) with patch("homeassistant.util.utcnow", return_value=now): async_fire_time_changed(hass, now) await hass.async_block_till_done() # State updated state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "off"
def _build_digest_header(self, method, url): """ :rtype: str """ realm = self.challenge["realm"] nonce = self.challenge["nonce"] qop = self.challenge.get("qop") algorithm = self.challenge.get("algorithm", "MD5").upper() opaque = self.challenge.get("opaque") if qop and not (qop == "auth" or "auth" in qop.split(",")): raise ClientError("Unsupported qop value: %s" % qop) # lambdas assume digest modules are imported at the top level if algorithm == "MD5" or algorithm == "MD5-SESS": hash_fn = hashlib.md5 elif algorithm == "SHA": hash_fn = hashlib.sha1 else: return "" def H(x): return hash_fn(x.encode()).hexdigest() def KD(s, d): return H("%s:%s" % (s, d)) path = URL(url).path_qs A1 = "%s:%s:%s" % (self.username, realm, self.password) A2 = "%s:%s" % (method, path) HA1 = H(A1) HA2 = H(A2) if nonce == self.last_nonce: self.nonce_count += 1 else: self.nonce_count = 1 self.last_nonce = nonce ncvalue = "%08x" % self.nonce_count # cnonce is just a random string generated by the client. cnonce_data = "".join( [ str(self.nonce_count), nonce, time.ctime(), os.urandom(8).decode(errors="ignore"), ] ).encode() cnonce = hashlib.sha1(cnonce_data).hexdigest()[:16] if algorithm == "MD5-SESS": HA1 = H("%s:%s:%s" % (HA1, nonce, cnonce)) # This assumes qop was validated to be 'auth' above. If 'auth-int' # support is added this will need to change. if qop: noncebit = ":".join([nonce, ncvalue, cnonce, "auth", HA2]) response_digest = KD(HA1, noncebit) else: response_digest = KD(HA1, "%s:%s" % (nonce, HA2)) base = ", ".join( [ 'username="******"' % self.username, 'realm="%s"' % realm, 'nonce="%s"' % nonce, 'uri="%s"' % path, 'response="%s"' % response_digest, 'algorithm="%s"' % algorithm, ] ) if opaque: base += ', opaque="%s"' % opaque if qop: base += ', qop="auth", nc=%s, cnonce="%s"' % (ncvalue, cnonce) return "Digest %s" % base
data["token"].pop("expires_at") data["token"].pop("expires_in") assert data == { "auth_implementation": "device_auth", "token": { "access_token": "ACCESS_TOKEN", "refresh_token": "REFRESH_TOKEN", "scope": "https://www.googleapis.com/auth/calendar", "token_type": "Bearer", }, } assert len(mock_setup.mock_calls) == 1 @pytest.mark.parametrize("primary_calendar_error", [ClientError()]) async def test_calendar_lookup_failure( hass: HomeAssistant, mock_code_flow: Mock, mock_exchange: Mock, component_setup: ComponentSetup, ) -> None: """Test successful config flow and title fetch fails gracefully.""" assert await component_setup() result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER}) assert result.get("type") == "progress" assert result.get("step_id") == "auth" assert "description_placeholders" in result assert "url" in result["description_placeholders"]
async def post(self, *args: Any, **kwargs: Any): # pylint: disable=W0613 raise ClientError() # Need to yield to satisfy static analysis of @asynccontextmanager. yield # noqa
async def test_update_error( hass, component_setup, mock_calendars_list, mock_events_list, test_api_calendar, aioclient_mock, ): """Test that the calendar update handles a server error.""" now = dt_util.now() mock_calendars_list({"items": [test_api_calendar]}) mock_events_list({ "items": [{ **TEST_EVENT, "start": { "dateTime": (now + datetime.timedelta(minutes=-30)).isoformat() }, "end": { "dateTime": (now + datetime.timedelta(minutes=30)).isoformat() }, }] }) assert await component_setup() state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "on" # Advance time to next data update interval now += datetime.timedelta(minutes=30) aioclient_mock.clear_requests() mock_events_list({}, exc=ClientError()) with patch("homeassistant.util.utcnow", return_value=now): async_fire_time_changed(hass, now) await hass.async_block_till_done() # Entity is marked uanvailable due to API failure state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "unavailable" # Advance time past next coordinator update now += datetime.timedelta(minutes=30) aioclient_mock.clear_requests() mock_events_list({ "items": [{ **TEST_EVENT, "start": { "dateTime": (now + datetime.timedelta(minutes=30)).isoformat() }, "end": { "dateTime": (now + datetime.timedelta(minutes=60)).isoformat() }, }] }) with patch("homeassistant.util.utcnow", return_value=now): async_fire_time_changed(hass, now) await hass.async_block_till_done() # State updated with new API response state = hass.states.get(TEST_ENTITY) assert state.name == TEST_ENTITY_NAME assert state.state == "off"