def test_sensor_trending_state_is_not_populate_if_previous_closing_missing( hass): """The trending state is None if _previous_close is None for some reason.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, 12) # Force update _previous_close to None mock_coordinator.data[symbol][DATA_REGULAR_MARKET_PREVIOUS_CLOSE] = None config = copy.deepcopy(DEFAULT_OPTIONAL_CONFIG) config[CONF_SHOW_TRENDING_ICON] = True sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, config) # Accessing `available` triggers data population assert sensor.available is True # ATTR_TRENDING should always reflect the trending status regarding of CONF_SHOW_TRENDING_ICON assert (ATTR_TRENDING in sensor.device_state_attributes) is False # icon is based on the currency currency = sensor.unit_of_measurement lower_currency = currency.lower() assert sensor.icon == f"mdi:currency-{lower_currency}"
def test_conversion_not_attempted_if_target_currency_same(hass): """No conversion is attempted if target curency is the same as symbol currency.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, 12) # Force update _previous_close to None mock_coordinator.data[symbol][DATA_REGULAR_MARKET_PREVIOUS_CLOSE] = None sensor = YahooFinanceSensor( hass, mock_coordinator, { "symbol": symbol, CONF_TARGET_CURRENCY: "USD" }, DEFAULT_OPTIONAL_CONFIG, ) with patch.object(mock_coordinator, "add_symbol") as mock_add_symbol: # Accessing `available` triggers data population assert sensor.available is True # The mock data has currency USD and target is USD too. assert mock_add_symbol.call_count == 0
def test_sensor_creation(hass, last_update_success, symbol, market_price, expected_market_price): """Test sensor status based on the expected_market_price.""" mock_coordinator = build_mock_coordinator(hass, last_update_success, symbol, market_price) sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, DEFAULT_OPTIONAL_CONFIG) # Accessing `available` triggers data population assert sensor.available is last_update_success # state represents the rounded market price assert sensor.state == expected_market_price assert sensor.name == f"Symbol {symbol}" attributes = sensor.device_state_attributes # Sensor would be trending up because _previous_close is 0. assert attributes[ATTR_TRENDING] == "up" # All numeric values besides DATA_REGULAR_MARKET_PRICE should be 0 for numeric_data_key in NUMERIC_DATA_GROUPS: for value in NUMERIC_DATA_GROUPS[numeric_data_key]: key = value[0] if key != DATA_REGULAR_MARKET_PRICE: assert attributes[key] == 0 # Since we did not provide any data so currency should be the default value assert sensor.unit_of_measurement == DEFAULT_CURRENCY assert attributes[ATTR_CURRENCY_SYMBOL] == DEFAULT_CURRENCY_SYMBOL assert sensor.should_poll is False
def test_sensor_trend(hass, market_price, previous_close, show_trending, expected_trend): """Test sensor trending status.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, market_price) mock_coordinator.data[symbol][ DATA_REGULAR_MARKET_PREVIOUS_CLOSE] = previous_close config = copy.deepcopy(DEFAULT_OPTIONAL_CONFIG) config[CONF_SHOW_TRENDING_ICON] = show_trending sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, config) # Accessing `available` triggers data population assert sensor.available is True # ATTR_TRENDING should always reflect the trending status regarding of CONF_SHOW_TRENDING_ICON assert sensor.device_state_attributes[ATTR_TRENDING] == expected_trend if show_trending: assert sensor.icon == f"mdi:trending-{expected_trend}" else: currency = sensor.unit_of_measurement lower_currency = currency.lower() assert sensor.icon == f"mdi:currency-{lower_currency}"
async def test_setup_listener_unregistration(hass): """Test entity listener unregistration.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, 12) sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, DEFAULT_OPTIONAL_CONFIG) await sensor.async_will_remove_from_hass() assert mock_coordinator.async_remove_listener.call_count == 1
async def test_sensor_update_calls_coordinator(hass): """Test sensor data update.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, None) mock_coordinator.async_request_refresh = AsyncMock(return_value=None) sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, DEFAULT_OPTIONAL_CONFIG) await sensor.async_update() assert mock_coordinator.async_request_refresh.call_count == 1
def test_sensor_decimal_placs(hass, market_price, decimal_places, expected_market_price): """Tests numeric value rounding.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, market_price) config = copy.deepcopy(SAMPLE_VALID_CONFIG) config[CONF_DECIMAL_PLACES] = decimal_places sensor = YahooFinanceSensor(hass, mock_coordinator, symbol, config) # Accessing `available` triggers data population assert sensor.available is True # state represents the rounded market price assert sensor.state == expected_market_price
def test_repeated_available(hass): """Test repeated calls to available.""" symbol = "XYZ" market_price = 12 symbol_data = build_mock_symbol_data(symbol, market_price) # symbol_data[DATA_REGULAR_MARKET_PREVIOUS_CLOSE] = market_price mock_coordinator = Mock() mock_data = PropertyMock(return_value=symbol_data) type(mock_coordinator).data = mock_data sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, DEFAULT_OPTIONAL_CONFIG) # Calling available in quick successions results in property updates once assert sensor.available assert sensor.available assert mock_data.call_count == 1
def test_sensor_data_when_coordinator_returns_none(hass): """Test sensor status when data coordinator does not have any data.""" symbol = "XYZ" mock_coordinator = Mock( data=None, hass=hass, last_update_success=False, ) sensor = YahooFinanceSensor(hass, mock_coordinator, {"symbol": symbol}, DEFAULT_OPTIONAL_CONFIG) # Accessing `available` triggers data population assert sensor.available is False assert sensor.state is None # Since we do not have data so the name will be the symbol assert sensor.name == symbol
def test_sensor_data_when_coordinator_is_missing_symbol_data( hass, last_update_success, symbol, market_price): """Test sensor status when data coordinator does not have data for that symbol.""" mock_coordinator = build_mock_coordinator(hass, last_update_success, symbol, market_price) # Create a sensor for some other symbol symbol_to_test = "ABC" sensor = YahooFinanceSensor(hass, mock_coordinator, symbol_to_test, SAMPLE_VALID_CONFIG) # Accessing `available` triggers data population assert sensor.available is last_update_success assert sensor.state is None # Symbol is used as name when there is no data assert sensor.name == symbol_to_test
async def test_data_from_json(hass, mock_json): """Tests data update all the way from from json.""" symbol = "BABA" coordinator = YahooSymbolUpdateCoordinator([symbol], hass, DEFAULT_SCAN_INTERVAL) coordinator.get_json = AsyncMock(return_value=mock_json) await coordinator.async_refresh() await hass.async_block_till_done() sensor = YahooFinanceSensor(hass, coordinator, symbol, SAMPLE_VALID_CONFIG) # Accessing `available` triggers data population assert sensor.available is True attributes = sensor.device_state_attributes assert sensor.state == 232.73 assert attributes["regularMarketChange"] == -5.66 assert attributes["twoHundredDayAverageChangePercent"] == -0.13
def test_conversion_requests_additional_data_from_coordinator(hass): """Numeric values get multiplied based on conversion currency.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator(hass, True, symbol, 12) # Force update _previous_close to None mock_coordinator.data[symbol][DATA_REGULAR_MARKET_PREVIOUS_CLOSE] = None sensor = YahooFinanceSensor( hass, mock_coordinator, {"symbol": symbol, CONF_TARGET_CURRENCY: "EUR"}, SAMPLE_VALID_CONFIG, ) with patch.object(mock_coordinator, "add_symbol") as mock_add_symbol: # Accessing `available` triggers data population assert sensor.available is True assert mock_add_symbol.call_count == 1
def test_conversion(hass): """Numeric values get multiplied based on conversion currency.""" symbol = "XYZ" mock_coordinator = build_mock_coordinator_for_conversion( hass, symbol, 12, "USD", "CHF", 1.5 ) # Force update _previous_close to None mock_coordinator.data[symbol][DATA_REGULAR_MARKET_PREVIOUS_CLOSE] = None sensor = YahooFinanceSensor( hass, mock_coordinator, {"symbol": symbol, CONF_TARGET_CURRENCY: "CHF"}, SAMPLE_VALID_CONFIG, ) # Accessing `available` triggers data population assert sensor.available is True assert sensor.state == (12 * 1.5)