Skip to content

Commit

Permalink
Merge pull request #169 from dknowles2/codes
Browse files Browse the repository at this point in the history
Shore up test coverage
  • Loading branch information
dknowles2 authored Aug 12, 2024
2 parents cfdb196 + 601e29c commit b989968
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 57 deletions.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ readme = { file = ["README.md"], content-type = "text/markdown" }
[tool.setuptools_scm]
write_to = "pyschlage/_version.py"

[tool.coverage.report]
omit = ["pyschlage/_version.py"]

[tool.isort]
profile = "black"
combine_as_imports = true
Expand Down
2 changes: 1 addition & 1 deletion pyschlage/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def wrapper(*args, **kwargs) -> requests.Response:
raise NotAuthorizedError(
resp_err.get("Message", "Not authorized")
) from ex
raise UnknownError(str(ex)) from ex
raise UnknownError(str(ex)) from ex # pragma: no cover

return wrapper

Expand Down
7 changes: 3 additions & 4 deletions pyschlage/code.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class RecurringSchedule:
"""Minute at which the access code is disabled."""

@classmethod
def from_json(cls, json) -> RecurringSchedule | None:
def from_json(cls, json: dict[str, Any] | None) -> RecurringSchedule | None:
"""Creates a RecurringSchedule from a JSON dict.
:meta private:
Expand Down Expand Up @@ -179,7 +179,7 @@ def request_path(device_id: str, access_code_id: str | None = None) -> str:
"""
path = f"devices/{device_id}/storage/accesscode"
if access_code_id:
return f"{path}/{access_code_id}"
return f"{path}/{access_code_id}" # pragma: no cover
return path

@classmethod
Expand Down Expand Up @@ -250,7 +250,6 @@ def save(self):
if self._notification is None:
self._notification = Notification(
_auth=self._auth,
_device=self._device,
notification_id=f"{self._auth.user_id}_{self.access_code_id}",
user_id=self._auth.user_id,
device_id=self.device_id,
Expand All @@ -259,7 +258,7 @@ def save(self):
)
self._notification.filter_value = self.name
self._notification.active = self.notify_on_use
self._notification.save(self._device)
self._notification.save()

def delete(self):
"""Deletes the access code.
Expand Down
8 changes: 4 additions & 4 deletions pyschlage/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ def _get_access_codes(self) -> Iterable[AccessCode]:
if notification.notification_type == ON_UNLOCK_ACTION:
if not notification.notification_id.startswith(self._auth.user_id):
# This shouldn't happen, but ignore it just in case.
continue
continue # pragma: no cover
access_code_id = notification.notification_id[user_id_len + 1 :]
notifications[access_code_id] = notification
path = AccessCode.request_path(self.device_id)
Expand All @@ -352,12 +352,12 @@ def _get_access_codes(self) -> Iterable[AccessCode]:

def _get_notifications(self) -> Iterable[Notification]:
if not self._auth:
raise NotAuthenticatedError
raise NotAuthenticatedError # pragma: no cover
path = Notification.request_path()
params = {"deviceId": self.device_id}
resp = self._auth.request("get", path, params=params)
for notification_json in resp.json():
notification = Notification.from_json(self._auth, self, notification_json)
notification = Notification.from_json(self._auth, notification_json)
notification.device_type = self.device_type
yield notification

Expand All @@ -379,7 +379,7 @@ def set_beeper(self, enabled: bool):

def set_lock_and_leave(self, enabled: bool):
"""Sets the lock_and_leave setting."""
self._put_attributes({"lockAndLeave": 1 if enabled else 0})
self._put_attributes({"lockAndLeaveEnabled": 1 if enabled else 0})

def set_auto_lock_time(self, auto_lock_time: int):
"""Sets the auto_lock_time setting."""
Expand Down
15 changes: 3 additions & 12 deletions pyschlage/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

from .auth import Auth
from .common import Mutable, fromisoformat
from .device import Device
from .exceptions import NotAuthenticatedError

ON_ALARM = "onalarmstate"
Expand All @@ -31,7 +30,6 @@ class Notification(Mutable):
filter_value: str | None = None
created_at: datetime | None = None
updated_at: datetime | None = None
_device: Device | None = field(default=None, repr=False)
_json: dict[str, Any] = field(default_factory=dict, repr=False)

@staticmethod
Expand All @@ -46,13 +44,10 @@ def request_path(notification_id: str | None = None) -> str:
return path

@classmethod
def from_json(
cls, auth: Auth, device: Device, json: dict[str, Any]
) -> "Notification":
def from_json(cls, auth: Auth, json: dict[str, Any]) -> "Notification":
return Notification(
_auth=auth,
_json=json,
_device=device,
notification_id=json["notificationId"],
user_id=json["userId"],
device_id=json["deviceId"],
Expand All @@ -77,17 +72,14 @@ def to_json(self) -> dict[str, Any]:
json["filterValue"] = self.filter_value
return json

def save(self, device: Device | None = None):
def save(self):
"""Saves the Notification."""
if not self._auth:
raise NotAuthenticatedError
if device:
self._device = device
assert self._device is not None
method = "put" if self.created_at else "post"
path = self.request_path(self.notification_id)
resp = self._auth.request(method, path, self.to_json())
self._update_with(self._device, resp.json())
self._update_with(resp.json())

def delete(self):
"""Deletes the notification."""
Expand All @@ -97,6 +89,5 @@ def delete(self):
self._auth.request("delete", path)
self._auth = None
self._json = {}
self._device = None
self.notification_id = None
self.active = False
2 changes: 1 addition & 1 deletion pyschlage/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def request_path(user_id: str | None = None) -> str:
"""
path = "users"
if user_id:
return f"{path}/{user_id}"
return f"{path}/{user_id}" # pragma: no cover
return path

@classmethod
Expand Down
14 changes: 9 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pyschlage.code import AccessCode
from pyschlage.device import Device
from pyschlage.lock import Lock
from pyschlage.log import LockLog
from pyschlage.notification import ON_UNLOCK_ACTION, Notification


Expand Down Expand Up @@ -232,14 +233,12 @@ def notification_json() -> dict[str, Any]:


@fixture
def notification(
mock_auth: Auth, wifi_device: Device, notification_json
) -> Notification:
return Notification.from_json(mock_auth, wifi_device, notification_json)
def notification(mock_auth: Auth, notification_json) -> Notification:
return Notification.from_json(mock_auth, notification_json)


@fixture
def log_json():
def log_json() -> dict[str, Any]:
return {
"createdAt": "2023-03-01T17:26:47.366Z",
"deviceId": "__device_uuid__",
Expand All @@ -257,3 +256,8 @@ def log_json():
"type": "DEVICE_LOG",
"updatedAt": "2023-03-01T17:26:47.366Z",
}


@fixture
def lock_log(log_json: dict[str, Any]) -> LockLog:
return LockLog.from_json(log_json)
14 changes: 13 additions & 1 deletion tests/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from typing import Any
from unittest.mock import Mock, create_autospec, patch

import pytest

from pyschlage.code import AccessCode, DaysOfWeek, RecurringSchedule, TemporarySchedule
from pyschlage.device import Device
from pyschlage.exceptions import NotAuthenticatedError
from pyschlage.notification import Notification


Expand All @@ -29,6 +32,8 @@ def test_to_from_json(
def test_to_from_json_recurring_schedule(
self, mock_auth: Mock, access_code_json: dict[str, Any], wifi_device: Device
):
assert RecurringSchedule.from_json({}) is None
assert RecurringSchedule.from_json(None) is None
access_code_id = "__access_code_uuid__"
sched = RecurringSchedule(days_of_week=DaysOfWeek(mon=False))
json = deepcopy(access_code_json)
Expand Down Expand Up @@ -75,6 +80,8 @@ def test_save(
mock_auth: Mock,
access_code_json: dict[str, Any],
):
with pytest.raises(NotAuthenticatedError):
AccessCode().save()
mock_device = create_autospec(Device, spec_set=True, device_id="__wifi_uuid__")
code = AccessCode.from_json(mock_auth, mock_device, access_code_json)
code.code = "1122"
Expand All @@ -94,7 +101,7 @@ def test_save(
json=Mock(return_value=new_json)
)
code.save()
mock_notification.save.assert_called_once_with(mock_device)
mock_notification.save.assert_called_once_with()
mock_device.send_command.assert_called_once_with(
"updateaccesscode", old_json
)
Expand All @@ -103,12 +110,17 @@ def test_save(
assert code.name == "New name"

def test_delete(self, mock_auth: Mock, access_code_json: dict[str, Any]):
with pytest.raises(NotAuthenticatedError):
AccessCode().delete()
mock_device = create_autospec(Device, spec_set=True, device_id="__wifi_uuid__")
code = AccessCode.from_json(mock_auth, mock_device, access_code_json)
mock_notification = create_autospec(Notification, spec_set=True)
code._notification = mock_notification
mock_auth.request.return_value = Mock()
json = code.to_json()
code.delete()
mock_device.send_command.assert_called_once_with("deleteaccesscode", json)
mock_notification.delete.assert_called_once_with()
assert code._auth is None
assert code._json == {}
assert code.access_code_id is None
Expand Down
9 changes: 9 additions & 0 deletions tests/test_common.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
from __future__ import annotations

from pickle import dumps, loads
from typing import Any

import pytest

from pyschlage import common


def test_pickle_unpickle() -> None:
mut = common.Mutable()
mut2 = loads(dumps(mut))
assert mut2._mu is not None
assert mut2._mu != mut._mu
assert mut2._auth == mut._auth


@pytest.fixture
def json_dict() -> dict[Any, Any]:
return {
Expand Down
Loading

0 comments on commit b989968

Please sign in to comment.