From 400b1e801110cb22b7014bafb4ce9d38f25fe9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Ean=20G=C3=BCne=C5=9F?= <180301198+sgunes-wirepas@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:00:01 +0200 Subject: [PATCH] Improve errors for SetScratchpadTargetAndActionRequest.from_payload() If constructing a new SetScratchpadTargetAndActionRequest from payload fails due to invalid values, a new InvalidMessageContents exception is raised. This exception can hold the header of the request message, and it would be possible to generate a response with the corresponding req_id based on the exception. --- tests/test_set_scratchpad_target.py | 82 ++++++++++++++++++- .../set_scratchpad_target.py | 18 ++-- wirepas_mesh_messaging/wirepas_exceptions.py | 13 +++ 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/tests/test_set_scratchpad_target.py b/tests/test_set_scratchpad_target.py index 87ed7fd..d39dda0 100644 --- a/tests/test_set_scratchpad_target.py +++ b/tests/test_set_scratchpad_target.py @@ -1,9 +1,14 @@ # flake8: noqa -import wirepas_mesh_messaging import enum + +import pytest from default_value import * +import wirepas_mesh_messaging +from wirepas_mesh_messaging.proto.generic_message_pb2 import GenericMessage +from wirepas_mesh_messaging.wirepas_exceptions import InvalidMessageContents + def test_generate_parse_request_with_raw(): request = wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest( @@ -62,3 +67,78 @@ def test_generate_parse_response(): assert v.value == request2.__dict__[k].value else: assert v == request2.__dict__[k] + + +def test_constructing_request_with_too_large_target_sequence_should_fail(): + target_and_action = { + "action": wirepas_mesh_messaging.ScratchpadAction.ACTION_PROPAGATE_ONLY, + "target_sequence": 0x100, + } + with pytest.raises(ValueError): + wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest( + SINK_ID, target_and_action, REQUEST_ID + ) + + +def test_constructing_request_with_too_large_target_crc_should_fail(): + target_and_action = { + "action": wirepas_mesh_messaging.ScratchpadAction.ACTION_PROPAGATE_ONLY, + "target_crc": 0x10000, + } + with pytest.raises(ValueError): + wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest( + SINK_ID, target_and_action, REQUEST_ID + ) + + +def test_constructing_request_with_too_large_raw_action_should_fail(): + target_and_action = { + "action": wirepas_mesh_messaging.ScratchpadAction.ACTION_PROPAGATE_AND_PROCESS, + "param": 0x100, + } + with pytest.raises(ValueError): + wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest( + SINK_ID, target_and_action, REQUEST_ID + ) + + +def test_decoding_request_with_too_large_target_sequence_should_fail_with_correct_exception(): + TEST_TIME = 34567 + base_request = wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest( + SINK_ID, SCRATCHPAD_TARGET_RAW, REQUEST_ID, time_ms_epoch=TEST_TIME + ) + message = GenericMessage() + message.ParseFromString(base_request.payload) + message.wirepas.set_scratchpad_target_and_action_req.target_and_action.target_sequence = ( + 0x100 + ) + test_payload = message.SerializeToString() + + with pytest.raises(InvalidMessageContents) as exc_info: + wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest.from_payload( + test_payload + ) + assert exc_info.value.header["req_id"] == REQUEST_ID + assert exc_info.value.header["sink_id"] == SINK_ID + assert exc_info.value.header["time_ms_epoch"] == TEST_TIME + + +def test_decoding_request_with_too_large_target_crc_should_fail_with_correct_exception(): + TEST_TIME = 1234567 + base_request = wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest( + SINK_ID_2, SCRATCHPAD_TARGET_RAW, REQUEST_ID, time_ms_epoch=TEST_TIME + ) + message = GenericMessage() + message.ParseFromString(base_request.payload) + message.wirepas.set_scratchpad_target_and_action_req.target_and_action.target_crc = ( + 0x10000 + ) + test_payload = message.SerializeToString() + + with pytest.raises(InvalidMessageContents) as exc_info: + wirepas_mesh_messaging.SetScratchpadTargetAndActionRequest.from_payload( + test_payload + ) + assert exc_info.value.header["req_id"] == REQUEST_ID + assert exc_info.value.header["sink_id"] == SINK_ID_2 + assert exc_info.value.header["time_ms_epoch"] == TEST_TIME diff --git a/wirepas_mesh_messaging/set_scratchpad_target.py b/wirepas_mesh_messaging/set_scratchpad_target.py index 167d48b..1b89b8d 100644 --- a/wirepas_mesh_messaging/set_scratchpad_target.py +++ b/wirepas_mesh_messaging/set_scratchpad_target.py @@ -7,6 +7,7 @@ See file LICENSE for full license details. """ +from wirepas_mesh_messaging.wirepas_exceptions import InvalidMessageContents from .proto import GenericMessage from .request import Request @@ -86,15 +87,22 @@ def _get_related_message(generic_message): def from_payload(cls, payload): req = cls._decode_and_get_related_message(payload) - d = Request._parse_request_header(req.header) + header = Request._parse_request_header(req.header) target = {} parse_scratchpad_target(req.target_and_action, target) - return cls(sink_id=d["sink_id"], - target=target, - req_id=d["req_id"], - time_ms_epoch=d["time_ms_epoch"]) + try: + return cls( + sink_id=header["sink_id"], + target=target, + req_id=header["req_id"], + time_ms_epoch=header["time_ms_epoch"], + ) + except ValueError as e: + raise InvalidMessageContents( + f"Invalid values in {cls.__name__}", header + ) from e @property def payload(self): diff --git a/wirepas_mesh_messaging/wirepas_exceptions.py b/wirepas_mesh_messaging/wirepas_exceptions.py index a8821ce..38cbae9 100644 --- a/wirepas_mesh_messaging/wirepas_exceptions.py +++ b/wirepas_mesh_messaging/wirepas_exceptions.py @@ -17,3 +17,16 @@ class InvalidMessageType(GatewayAPIParsingException): """ Exception indicating wrong message type during deserialization """ + +class InvalidMessageContents(GatewayAPIParsingException): + """ + Exception indicating invalid message contents in the received message. + + Parameters: + - message: The exception message + - header: Parsed header of the message, as a dictionary + """ + + def __init__(self, message, header): + super().__init__(message) + self.header = header