diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9b38853 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/android_sms_gateway/domain.py b/android_sms_gateway/domain.py index 41d0d98..a29990f 100644 --- a/android_sms_gateway/domain.py +++ b/android_sms_gateway/domain.py @@ -1,7 +1,7 @@ import dataclasses import typing as t -from .enums import ProcessState, WebhookEvent +from .enums import ProcessState, WebhookEvent, MessagePriority def snake_to_camel(snake_str): @@ -11,6 +11,20 @@ def snake_to_camel(snake_str): @dataclasses.dataclass(frozen=True) class Message: + """ + Represents an SMS message. + + Attributes: + message (str): The message text. + phone_numbers (List[str]): A list of phone numbers to send the message to. + with_delivery_report (bool): Whether to request a delivery report. Defaults to True. + is_encrypted (bool): Whether the message is encrypted. Defaults to False. + id (Optional[str]): The message ID. Defaults to None. + ttl (Optional[int]): The time-to-live in seconds. Defaults to None. + sim_number (Optional[int]): The SIM number to use. Defaults to None. + priority (Optional[MessagePriority]): The priority of the message. Defaults to None. + """ + message: str phone_numbers: t.List[str] with_delivery_report: bool = True @@ -19,8 +33,15 @@ class Message: id: t.Optional[str] = None ttl: t.Optional[int] = None sim_number: t.Optional[int] = None + priority: t.Optional[MessagePriority] = None def asdict(self) -> t.Dict[str, t.Any]: + """ + Returns a dictionary representation of the message. + + Returns: + Dict[str, Any]: A dictionary representation of the message. + """ return { snake_to_camel(field.name): getattr(self, field.name) for field in dataclasses.fields(self) diff --git a/android_sms_gateway/enums.py b/android_sms_gateway/enums.py index dfc190c..3613b83 100644 --- a/android_sms_gateway/enums.py +++ b/android_sms_gateway/enums.py @@ -28,3 +28,19 @@ class WebhookEvent(enum.Enum): SYSTEM_PING = "system:ping" """Triggered when the device pings the server.""" + + +class MessagePriority(enum.IntEnum): + """Priority levels for messages.""" + + MINIMUM = -128 + """Minimum priority level.""" + + DEFAULT = 0 + """Default priority level.""" + + BYPASS_THRESHOLD = 100 + """Priority level to bypass limits and delays.""" + + MAXIMUM = 127 + """Maximum priority level.""" diff --git a/tests/test_domain.py b/tests/test_domain.py index 50fc108..8287640 100644 --- a/tests/test_domain.py +++ b/tests/test_domain.py @@ -1,7 +1,7 @@ import pytest -from android_sms_gateway.enums import WebhookEvent -from android_sms_gateway.domain import MessageState, RecipientState, Webhook +from android_sms_gateway.enums import WebhookEvent, MessagePriority +from android_sms_gateway.domain import MessageState, RecipientState, Webhook, Message # Test for successful instantiation from a dictionary @@ -135,3 +135,127 @@ def test_webhook_asdict(): } assert webhook.asdict() == expected_dict + + +@pytest.mark.parametrize( + "message_content,phone_numbers,with_delivery_report,is_encrypted,id,ttl,sim_number,priority,expected", + [ + ( + "Hello, world!", + ["123", "456"], + True, + False, + "msg_123", + 300, + 1, + MessagePriority.BYPASS_THRESHOLD, + { + "message": "Hello, world!", + "phoneNumbers": ["123", "456"], + "withDeliveryReport": True, + "isEncrypted": False, + "id": "msg_123", + "ttl": 300, + "simNumber": 1, + "priority": 100, + }, + ), + ( + "Hello, world!", + ["123", "456"], + True, + False, + None, + None, + None, + None, + { + "message": "Hello, world!", + "phoneNumbers": ["123", "456"], + "withDeliveryReport": True, + "isEncrypted": False, + }, + ), + ( + "Hello, world!", + ["123", "456"], + True, + False, + "msg_123", + None, + 1, + None, + { + "message": "Hello, world!", + "phoneNumbers": ["123", "456"], + "withDeliveryReport": True, + "isEncrypted": False, + "id": "msg_123", + "simNumber": 1, + }, + ), + ( + "Hello, world!", + ["123", "456"], + True, + False, + "msg_123", + None, + None, + MessagePriority.DEFAULT, + { + "message": "Hello, world!", + "phoneNumbers": ["123", "456"], + "withDeliveryReport": True, + "isEncrypted": False, + "id": "msg_123", + "priority": 0, + }, + ), + ( + "Hi", + ["555"], + True, + False, + None, + None, + None, + MessagePriority.MINIMUM, + { + "message": "Hi", + "phoneNumbers": ["555"], + "withDeliveryReport": True, + "isEncrypted": False, + "priority": -128, + }, + ), + ], +) +def test_message_asdict( + message_content, + phone_numbers, + with_delivery_report, + is_encrypted, + id, + ttl, + sim_number, + priority, + expected, +): + """ + Tests that a Message instance can be successfully converted to a dictionary + representation with camelCase keys and that only non-None fields are included. + Uses parametrized testing to cover multiple scenarios. + """ + message = Message( + message=message_content, + phone_numbers=phone_numbers, + with_delivery_report=with_delivery_report, + is_encrypted=is_encrypted, + id=id, + ttl=ttl, + sim_number=sim_number, + priority=priority, + ) + + assert message.asdict() == expected