Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nnsnodnb committed Jan 13, 2024
1 parent 5352732 commit 8f57473
Show file tree
Hide file tree
Showing 14 changed files with 346 additions and 11 deletions.
3 changes: 2 additions & 1 deletion kalyke/clients/live_activity.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from dataclasses import dataclass
from typing import Any, Dict, Union

from httpx import AsyncClient

Expand All @@ -11,7 +12,7 @@ class LiveActivityClient(ApnsClient):
async def send_message(
self,
device_token: str,
payload: LiveActivityPayload,
payload: Union[LiveActivityPayload, Dict[str, Any]],
apns_config: LiveActivityApnsConfig,
) -> str:
return await super().send_message(
Expand Down
4 changes: 2 additions & 2 deletions kalyke/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ def __str__(self) -> str:

class LiveActivityAttributesIsNotJSONSerializable(Exception):
def __str__(self) -> str:
return "The attributes is not JSON serializable."
return "attributes is not JSON serializable."


class LiveActivityContentStateIsNotJSONSerializable(Exception):
def __str__(self) -> str:
return "The content state is not JSON serializable."
return "content-state is not JSON serializable."


# https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/handling_notification_responses_from_apns#3394535
Expand Down
6 changes: 3 additions & 3 deletions kalyke/models/live_activity_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@


class LiveActivityEvent(Enum):
start: str = "start"
update: str = "update"
end: str = "end"
START: str = "start"
UPDATE: str = "update"
END: str = "end"
9 changes: 4 additions & 5 deletions kalyke/models/payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,19 @@ class LiveActivityPayload(Payload):
def __post_init__(self):
if self.event is None:
raise ValueError("event must be specified.")
elif self.event == LiveActivityEvent.start:
elif self.event == LiveActivityEvent.START:
if self.attributes_type is None or self.attributes is None:
raise ValueError(
"""attributes_type and attributes must be specified when event is start.
Please see documentation: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification""" # noqa: E501
"attributes_type and attributes must be specified when event is start.\nPlease see documentation: https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification" # noqa: E501
)
try:
_ = json.dumps(self.attributes)
except json.decoder.JSONDecodeError:
except TypeError:
raise LiveActivityAttributesIsNotJSONSerializable()

try:
_ = json.dumps(self.content_state)
except json.decoder.JSONDecodeError:
except TypeError:
raise LiveActivityContentStateIsNotJSONSerializable()
super().__post_init__()

Expand Down
Empty file.
8 changes: 8 additions & 0 deletions tests/clients/live_activity/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pathlib import Path

import pytest


@pytest.fixture()
def auth_key_filepath() -> Path:
return Path(__file__).parent / "dummy.p8"
5 changes: 5 additions & 0 deletions tests/clients/live_activity/dummy.p8
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgrcxc3odhJh+wcUbY
FQRIACPBtRdkAw2RkkoLEU//vVWhRANCAAS3E4nNyDsuBLssEHUDu/Eck4pUCKge
M/WbLSx83MmMWyv4ynD3z3xqjRptG1cGLGWuCXFZZ4+qPKKXixqShjaE
-----END PRIVATE KEY-----
23 changes: 23 additions & 0 deletions tests/clients/live_activity/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from kalyke import LiveActivityClient


def test_initialize_with_pathlib(auth_key_filepath):
client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=auth_key_filepath,
)

assert isinstance(client, LiveActivityClient)


def test_initialize_with_str(auth_key_filepath):
client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=str(auth_key_filepath),
)

assert isinstance(client, LiveActivityClient)
20 changes: 20 additions & 0 deletions tests/clients/live_activity/test_init_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from httpx import AsyncClient

from kalyke import LiveActivityApnsConfig, LiveActivityClient


def test_exist_authorization_header(auth_key_filepath):
client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=auth_key_filepath,
)
actual_client = client._init_client(
apns_config=LiveActivityApnsConfig(
topic="com.example.App.push-type.liveactivity",
),
)

assert isinstance(actual_client, AsyncClient)
assert actual_client.headers["authorization"].startswith("bearer ey")
53 changes: 53 additions & 0 deletions tests/clients/live_activity/test_make_authorization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import datetime
from pathlib import Path
from unittest.mock import MagicMock

import jwt
import pytest

from kalyke import LiveActivityClient


def test_success(auth_key_filepath):
datetime_mock = MagicMock(wraps=datetime.datetime)
datetime_mock.now.return_value = datetime.datetime(2022, 1, 10, 23, 6, 34)

client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=auth_key_filepath,
)
token = client._make_authorization()

expect = jwt.encode(
payload={
"iss": "DUMMY_TEAM_ID",
"iat": str(int(datetime.datetime.now().timestamp())),
},
key=auth_key_filepath.read_text(),
algorithm="ES256",
headers={
"alg": "ES256",
"kid": "DUMMY",
},
)

actual_payload = jwt.decode(jwt=token, options={"verify_signature": False})
expect_payload = jwt.decode(jwt=expect, options={"verify_signature": False})
assert actual_payload == expect_payload
assert jwt.get_unverified_header(jwt=token) == jwt.get_unverified_header(jwt=expect)


def test_file_not_found_error():
client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=Path("/") / "no_exist.p8",
)

with pytest.raises(FileNotFoundError) as e:
_ = client._make_authorization()

assert str(e.value) == "[Errno 2] No such file or directory: '/no_exist.p8'"
86 changes: 86 additions & 0 deletions tests/clients/live_activity/test_send_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import pytest

from kalyke import LiveActivityApnsConfig, LiveActivityClient
from kalyke.exceptions import BadDeviceToken


@pytest.mark.asyncio
async def test_success(httpx_mock, auth_key_filepath):
httpx_mock.add_response(
status_code=200,
http_version="HTTP/2.0",
headers={
"apns-id": "stub-apns-id",
},
json={},
)

client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM",
auth_key_id="DUMMY",
auth_key_filepath=auth_key_filepath,
)
apns_id = await client.send_message(
device_token="stub_device_token",
payload={
"alert": "test alert",
},
apns_config=LiveActivityApnsConfig(
topic="com.example.App.push-type.liveactivity",
),
)

assert apns_id == "stub-apns-id"


@pytest.mark.asyncio
async def test_bad_device_token(httpx_mock, auth_key_filepath):
httpx_mock.add_response(
status_code=400,
http_version="HTTP/2.0",
json={
"reason": "BadDeviceToken",
},
)

client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=auth_key_filepath,
)

with pytest.raises(BadDeviceToken) as e:
await client.send_message(
device_token="stub_device_token",
payload={
"alert": "test alert",
},
apns_config=LiveActivityApnsConfig(
topic="com.example.App.push-type.liveactivity",
),
)

assert str(e.value) == str(BadDeviceToken(error={}))


@pytest.mark.asyncio
async def test_value_error(auth_key_filepath):
client = LiveActivityClient(
use_sandbox=True,
team_id="DUMMY_TEAM_ID",
auth_key_id="DUMMY",
auth_key_filepath=auth_key_filepath,
)

with pytest.raises(ValueError) as e:
await client.send_message(
device_token="stub_device_token",
payload=["test alert"],
apns_config=LiveActivityApnsConfig(
topic="com.example.App.push-type.liveactivity",
),
)

assert str(e.value) == "Type of 'payload' must be specified by Payload or Dict[str, Any]."
Empty file.
Loading

0 comments on commit 8f57473

Please sign in to comment.