mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add JSON support to load_fixture (#88076)
* Add JSON support to load_fixture * More tests * Remove lru_cache on load_json
This commit is contained in:
parent
bc2b35765e
commit
8c821c8969
@ -18,6 +18,8 @@ JsonValueType = (
|
|||||||
dict[str, "JsonValueType"] | list["JsonValueType"] | str | int | float | bool | None
|
dict[str, "JsonValueType"] | list["JsonValueType"] | str | int | float | bool | None
|
||||||
)
|
)
|
||||||
"""Any data that can be returned by the standard JSON deserializing process."""
|
"""Any data that can be returned by the standard JSON deserializing process."""
|
||||||
|
JsonArrayType = list[JsonValueType]
|
||||||
|
"""List that can be returned by the standard JSON deserializing process."""
|
||||||
JsonObjectType = dict[str, JsonValueType]
|
JsonObjectType = dict[str, JsonValueType]
|
||||||
"""Dictionary that can be returned by the standard JSON deserializing process."""
|
"""Dictionary that can be returned by the standard JSON deserializing process."""
|
||||||
|
|
||||||
@ -34,6 +36,15 @@ json_loads = orjson.loads
|
|||||||
"""Parse JSON data."""
|
"""Parse JSON data."""
|
||||||
|
|
||||||
|
|
||||||
|
def json_loads_array(__obj: bytes | bytearray | memoryview | str) -> JsonArrayType:
|
||||||
|
"""Parse JSON data and ensure result is a list."""
|
||||||
|
value: JsonValueType = json_loads(__obj)
|
||||||
|
# Avoid isinstance overhead as we are not interested in list subclasses
|
||||||
|
if type(value) is list: # pylint: disable=unidiomatic-typecheck
|
||||||
|
return value
|
||||||
|
raise ValueError(f"Expected JSON to be parsed as a list got {type(value)}")
|
||||||
|
|
||||||
|
|
||||||
def json_loads_object(__obj: bytes | bytearray | memoryview | str) -> JsonObjectType:
|
def json_loads_object(__obj: bytes | bytearray | memoryview | str) -> JsonObjectType:
|
||||||
"""Parse JSON data and ensure result is a dictionary."""
|
"""Parse JSON data and ensure result is a dictionary."""
|
||||||
value: JsonValueType = json_loads(__obj)
|
value: JsonValueType = json_loads(__obj)
|
||||||
|
@ -67,6 +67,14 @@ from homeassistant.helpers.typing import ConfigType, StateType
|
|||||||
from homeassistant.setup import setup_component
|
from homeassistant.setup import setup_component
|
||||||
from homeassistant.util.async_ import run_callback_threadsafe
|
from homeassistant.util.async_ import run_callback_threadsafe
|
||||||
import homeassistant.util.dt as date_util
|
import homeassistant.util.dt as date_util
|
||||||
|
from homeassistant.util.json import (
|
||||||
|
JsonArrayType,
|
||||||
|
JsonObjectType,
|
||||||
|
JsonValueType,
|
||||||
|
json_loads,
|
||||||
|
json_loads_array,
|
||||||
|
json_loads_object,
|
||||||
|
)
|
||||||
from homeassistant.util.unit_system import METRIC_SYSTEM
|
from homeassistant.util.unit_system import METRIC_SYSTEM
|
||||||
import homeassistant.util.uuid as uuid_util
|
import homeassistant.util.uuid as uuid_util
|
||||||
import homeassistant.util.yaml.loader as yaml_loader
|
import homeassistant.util.yaml.loader as yaml_loader
|
||||||
@ -428,6 +436,27 @@ def load_fixture(filename: str, integration: str | None = None) -> str:
|
|||||||
return get_fixture_path(filename, integration).read_text()
|
return get_fixture_path(filename, integration).read_text()
|
||||||
|
|
||||||
|
|
||||||
|
def load_json_value_fixture(
|
||||||
|
filename: str, integration: str | None = None
|
||||||
|
) -> JsonValueType:
|
||||||
|
"""Load a JSON value from a fixture."""
|
||||||
|
return json_loads(load_fixture(filename, integration))
|
||||||
|
|
||||||
|
|
||||||
|
def load_json_array_fixture(
|
||||||
|
filename: str, integration: str | None = None
|
||||||
|
) -> JsonArrayType:
|
||||||
|
"""Load a JSON array from a fixture."""
|
||||||
|
return json_loads_array(load_fixture(filename, integration))
|
||||||
|
|
||||||
|
|
||||||
|
def load_json_object_fixture(
|
||||||
|
filename: str, integration: str | None = None
|
||||||
|
) -> JsonObjectType:
|
||||||
|
"""Load a JSON object from a fixture."""
|
||||||
|
return json_loads_object(load_fixture(filename, integration))
|
||||||
|
|
||||||
|
|
||||||
def mock_state_change_event(
|
def mock_state_change_event(
|
||||||
hass: HomeAssistant, new_state: State, old_state: State | None = None
|
hass: HomeAssistant, new_state: State, old_state: State | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
"""Tests for AccuWeather."""
|
"""Tests for AccuWeather."""
|
||||||
import json
|
|
||||||
from unittest.mock import PropertyMock, patch
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
from homeassistant.components.accuweather.const import DOMAIN
|
from homeassistant.components.accuweather.const import DOMAIN
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
from tests.common import (
|
||||||
|
MockConfigEntry,
|
||||||
|
load_json_array_fixture,
|
||||||
|
load_json_object_fixture,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def init_integration(
|
async def init_integration(
|
||||||
@ -28,8 +31,8 @@ async def init_integration(
|
|||||||
options=options,
|
options=options,
|
||||||
)
|
)
|
||||||
|
|
||||||
current = json.loads(load_fixture("accuweather/current_conditions_data.json"))
|
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||||
forecast = json.loads(load_fixture("accuweather/forecast_data.json"))
|
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||||
|
|
||||||
if unsupported_icon:
|
if unsupported_icon:
|
||||||
current["WeatherIcon"] = 999
|
current["WeatherIcon"] = 999
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
"""Define tests for the AccuWeather config flow."""
|
"""Define tests for the AccuWeather config flow."""
|
||||||
import json
|
|
||||||
from unittest.mock import PropertyMock, patch
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
from accuweather import ApiError, InvalidApiKeyError, RequestsExceededError
|
from accuweather import ApiError, InvalidApiKeyError, RequestsExceededError
|
||||||
@ -10,7 +9,7 @@ from homeassistant.config_entries import SOURCE_USER
|
|||||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, load_fixture
|
from tests.common import MockConfigEntry, load_json_object_fixture
|
||||||
|
|
||||||
VALID_CONFIG = {
|
VALID_CONFIG = {
|
||||||
CONF_NAME: "abcd",
|
CONF_NAME: "abcd",
|
||||||
@ -99,7 +98,7 @@ async def test_integration_already_exists(hass: HomeAssistant) -> None:
|
|||||||
"""Test we only allow a single config flow."""
|
"""Test we only allow a single config flow."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||||
return_value=json.loads(load_fixture("accuweather/location_data.json")),
|
return_value=load_json_object_fixture("accuweather/location_data.json"),
|
||||||
):
|
):
|
||||||
MockConfigEntry(
|
MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
@ -121,7 +120,7 @@ async def test_create_entry(hass: HomeAssistant) -> None:
|
|||||||
"""Test that the user step works."""
|
"""Test that the user step works."""
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||||
return_value=json.loads(load_fixture("accuweather/location_data.json")),
|
return_value=load_json_object_fixture("accuweather/location_data.json"),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.accuweather.async_setup_entry", return_value=True
|
"homeassistant.components.accuweather.async_setup_entry", return_value=True
|
||||||
):
|
):
|
||||||
@ -150,11 +149,11 @@ async def test_options_flow(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
"homeassistant.components.accuweather.AccuWeather._async_get_data",
|
||||||
return_value=json.loads(load_fixture("accuweather/location_data.json")),
|
return_value=load_json_object_fixture("accuweather/location_data.json"),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||||
return_value=json.loads(
|
return_value=load_json_object_fixture(
|
||||||
load_fixture("accuweather/current_conditions_data.json")
|
"accuweather/current_conditions_data.json"
|
||||||
),
|
),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.async_get_forecast"
|
"homeassistant.components.accuweather.AccuWeather.async_get_forecast"
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
"""Test AccuWeather diagnostics."""
|
"""Test AccuWeather diagnostics."""
|
||||||
import json
|
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
from . import init_integration
|
from . import init_integration
|
||||||
|
|
||||||
from tests.common import load_fixture
|
from tests.common import load_json_object_fixture
|
||||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||||
from tests.typing import ClientSessionGenerator
|
from tests.typing import ClientSessionGenerator
|
||||||
|
|
||||||
@ -16,9 +15,10 @@ async def test_entry_diagnostics(
|
|||||||
"""Test config entry diagnostics."""
|
"""Test config entry diagnostics."""
|
||||||
entry = await init_integration(hass)
|
entry = await init_integration(hass)
|
||||||
|
|
||||||
coordinator_data = json.loads(
|
coordinator_data = load_json_object_fixture(
|
||||||
load_fixture("current_conditions_data.json", "accuweather")
|
"current_conditions_data.json", "accuweather"
|
||||||
)
|
)
|
||||||
|
|
||||||
coordinator_data["forecast"] = {}
|
coordinator_data["forecast"] = {}
|
||||||
|
|
||||||
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
|
result = await get_diagnostics_for_config_entry(hass, hass_client, entry)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Test init of AccuWeather integration."""
|
"""Test init of AccuWeather integration."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import json
|
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from accuweather import ApiError
|
from accuweather import ApiError
|
||||||
@ -13,7 +12,12 @@ from homeassistant.util.dt import utcnow
|
|||||||
|
|
||||||
from . import init_integration
|
from . import init_integration
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
|
from tests.common import (
|
||||||
|
MockConfigEntry,
|
||||||
|
async_fire_time_changed,
|
||||||
|
load_json_array_fixture,
|
||||||
|
load_json_object_fixture,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_async_setup_entry(hass: HomeAssistant) -> None:
|
async def test_async_setup_entry(hass: HomeAssistant) -> None:
|
||||||
@ -69,7 +73,7 @@ async def test_update_interval(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert entry.state is ConfigEntryState.LOADED
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
current = json.loads(load_fixture("accuweather/current_conditions_data.json"))
|
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||||
future = utcnow() + timedelta(minutes=40)
|
future = utcnow() + timedelta(minutes=40)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
@ -90,8 +94,8 @@ async def test_update_interval_forecast(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
assert entry.state is ConfigEntryState.LOADED
|
assert entry.state is ConfigEntryState.LOADED
|
||||||
|
|
||||||
current = json.loads(load_fixture("accuweather/current_conditions_data.json"))
|
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||||
forecast = json.loads(load_fixture("accuweather/forecast_data.json"))
|
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||||
future = utcnow() + timedelta(minutes=80)
|
future = utcnow() + timedelta(minutes=80)
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Test sensor of AccuWeather integration."""
|
"""Test sensor of AccuWeather integration."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import json
|
|
||||||
from unittest.mock import PropertyMock, patch
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
from homeassistant.components.accuweather.const import ATTRIBUTION, DOMAIN
|
from homeassistant.components.accuweather.const import ATTRIBUTION, DOMAIN
|
||||||
@ -35,7 +34,11 @@ from homeassistant.util.unit_system import US_CUSTOMARY_SYSTEM
|
|||||||
|
|
||||||
from . import init_integration
|
from . import init_integration
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed, load_fixture
|
from tests.common import (
|
||||||
|
async_fire_time_changed,
|
||||||
|
load_json_array_fixture,
|
||||||
|
load_json_object_fixture,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_sensor_without_forecast(hass: HomeAssistant) -> None:
|
async def test_sensor_without_forecast(hass: HomeAssistant) -> None:
|
||||||
@ -684,8 +687,8 @@ async def test_availability(hass: HomeAssistant) -> None:
|
|||||||
future = utcnow() + timedelta(minutes=120)
|
future = utcnow() + timedelta(minutes=120)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||||
return_value=json.loads(
|
return_value=load_json_object_fixture(
|
||||||
load_fixture("accuweather/current_conditions_data.json")
|
"accuweather/current_conditions_data.json"
|
||||||
),
|
),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||||
@ -707,8 +710,8 @@ async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
await async_setup_component(hass, "homeassistant", {})
|
await async_setup_component(hass, "homeassistant", {})
|
||||||
|
|
||||||
current = json.loads(load_fixture("accuweather/current_conditions_data.json"))
|
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||||
forecast = json.loads(load_fixture("accuweather/forecast_data.json"))
|
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||||
@ -755,8 +758,8 @@ async def test_state_update(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
future = utcnow() + timedelta(minutes=60)
|
future = utcnow() + timedelta(minutes=60)
|
||||||
|
|
||||||
current_condition = json.loads(
|
current_condition = load_json_object_fixture(
|
||||||
load_fixture("accuweather/current_conditions_data.json")
|
"accuweather/current_conditions_data.json"
|
||||||
)
|
)
|
||||||
current_condition["Ceiling"]["Metric"]["Value"] = 3300
|
current_condition["Ceiling"]["Metric"]["Value"] = 3300
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Test weather of AccuWeather integration."""
|
"""Test weather of AccuWeather integration."""
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import json
|
|
||||||
from unittest.mock import PropertyMock, patch
|
from unittest.mock import PropertyMock, patch
|
||||||
|
|
||||||
from homeassistant.components.accuweather.const import ATTRIBUTION
|
from homeassistant.components.accuweather.const import ATTRIBUTION
|
||||||
@ -30,7 +29,11 @@ from homeassistant.util.dt import utcnow
|
|||||||
|
|
||||||
from . import init_integration
|
from . import init_integration
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed, load_fixture
|
from tests.common import (
|
||||||
|
async_fire_time_changed,
|
||||||
|
load_json_array_fixture,
|
||||||
|
load_json_object_fixture,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_weather_without_forecast(hass: HomeAssistant) -> None:
|
async def test_weather_without_forecast(hass: HomeAssistant) -> None:
|
||||||
@ -111,8 +114,8 @@ async def test_availability(hass: HomeAssistant) -> None:
|
|||||||
future = utcnow() + timedelta(minutes=120)
|
future = utcnow() + timedelta(minutes=120)
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||||
return_value=json.loads(
|
return_value=load_json_object_fixture(
|
||||||
load_fixture("accuweather/current_conditions_data.json")
|
"accuweather/current_conditions_data.json"
|
||||||
),
|
),
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
"homeassistant.components.accuweather.AccuWeather.requests_remaining",
|
||||||
@ -134,8 +137,8 @@ async def test_manual_update_entity(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
await async_setup_component(hass, "homeassistant", {})
|
await async_setup_component(hass, "homeassistant", {})
|
||||||
|
|
||||||
current = json.loads(load_fixture("accuweather/current_conditions_data.json"))
|
current = load_json_object_fixture("accuweather/current_conditions_data.json")
|
||||||
forecast = json.loads(load_fixture("accuweather/forecast_data.json"))
|
forecast = load_json_array_fixture("accuweather/forecast_data.json")
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
"homeassistant.components.accuweather.AccuWeather.async_get_current_conditions",
|
||||||
|
@ -15,6 +15,7 @@ from homeassistant.helpers.json import JSONEncoder as DefaultHASSJSONEncoder
|
|||||||
from homeassistant.util.json import (
|
from homeassistant.util.json import (
|
||||||
SerializationError,
|
SerializationError,
|
||||||
find_paths_unserializable_data,
|
find_paths_unserializable_data,
|
||||||
|
json_loads_array,
|
||||||
json_loads_object,
|
json_loads_object,
|
||||||
load_json,
|
load_json,
|
||||||
save_json,
|
save_json,
|
||||||
@ -194,6 +195,23 @@ def test_find_unserializable_data() -> None:
|
|||||||
) == {"$(BadData).bla": bad_data}
|
) == {"$(BadData).bla": bad_data}
|
||||||
|
|
||||||
|
|
||||||
|
def test_json_loads_array() -> None:
|
||||||
|
"""Test json_loads_array validates result."""
|
||||||
|
assert json_loads_array('[{"c":1.2}]') == [{"c": 1.2}]
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError, match="Expected JSON to be parsed as a list got <class 'dict'>"
|
||||||
|
):
|
||||||
|
json_loads_array("{}")
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError, match="Expected JSON to be parsed as a list got <class 'bool'>"
|
||||||
|
):
|
||||||
|
json_loads_array("true")
|
||||||
|
with pytest.raises(
|
||||||
|
ValueError, match="Expected JSON to be parsed as a list got <class 'NoneType'>"
|
||||||
|
):
|
||||||
|
json_loads_array("null")
|
||||||
|
|
||||||
|
|
||||||
def test_json_loads_object() -> None:
|
def test_json_loads_object() -> None:
|
||||||
"""Test json_loads_object validates result."""
|
"""Test json_loads_object validates result."""
|
||||||
assert json_loads_object('{"c":1.2}') == {"c": 1.2}
|
assert json_loads_object('{"c":1.2}') == {"c": 1.2}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user