mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Fix test framework running against real devices (#1235)
This commit is contained in:
parent
32671da9e9
commit
71ae06fa83
@ -320,6 +320,11 @@ class Device(ABC):
|
|||||||
def model(self) -> str:
|
def model(self) -> str:
|
||||||
"""Returns the device model."""
|
"""Returns the device model."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def _model_region(self) -> str:
|
||||||
|
"""Return device full model name and region."""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def alias(self) -> str | None:
|
def alias(self) -> str | None:
|
||||||
|
@ -455,6 +455,12 @@ class IotDevice(Device):
|
|||||||
sys_info = self._sys_info
|
sys_info = self._sys_info
|
||||||
return str(sys_info["model"])
|
return str(sys_info["model"])
|
||||||
|
|
||||||
|
@property
|
||||||
|
@requires_update
|
||||||
|
def _model_region(self) -> str:
|
||||||
|
"""Return device full model name and region."""
|
||||||
|
return self.model
|
||||||
|
|
||||||
@property # type: ignore
|
@property # type: ignore
|
||||||
def alias(self) -> str | None:
|
def alias(self) -> str | None:
|
||||||
"""Return device name (alias)."""
|
"""Return device name (alias)."""
|
||||||
|
@ -492,6 +492,17 @@ class SmartDevice(Device):
|
|||||||
"""Returns the device model."""
|
"""Returns the device model."""
|
||||||
return str(self._info.get("model"))
|
return str(self._info.get("model"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _model_region(self) -> str:
|
||||||
|
"""Return device full model name and region."""
|
||||||
|
if (disco := self._discovery_info) and (
|
||||||
|
disco_model := disco.get("device_model")
|
||||||
|
):
|
||||||
|
return disco_model
|
||||||
|
# Some devices have the region in the specs element.
|
||||||
|
region = f"({specs})" if (specs := self._info.get("specs")) else ""
|
||||||
|
return f"{self.model}{region}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def alias(self) -> str | None:
|
def alias(self) -> str | None:
|
||||||
"""Returns the device alias or nickname."""
|
"""Returns the device alias or nickname."""
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -142,7 +143,7 @@ ALL_DEVICES_SMART = (
|
|||||||
)
|
)
|
||||||
ALL_DEVICES = ALL_DEVICES_IOT.union(ALL_DEVICES_SMART)
|
ALL_DEVICES = ALL_DEVICES_IOT.union(ALL_DEVICES_SMART)
|
||||||
|
|
||||||
IP_MODEL_CACHE: dict[str, str] = {}
|
IP_FIXTURE_CACHE: dict[str, FixtureInfo] = {}
|
||||||
|
|
||||||
|
|
||||||
def parametrize_combine(parametrized: list[pytest.MarkDecorator]):
|
def parametrize_combine(parametrized: list[pytest.MarkDecorator]):
|
||||||
@ -448,6 +449,39 @@ def get_fixture_info(fixture, protocol):
|
|||||||
return fixture_info
|
return fixture_info
|
||||||
|
|
||||||
|
|
||||||
|
def get_nearest_fixture_to_ip(dev):
|
||||||
|
if isinstance(dev, SmartDevice):
|
||||||
|
protocol_fixtures = filter_fixtures("", protocol_filter={"SMART"})
|
||||||
|
elif isinstance(dev, SmartCamera):
|
||||||
|
protocol_fixtures = filter_fixtures("", protocol_filter={"SMARTCAMERA"})
|
||||||
|
else:
|
||||||
|
protocol_fixtures = filter_fixtures("", protocol_filter={"IOT"})
|
||||||
|
assert protocol_fixtures, "Unknown device type"
|
||||||
|
|
||||||
|
# This will get the best fixture with a match on model region
|
||||||
|
if model_region_fixtures := filter_fixtures(
|
||||||
|
"", model_filter={dev._model_region}, fixture_list=protocol_fixtures
|
||||||
|
):
|
||||||
|
return next(iter(model_region_fixtures))
|
||||||
|
|
||||||
|
# This will get the best fixture based on model starting with the name.
|
||||||
|
if "(" in dev.model:
|
||||||
|
model, _, _ = dev.model.partition("(")
|
||||||
|
else:
|
||||||
|
model = dev.model
|
||||||
|
if model_fixtures := filter_fixtures(
|
||||||
|
"", model_startswith_filter=model, fixture_list=protocol_fixtures
|
||||||
|
):
|
||||||
|
return next(iter(model_fixtures))
|
||||||
|
|
||||||
|
if device_type_fixtures := filter_fixtures(
|
||||||
|
"", device_type_filter={dev.device_type}, fixture_list=protocol_fixtures
|
||||||
|
):
|
||||||
|
return next(iter(device_type_fixtures))
|
||||||
|
|
||||||
|
return next(iter(protocol_fixtures))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(params=filter_fixtures("main devices"), ids=idgenerator)
|
@pytest.fixture(params=filter_fixtures("main devices"), ids=idgenerator)
|
||||||
async def dev(request) -> AsyncGenerator[Device, None]:
|
async def dev(request) -> AsyncGenerator[Device, None]:
|
||||||
"""Device fixture.
|
"""Device fixture.
|
||||||
@ -459,24 +493,28 @@ async def dev(request) -> AsyncGenerator[Device, None]:
|
|||||||
dev: Device
|
dev: Device
|
||||||
|
|
||||||
ip = request.config.getoption("--ip")
|
ip = request.config.getoption("--ip")
|
||||||
username = request.config.getoption("--username")
|
username = request.config.getoption("--username") or os.environ.get("KASA_USERNAME")
|
||||||
password = request.config.getoption("--password")
|
password = request.config.getoption("--password") or os.environ.get("KASA_PASSWORD")
|
||||||
if ip:
|
if ip:
|
||||||
model = IP_MODEL_CACHE.get(ip)
|
fixture = IP_FIXTURE_CACHE.get(ip)
|
||||||
d = None
|
|
||||||
if not model:
|
|
||||||
d = await _discover_update_and_close(ip, username, password)
|
|
||||||
IP_MODEL_CACHE[ip] = model = d.model
|
|
||||||
|
|
||||||
if model not in fixture_data.name:
|
d = None
|
||||||
|
if not fixture:
|
||||||
|
d = await _discover_update_and_close(ip, username, password)
|
||||||
|
IP_FIXTURE_CACHE[ip] = fixture = get_nearest_fixture_to_ip(d)
|
||||||
|
assert fixture
|
||||||
|
if fixture.name != fixture_data.name:
|
||||||
pytest.skip(f"skipping file {fixture_data.name}")
|
pytest.skip(f"skipping file {fixture_data.name}")
|
||||||
dev = d if d else await _discover_update_and_close(ip, username, password)
|
dev = None
|
||||||
|
else:
|
||||||
|
dev = d if d else await _discover_update_and_close(ip, username, password)
|
||||||
else:
|
else:
|
||||||
dev = await get_device_for_fixture(fixture_data)
|
dev = await get_device_for_fixture(fixture_data)
|
||||||
|
|
||||||
yield dev
|
yield dev
|
||||||
|
|
||||||
await dev.disconnect()
|
if dev:
|
||||||
|
await dev.disconnect()
|
||||||
|
|
||||||
|
|
||||||
def get_parent_and_child_modules(device: Device, module_name):
|
def get_parent_and_child_modules(device: Device, module_name):
|
||||||
|
@ -104,8 +104,10 @@ def filter_fixtures(
|
|||||||
data_root_filter: str | None = None,
|
data_root_filter: str | None = None,
|
||||||
protocol_filter: set[str] | None = None,
|
protocol_filter: set[str] | None = None,
|
||||||
model_filter: set[str] | None = None,
|
model_filter: set[str] | None = None,
|
||||||
|
model_startswith_filter: str | None = None,
|
||||||
component_filter: str | ComponentFilter | None = None,
|
component_filter: str | ComponentFilter | None = None,
|
||||||
device_type_filter: Iterable[DeviceType] | None = None,
|
device_type_filter: Iterable[DeviceType] | None = None,
|
||||||
|
fixture_list: list[FixtureInfo] = FIXTURE_DATA,
|
||||||
):
|
):
|
||||||
"""Filter the fixtures based on supplied parameters.
|
"""Filter the fixtures based on supplied parameters.
|
||||||
|
|
||||||
@ -127,12 +129,15 @@ def filter_fixtures(
|
|||||||
and (model := model_filter_list[0])
|
and (model := model_filter_list[0])
|
||||||
and len(model.split("_")) == 3
|
and len(model.split("_")) == 3
|
||||||
):
|
):
|
||||||
# return exact match
|
# filter string includes hw and fw, return exact match
|
||||||
return fixture_data.name == f"{model}.json"
|
return fixture_data.name == f"{model}.json"
|
||||||
file_model_region = fixture_data.name.split("_")[0]
|
file_model_region = fixture_data.name.split("_")[0]
|
||||||
file_model = file_model_region.split("(")[0]
|
file_model = file_model_region.split("(")[0]
|
||||||
return file_model in model_filter
|
return file_model in model_filter
|
||||||
|
|
||||||
|
def _model_startswith_match(fixture_data: FixtureInfo, starts_with: str):
|
||||||
|
return fixture_data.name.startswith(starts_with)
|
||||||
|
|
||||||
def _component_match(
|
def _component_match(
|
||||||
fixture_data: FixtureInfo, component_filter: str | ComponentFilter
|
fixture_data: FixtureInfo, component_filter: str | ComponentFilter
|
||||||
):
|
):
|
||||||
@ -175,13 +180,17 @@ def filter_fixtures(
|
|||||||
filtered = []
|
filtered = []
|
||||||
if protocol_filter is None:
|
if protocol_filter is None:
|
||||||
protocol_filter = {"IOT", "SMART"}
|
protocol_filter = {"IOT", "SMART"}
|
||||||
for fixture_data in FIXTURE_DATA:
|
for fixture_data in fixture_list:
|
||||||
if data_root_filter and data_root_filter not in fixture_data.data:
|
if data_root_filter and data_root_filter not in fixture_data.data:
|
||||||
continue
|
continue
|
||||||
if fixture_data.protocol not in protocol_filter:
|
if fixture_data.protocol not in protocol_filter:
|
||||||
continue
|
continue
|
||||||
if model_filter is not None and not _model_match(fixture_data, model_filter):
|
if model_filter is not None and not _model_match(fixture_data, model_filter):
|
||||||
continue
|
continue
|
||||||
|
if model_startswith_filter is not None and not _model_startswith_match(
|
||||||
|
fixture_data, model_startswith_filter
|
||||||
|
):
|
||||||
|
continue
|
||||||
if component_filter and not _component_match(fixture_data, component_filter):
|
if component_filter and not _component_match(fixture_data, component_filter):
|
||||||
continue
|
continue
|
||||||
if device_type_filter and not _device_type_match(
|
if device_type_filter and not _device_type_match(
|
||||||
@ -191,8 +200,9 @@ def filter_fixtures(
|
|||||||
|
|
||||||
filtered.append(fixture_data)
|
filtered.append(fixture_data)
|
||||||
|
|
||||||
print(f"# {desc}")
|
if desc:
|
||||||
for value in filtered:
|
print(f"# {desc}")
|
||||||
print(f"\t{value.name}")
|
for value in filtered:
|
||||||
|
print(f"\t{value.name}")
|
||||||
filtered.sort()
|
filtered.sort()
|
||||||
return filtered
|
return filtered
|
||||||
|
@ -74,6 +74,7 @@ async def test_update_available_without_cloud(dev: SmartDevice):
|
|||||||
pytest.param(False, pytest.raises(KasaException), id="not-available"),
|
pytest.param(False, pytest.raises(KasaException), id="not-available"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@pytest.mark.requires_dummy()
|
||||||
async def test_firmware_update(
|
async def test_firmware_update(
|
||||||
dev: SmartDevice,
|
dev: SmartDevice,
|
||||||
mocker: MockerFixture,
|
mocker: MockerFixture,
|
||||||
|
@ -29,6 +29,8 @@ from kasa.exceptions import (
|
|||||||
)
|
)
|
||||||
from kasa.httpclient import HttpClient
|
from kasa.httpclient import HttpClient
|
||||||
|
|
||||||
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
|
||||||
DUMMY_QUERY = {"foobar": {"foo": "bar", "bar": "foo"}}
|
DUMMY_QUERY = {"foobar": {"foo": "bar", "bar": "foo"}}
|
||||||
|
|
||||||
key = b"8\x89\x02\xfa\xf5Xs\x1c\xa1 H\x9a\x82\xc7\xd9\t"
|
key = b"8\x89\x02\xfa\xf5Xs\x1c\xa1 H\x9a\x82\xc7\xd9\t"
|
||||||
|
@ -32,7 +32,7 @@ from .conftest import (
|
|||||||
from .test_iotdevice import SYSINFO_SCHEMA
|
from .test_iotdevice import SYSINFO_SCHEMA
|
||||||
|
|
||||||
|
|
||||||
@bulb
|
@bulb_iot
|
||||||
async def test_bulb_sysinfo(dev: Device):
|
async def test_bulb_sysinfo(dev: Device):
|
||||||
assert dev.sys_info is not None
|
assert dev.sys_info is not None
|
||||||
SYSINFO_SCHEMA_BULB(dev.sys_info)
|
SYSINFO_SCHEMA_BULB(dev.sys_info)
|
||||||
|
@ -125,8 +125,13 @@ async def test_parent_property(dev: Device):
|
|||||||
|
|
||||||
|
|
||||||
@has_children_smart
|
@has_children_smart
|
||||||
|
@pytest.mark.requires_dummy()
|
||||||
async def test_child_time(dev: Device, freezer: FrozenDateTimeFactory):
|
async def test_child_time(dev: Device, freezer: FrozenDateTimeFactory):
|
||||||
"""Test a child device gets the time from it's parent module."""
|
"""Test a child device gets the time from it's parent module.
|
||||||
|
|
||||||
|
This is excluded from real device testing as the test often fail if the
|
||||||
|
device time is not in the past.
|
||||||
|
"""
|
||||||
if not dev.children:
|
if not dev.children:
|
||||||
pytest.skip(f"Device {dev} fixture does not have any children")
|
pytest.skip(f"Device {dev} fixture does not have any children")
|
||||||
|
|
||||||
|
@ -51,6 +51,10 @@ from .conftest import (
|
|||||||
turn_on,
|
turn_on,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# The cli tests should be testing the cli logic rather than a physical device
|
||||||
|
# so mark the whole file for skipping with real devices.
|
||||||
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def runner():
|
def runner():
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from freezegun.api import FrozenDateTimeFactory
|
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
@ -326,22 +325,38 @@ async def test_light_preset_save(dev: Device, mocker: MockerFixture):
|
|||||||
assert new_preset_state.color_temp == new_preset.color_temp
|
assert new_preset_state.color_temp == new_preset.color_temp
|
||||||
|
|
||||||
|
|
||||||
async def test_set_time(dev: Device, freezer: FrozenDateTimeFactory):
|
async def test_set_time(dev: Device):
|
||||||
"""Test setting the device time."""
|
"""Test setting the device time."""
|
||||||
freezer.move_to("2021-01-09 12:00:00+00:00")
|
|
||||||
time_mod = dev.modules[Module.Time]
|
time_mod = dev.modules[Module.Time]
|
||||||
tz_info = time_mod.timezone
|
|
||||||
now = datetime.now(tz=tz_info)
|
|
||||||
now = now.replace(microsecond=0)
|
|
||||||
assert time_mod.time != now
|
|
||||||
|
|
||||||
await time_mod.set_time(now)
|
original_time = time_mod.time
|
||||||
await dev.update()
|
original_timezone = time_mod.timezone
|
||||||
assert time_mod.time == now
|
|
||||||
|
|
||||||
zone = ZoneInfo("Europe/Berlin")
|
test_time = datetime.fromisoformat("2021-01-09 12:00:00+00:00")
|
||||||
now = datetime.now(tz=zone)
|
test_time = test_time.astimezone(original_timezone)
|
||||||
now = now.replace(microsecond=0)
|
|
||||||
await time_mod.set_time(now)
|
try:
|
||||||
await dev.update()
|
assert time_mod.time != test_time
|
||||||
assert time_mod.time == now
|
|
||||||
|
await time_mod.set_time(test_time)
|
||||||
|
await dev.update()
|
||||||
|
assert time_mod.time == test_time
|
||||||
|
|
||||||
|
if (
|
||||||
|
isinstance(original_timezone, ZoneInfo)
|
||||||
|
and original_timezone.key != "Europe/Berlin"
|
||||||
|
):
|
||||||
|
test_zonezone = ZoneInfo("Europe/Berlin")
|
||||||
|
else:
|
||||||
|
test_zonezone = ZoneInfo("Europe/London")
|
||||||
|
|
||||||
|
# Just update the timezone
|
||||||
|
new_time = time_mod.time.astimezone(test_zonezone)
|
||||||
|
await time_mod.set_time(new_time)
|
||||||
|
await dev.update()
|
||||||
|
assert time_mod.time == new_time
|
||||||
|
finally:
|
||||||
|
# Reset back to the original
|
||||||
|
await time_mod.set_time(original_time)
|
||||||
|
await dev.update()
|
||||||
|
assert time_mod.time == original_time
|
||||||
|
@ -35,6 +35,10 @@ from kasa.discover import DiscoveryResult
|
|||||||
|
|
||||||
from .conftest import DISCOVERY_MOCK_IP
|
from .conftest import DISCOVERY_MOCK_IP
|
||||||
|
|
||||||
|
# Device Factory tests are not relevant for real devices which run against
|
||||||
|
# a single device that has already been created via the factory.
|
||||||
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
|
||||||
|
|
||||||
def _get_connection_type_device_class(discovery_info):
|
def _get_connection_type_device_class(discovery_info):
|
||||||
if "result" in discovery_info:
|
if "result" in discovery_info:
|
||||||
|
@ -53,6 +53,9 @@ from .conftest import (
|
|||||||
wallswitch_iot,
|
wallswitch_iot,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# A physical device has to respond to discovery for the tests to work.
|
||||||
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
|
||||||
UNSUPPORTED = {
|
UNSUPPORTED = {
|
||||||
"result": {
|
"result": {
|
||||||
"device_id": "xx",
|
"device_id": "xx",
|
||||||
|
@ -32,6 +32,9 @@ from kasa.smartprotocol import SmartProtocol
|
|||||||
|
|
||||||
DUMMY_QUERY = {"foobar": {"foo": "bar", "bar": "foo"}}
|
DUMMY_QUERY = {"foobar": {"foo": "bar", "bar": "foo"}}
|
||||||
|
|
||||||
|
# Transport tests are not designed for real devices
|
||||||
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
|
||||||
|
|
||||||
class _mock_response:
|
class _mock_response:
|
||||||
def __init__(self, status, content: bytes):
|
def __init__(self, status, content: bytes):
|
||||||
|
@ -687,10 +687,13 @@ def test_deprecated_protocol():
|
|||||||
@device_iot
|
@device_iot
|
||||||
async def test_iot_queries_redaction(dev: IotDevice, caplog: pytest.LogCaptureFixture):
|
async def test_iot_queries_redaction(dev: IotDevice, caplog: pytest.LogCaptureFixture):
|
||||||
"""Test query sensitive info redaction."""
|
"""Test query sensitive info redaction."""
|
||||||
device_id = "123456789ABCDEF"
|
if isinstance(dev.protocol._transport, FakeIotTransport):
|
||||||
cast(FakeIotTransport, dev.protocol._transport).proto["system"]["get_sysinfo"][
|
device_id = "123456789ABCDEF"
|
||||||
"deviceId"
|
cast(FakeIotTransport, dev.protocol._transport).proto["system"]["get_sysinfo"][
|
||||||
] = device_id
|
"deviceId"
|
||||||
|
] = device_id
|
||||||
|
else: # real device with --ip
|
||||||
|
device_id = dev.sys_info["deviceId"]
|
||||||
|
|
||||||
# Info no message logging
|
# Info no message logging
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
|
@ -26,6 +26,7 @@ from .conftest import (
|
|||||||
|
|
||||||
|
|
||||||
@device_smart
|
@device_smart
|
||||||
|
@pytest.mark.requires_dummy()
|
||||||
async def test_try_get_response(dev: SmartDevice, caplog):
|
async def test_try_get_response(dev: SmartDevice, caplog):
|
||||||
mock_response: dict = {
|
mock_response: dict = {
|
||||||
"get_device_info": SmartErrorCode.PARAMS_ERROR,
|
"get_device_info": SmartErrorCode.PARAMS_ERROR,
|
||||||
@ -37,6 +38,7 @@ async def test_try_get_response(dev: SmartDevice, caplog):
|
|||||||
|
|
||||||
|
|
||||||
@device_smart
|
@device_smart
|
||||||
|
@pytest.mark.requires_dummy()
|
||||||
async def test_update_no_device_info(dev: SmartDevice, mocker: MockerFixture):
|
async def test_update_no_device_info(dev: SmartDevice, mocker: MockerFixture):
|
||||||
mock_response: dict = {
|
mock_response: dict = {
|
||||||
"get_device_usage": {},
|
"get_device_usage": {},
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import cast
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_mock
|
import pytest_mock
|
||||||
@ -420,10 +419,11 @@ async def test_smart_queries_redaction(
|
|||||||
dev: SmartDevice, caplog: pytest.LogCaptureFixture
|
dev: SmartDevice, caplog: pytest.LogCaptureFixture
|
||||||
):
|
):
|
||||||
"""Test query sensitive info redaction."""
|
"""Test query sensitive info redaction."""
|
||||||
device_id = "123456789ABCDEF"
|
if isinstance(dev.protocol._transport, FakeSmartTransport):
|
||||||
cast(FakeSmartTransport, dev.protocol._transport).info["get_device_info"][
|
device_id = "123456789ABCDEF"
|
||||||
"device_id"
|
dev.protocol._transport.info["get_device_info"]["device_id"] = device_id
|
||||||
] = device_id
|
else: # real device
|
||||||
|
device_id = dev.device_id
|
||||||
|
|
||||||
# Info no message logging
|
# Info no message logging
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
|
@ -27,6 +27,9 @@ from kasa.experimental.sslaestransport import (
|
|||||||
from kasa.httpclient import HttpClient
|
from kasa.httpclient import HttpClient
|
||||||
from kasa.protocol import DEFAULT_CREDENTIALS, get_default_credentials
|
from kasa.protocol import DEFAULT_CREDENTIALS, get_default_credentials
|
||||||
|
|
||||||
|
# Transport tests are not designed for real devices
|
||||||
|
pytestmark = [pytest.mark.requires_dummy]
|
||||||
|
|
||||||
MOCK_ADMIN_USER = get_default_credentials(DEFAULT_CREDENTIALS["TAPOCAMERA"]).username
|
MOCK_ADMIN_USER = get_default_credentials(DEFAULT_CREDENTIALS["TAPOCAMERA"]).username
|
||||||
MOCK_PWD = "correct_pwd" # noqa: S105
|
MOCK_PWD = "correct_pwd" # noqa: S105
|
||||||
MOCK_USER = "mock@example.com"
|
MOCK_USER = "mock@example.com"
|
||||||
|
Loading…
Reference in New Issue
Block a user