From 781739eb8f5cd605494929016acacaa3a9a15943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francis=20Clairicia-Rose-Claire-Jos=C3=A9phine?= Date: Mon, 27 Nov 2023 19:26:23 +0100 Subject: [PATCH] Added missing functional tests for serializers (#173) --- src/easynetwork/exceptions.py | 2 + src/easynetwork/serializers/base_stream.py | 12 ++--- src/easynetwork/serializers/json.py | 4 +- src/easynetwork/serializers/line.py | 3 ++ .../functional_test/test_serializers/base.py | 15 +++--- .../test_serializers/samples/json.py | 2 +- .../test_serializers/test_base64.py | 39 +++++++++----- .../test_serializers/test_cbor.py | 2 +- .../test_serializers/test_compressors.py | 17 ++++-- .../test_serializers/test_encryptor.py | 29 ++++++++-- .../test_serializers/test_json.py | 54 +++++++++++++------ .../test_serializers/test_line.py | 40 ++++++++++---- .../test_serializers/test_namedtuple.py | 25 ++++++--- .../test_serializers/test_pickle.py | 12 +---- tests/unit_test/test_serializers/test_abc.py | 3 +- tests/unit_test/test_serializers/test_json.py | 1 + tests/unit_test/test_serializers/test_line.py | 20 +++++++ .../unit_test/test_serializers/test_tools.py | 3 +- 18 files changed, 195 insertions(+), 88 deletions(-) diff --git a/src/easynetwork/exceptions.py b/src/easynetwork/exceptions.py index f880b8b7..bc93ff8e 100644 --- a/src/easynetwork/exceptions.py +++ b/src/easynetwork/exceptions.py @@ -109,6 +109,8 @@ def __init__(self, message: str, buffer: ReadableBuffer, consumed: int, separato remaining_data = memoryview(buffer)[consumed:].tobytes() if separator and remaining_data.startswith(separator): remaining_data = remaining_data.removeprefix(separator) + else: + remaining_data = remaining_data[1:] super().__init__(message, remaining_data, error_info=None) diff --git a/src/easynetwork/serializers/base_stream.py b/src/easynetwork/serializers/base_stream.py index 5d477b3e..fa032003 100644 --- a/src/easynetwork/serializers/base_stream.py +++ b/src/easynetwork/serializers/base_stream.py @@ -132,19 +132,19 @@ def incremental_deserialize(self) -> Generator[None, bytes, tuple[_DTOPacketT, b """ reader = GeneratorStreamReader() data = yield from reader.read_until(self.__separator, limit=self.__limit, keep_end=False) - buffer = reader.read_all() + remainder = reader.read_all() try: packet = self.deserialize(data) except DeserializeError as exc: raise IncrementalDeserializeError( f"Error when deserializing data: {exc}", - remaining_data=buffer, + remaining_data=remainder, error_info=exc.error_info, ) from exc finally: del data - return packet, buffer + return packet, remainder @property @final @@ -231,19 +231,19 @@ def incremental_deserialize(self) -> Generator[None, bytes, tuple[_DTOPacketT, b """ reader = GeneratorStreamReader() data = yield from reader.read_exactly(self.__size) - buffer = reader.read_all() + remainder = reader.read_all() try: packet = self.deserialize(data) except DeserializeError as exc: raise IncrementalDeserializeError( f"Error when deserializing data: {exc}", - remaining_data=buffer, + remaining_data=remainder, error_info=exc.error_info, ) from exc finally: del data - return packet, buffer + return packet, remainder @property @final diff --git a/src/easynetwork/serializers/json.py b/src/easynetwork/serializers/json.py index dee55b0e..b912891a 100644 --- a/src/easynetwork/serializers/json.py +++ b/src/easynetwork/serializers/json.py @@ -404,7 +404,7 @@ def raw_parse(*, limit: int) -> Generator[None, bytes, tuple[bytes, bytes]]: raise LimitOverrunError( "JSON object's end frame is not found, and chunk exceed the limit", partial_document, - nprint_idx, + len(partial_document), ) partial_document += yield @@ -412,13 +412,13 @@ def raw_parse(*, limit: int) -> Generator[None, bytes, tuple[bytes, bytes]]: @staticmethod def _split_partial_document(partial_document: bytes, consumed: int, limit: int) -> tuple[bytes, bytes]: + consumed = _JSONParser._whitespaces_match(partial_document, consumed).end() if consumed > limit: raise LimitOverrunError( "JSON object's end frame is found, but chunk is longer than limit", partial_document, consumed, ) - consumed = _JSONParser._whitespaces_match(partial_document, consumed).end() if consumed == len(partial_document): # The following bytes are only spaces # Do not slice the document, the trailing spaces will be ignored by JSONDecoder diff --git a/src/easynetwork/serializers/line.py b/src/easynetwork/serializers/line.py index f9ae27c8..27c58bd4 100644 --- a/src/easynetwork/serializers/line.py +++ b/src/easynetwork/serializers/line.py @@ -129,6 +129,7 @@ def deserialize(self, data): Raises: DeserializeError: :class:`UnicodeError` raised when decoding `data`. + DeserializeError: Newline found in `data` (excluding those at the end of the sequence). Returns: the string. @@ -139,6 +140,8 @@ def deserialize(self, data): separator: bytes = self.separator while data.endswith(separator): data = data.removesuffix(separator) + if separator in data: + raise DeserializeError("Newline found in string") try: return data.decode(self.__encoding, self.__unicode_errors) except UnicodeError as exc: diff --git a/tests/functional_test/test_serializers/base.py b/tests/functional_test/test_serializers/base.py index 536b89e1..0f3ba0a9 100644 --- a/tests/functional_test/test_serializers/base.py +++ b/tests/functional_test/test_serializers/base.py @@ -124,9 +124,14 @@ def invalid_partial_data() -> bytes: @pytest.fixture(scope="class") @staticmethod - def invalid_partial_data_extra_data() -> bytes | None: + def invalid_partial_data_extra_data() -> bytes: return b"remaining_data" + @pytest.fixture(scope="class") + @staticmethod + def invalid_partial_data_expected_extra_data(invalid_partial_data_extra_data: bytes) -> bytes: + return invalid_partial_data_extra_data + def test____fixture____consistency____incremental_serializer( self, serializer_for_serialization: AbstractIncrementalPacketSerializer[Any], @@ -223,7 +228,8 @@ def test____incremental_deserialize____invalid_data( self, serializer_for_deserialization: AbstractIncrementalPacketSerializer[Any], invalid_partial_data: bytes, - invalid_partial_data_extra_data: bytes | None, + invalid_partial_data_extra_data: bytes, + invalid_partial_data_expected_extra_data: bytes, ) -> None: # Arrange consumer = serializer_for_deserialization.incremental_deserialize() @@ -238,10 +244,7 @@ def test____incremental_deserialize____invalid_data( exception = exc_info.value # Assert - if invalid_partial_data_extra_data is not None: - assert exception.remaining_data == invalid_partial_data_extra_data - else: - assert len(exception.remaining_data) > 0 + assert exception.remaining_data == invalid_partial_data_expected_extra_data @final diff --git a/tests/functional_test/test_serializers/samples/json.py b/tests/functional_test/test_serializers/samples/json.py index 1f3947f3..8ca400cb 100644 --- a/tests/functional_test/test_serializers/samples/json.py +++ b/tests/functional_test/test_serializers/samples/json.py @@ -56,7 +56,7 @@ ] -BIG_JSON: Any = [ +BIG_JSON: list[Any] = [ { "_id": "63cd615fa31a400f255ec20c", "index": 0, diff --git a/tests/functional_test/test_serializers/test_base64.py b/tests/functional_test/test_serializers/test_base64.py index a40c616d..2be7143c 100644 --- a/tests/functional_test/test_serializers/test_base64.py +++ b/tests/functional_test/test_serializers/test_base64.py @@ -2,6 +2,9 @@ from __future__ import annotations +import base64 +import hashlib +import hmac import random from typing import Any, Literal, final @@ -14,9 +17,6 @@ def generate_key_from_string(s: str) -> bytes: - import base64 - import hashlib - return base64.urlsafe_b64encode(hashlib.sha256(s.encode("utf-8")).digest()) @@ -30,6 +30,8 @@ def generate_key_from_string(s: str) -> bytes: class BaseTestBase64EncoderSerializer(BaseTestIncrementalSerializer): #### Serializers + BUFFER_LIMIT = 1024 + @pytest.fixture(scope="class", params=["standard", "urlsafe"]) @staticmethod def alphabet(request: pytest.FixtureRequest) -> Literal["standard", "urlsafe"]: @@ -42,7 +44,7 @@ def serializer( checksum: bool | bytes, alphabet: Literal["standard", "urlsafe"], ) -> Base64EncoderSerializer[bytes]: - return Base64EncoderSerializer(NoSerialization(), alphabet=alphabet, checksum=checksum) + return Base64EncoderSerializer(NoSerialization(), alphabet=alphabet, checksum=checksum, limit=cls.BUFFER_LIMIT) @pytest.fixture(scope="class") @staticmethod @@ -71,10 +73,6 @@ def expected_complete_data( checksum: bool | bytes, alphabet: Literal["standard", "urlsafe"], ) -> bytes: - import base64 - import hashlib - import hmac - if checksum: if isinstance(checksum, bytes): key = base64.urlsafe_b64decode(checksum) @@ -114,10 +112,27 @@ def complete_data_for_incremental_deserialize(complete_data: bytes) -> bytes: def invalid_complete_data(complete_data: bytes) -> bytes: return complete_data[:-1] # Remove one byte at last will break the padding - @pytest.fixture - @staticmethod - def invalid_partial_data() -> bytes: - pytest.skip("Cannot be tested") + @pytest.fixture(scope="class", params=["missing_data", "limit_overrun_without_newline", "limit_overrun_with_newline"]) + @classmethod + def invalid_partial_data(cls, request: pytest.FixtureRequest, alphabet: Literal["standard", "urlsafe"]) -> bytes: + match request.param: + case "missing_data": + if alphabet == "standard": + return base64.standard_b64encode(random.randbytes(255))[:-1] + b"\r\n" + return base64.urlsafe_b64encode(random.randbytes(255))[:-1] + b"\r\n" + case "limit_overrun_without_newline": + return b"4" * (cls.BUFFER_LIMIT + 10) + case "limit_overrun_with_newline": + return b"4" * (cls.BUFFER_LIMIT + 10) + b"\r\n" + case _: + pytest.fail("Invalid fixture parameter") + + @pytest.fixture(scope="class") + @classmethod + def invalid_partial_data_extra_data(cls, invalid_partial_data: bytes) -> bytes: + if len(invalid_partial_data) > cls.BUFFER_LIMIT: + return b"" + return b"remaining_data" #### Other diff --git a/tests/functional_test/test_serializers/test_cbor.py b/tests/functional_test/test_serializers/test_cbor.py index 3136f94f..eb7faa11 100644 --- a/tests/functional_test/test_serializers/test_cbor.py +++ b/tests/functional_test/test_serializers/test_cbor.py @@ -76,7 +76,7 @@ def complete_data_for_incremental_deserialize(complete_data: bytes) -> bytes: def invalid_complete_data(complete_data: bytes) -> bytes: return complete_data[:-1] # Missing data error - @pytest.fixture + @pytest.fixture(scope="class") @staticmethod def invalid_partial_data() -> bytes: pytest.skip("Cannot be tested") diff --git a/tests/functional_test/test_serializers/test_compressors.py b/tests/functional_test/test_serializers/test_compressors.py index 781e2cd9..a8e46b0c 100644 --- a/tests/functional_test/test_serializers/test_compressors.py +++ b/tests/functional_test/test_serializers/test_compressors.py @@ -20,6 +20,10 @@ ] +def _make_data_invalid(token: bytes) -> bytes: + return token[:-2] + random.randbytes(5) + token[-2:] + + class BaseTestCompressorSerializer(BaseTestIncrementalSerializer): #### Serializers: To be defined in subclass @@ -53,12 +57,17 @@ def complete_data_for_incremental_deserialize(complete_data: bytes) -> bytes: @pytest.fixture(scope="class") @staticmethod def invalid_complete_data(complete_data: bytes) -> bytes: - return complete_data[:-1] # Remove one byte at last will break the checksum + return _make_data_invalid(complete_data) + + @pytest.fixture(scope="class") + @staticmethod + def invalid_partial_data(invalid_complete_data: bytes) -> bytes: + return invalid_complete_data - @pytest.fixture + @pytest.fixture(scope="class") @staticmethod - def invalid_partial_data() -> bytes: - pytest.skip("Cannot be tested") + def invalid_partial_data_expected_extra_data() -> bytes: + return b"" @final diff --git a/tests/functional_test/test_serializers/test_encryptor.py b/tests/functional_test/test_serializers/test_encryptor.py index 76ceb16e..2a3fb09d 100644 --- a/tests/functional_test/test_serializers/test_encryptor.py +++ b/tests/functional_test/test_serializers/test_encryptor.py @@ -20,10 +20,12 @@ class TestEncryptorSerializer(BaseTestIncrementalSerializer): KEY = generate_key_from_string("key") + BUFFER_LIMIT = 1024 + @pytest.fixture(scope="class") @classmethod def serializer(cls) -> EncryptorSerializer[bytes]: - return EncryptorSerializer(NoSerialization(), key=cls.KEY) + return EncryptorSerializer(NoSerialization(), key=cls.KEY, limit=cls.BUFFER_LIMIT) @pytest.fixture(scope="class") @staticmethod @@ -96,10 +98,27 @@ def invalid_complete_data(complete_data: bytes) -> bytes: pytest.skip("empty bytes") return complete_data[:-1] # Remove one byte at last will break the padding - @pytest.fixture - @staticmethod - def invalid_partial_data() -> bytes: - pytest.skip("Cannot be tested") + @pytest.fixture(scope="class", params=["missing_data", "limit_overrun_without_newline", "limit_overrun_with_newline"]) + @classmethod + def invalid_partial_data(cls, request: pytest.FixtureRequest) -> bytes: + match request.param: + case "missing_data": + from cryptography.fernet import Fernet + + return Fernet(cls.KEY).encrypt_at_time(b"a", 0)[:-1] + b"\r\n" + case "limit_overrun_without_newline": + return b"4" * (cls.BUFFER_LIMIT + 10) + case "limit_overrun_with_newline": + return b"4" * (cls.BUFFER_LIMIT + 10) + b"\r\n" + case _: + pytest.fail("Invalid fixture parameter") + + @pytest.fixture(scope="class") + @classmethod + def invalid_partial_data_extra_data(cls, invalid_partial_data: bytes) -> bytes: + if len(invalid_partial_data) > cls.BUFFER_LIMIT: + return b"" + return b"remaining_data" #### Other diff --git a/tests/functional_test/test_serializers/test_json.py b/tests/functional_test/test_serializers/test_json.py index 0b1b7f14..6cbd421b 100644 --- a/tests/functional_test/test_serializers/test_json.py +++ b/tests/functional_test/test_serializers/test_json.py @@ -3,6 +3,7 @@ from __future__ import annotations import dataclasses +import json from typing import Any, final from easynetwork.serializers.json import JSONEncoderConfig, JSONSerializer @@ -10,7 +11,12 @@ import pytest from .base import BaseTestIncrementalSerializer -from .samples.json import SAMPLES +from .samples.json import BIG_JSON, SAMPLES + +# 'BIG_JSON' serialized is approximatively 7.5KiB long. +TOO_BIG_JSON = BIG_JSON * 2 + +TOO_BIG_JSON_SERIALIZED = json.dumps(TOO_BIG_JSON, ensure_ascii=False).encode("utf-8") @final @@ -19,6 +25,10 @@ class TestJSONSerializer(BaseTestIncrementalSerializer): ENCODER_CONFIG = JSONEncoderConfig(ensure_ascii=False) + BUFFER_LIMIT = 14 * 1024 # 14KiB + + assert len(TOO_BIG_JSON_SERIALIZED) > BUFFER_LIMIT + @pytest.fixture(scope="class", params=[False, True], ids=lambda p: f"use_lines=={p}") @staticmethod def use_lines(request: Any) -> bool: @@ -27,12 +37,12 @@ def use_lines(request: Any) -> bool: @pytest.fixture(scope="class") @classmethod def serializer_for_serialization(cls, use_lines: bool) -> JSONSerializer: - return JSONSerializer(encoder_config=cls.ENCODER_CONFIG, use_lines=use_lines) + return JSONSerializer(encoder_config=cls.ENCODER_CONFIG, use_lines=use_lines, limit=cls.BUFFER_LIMIT) @pytest.fixture(scope="class") - @staticmethod - def serializer_for_deserialization(use_lines: bool) -> JSONSerializer: - return JSONSerializer(use_lines=use_lines) + @classmethod + def serializer_for_deserialization(cls, use_lines: bool) -> JSONSerializer: + return JSONSerializer(use_lines=use_lines, limit=cls.BUFFER_LIMIT) #### Packets to test @@ -46,8 +56,6 @@ def packet_to_serialize(request: Any) -> Any: @pytest.fixture(scope="class") @classmethod def expected_complete_data(cls, packet_to_serialize: Any) -> bytes: - import json - return json.dumps(packet_to_serialize, **dataclasses.asdict(cls.ENCODER_CONFIG), separators=(",", ":")).encode("utf-8") #### Incremental Serialize @@ -64,8 +72,6 @@ def expected_joined_data(expected_complete_data: bytes, use_lines: bool) -> byte @pytest.fixture(scope="class") @staticmethod def complete_data(packet_to_serialize: Any, use_lines: bool) -> bytes: - import json - indent: int | None = None if not use_lines: # Test with indentation to see whitespace handling @@ -82,7 +88,7 @@ def complete_data_for_incremental_deserialize(complete_data: bytes) -> bytes: #### Invalid data - @pytest.fixture(scope="class", params=[b"invalid", b"\0"]) + @pytest.fixture(scope="class", params=[b"invalid", b"\0", '{"é": 123}'.encode("latin-1")]) @staticmethod def invalid_complete_data(request: Any, use_lines: bool) -> bytes: data: bytes = getattr(request, "param") @@ -90,17 +96,31 @@ def invalid_complete_data(request: Any, use_lines: bool) -> bytes: data += b"\n" return data - @pytest.fixture(scope="class", params=[b"[ invalid ]", b"\0"]) - @staticmethod - def invalid_partial_data(request: Any, use_lines: bool) -> bytes: + @pytest.fixture( + scope="class", + params=[ + b"[ invalid ]", + b"\0", + '{"é": 123}'.encode("latin-1"), + pytest.param(TOO_BIG_JSON_SERIALIZED[:-20], id="too_big_json_partial"), + pytest.param(TOO_BIG_JSON_SERIALIZED, id="too_big_json_no_newline"), + pytest.param(TOO_BIG_JSON_SERIALIZED + b"\n", id="too_big_json_with_newline"), + pytest.param(b"4" * (BUFFER_LIMIT + 1024), id="too_big_raw_value_no_newline"), + pytest.param(b"4" * (BUFFER_LIMIT + 1024) + b"\n", id="too_big_raw_value_with_newline"), + ], + ) + @classmethod + def invalid_partial_data(cls, request: Any, use_lines: bool) -> bytes: data: bytes = getattr(request, "param") - if use_lines: + if len(data) <= cls.BUFFER_LIMIT and use_lines: data += b"\n" return data - @pytest.fixture - @staticmethod - def invalid_partial_data_extra_data(invalid_partial_data: bytes) -> bytes: + @pytest.fixture(scope="class") + @classmethod + def invalid_partial_data_extra_data(cls, invalid_partial_data: bytes) -> bytes: + if len(invalid_partial_data) > cls.BUFFER_LIMIT: + return b"" if invalid_partial_data.startswith(b"\0"): return b"" return b"remaining_data" diff --git a/tests/functional_test/test_serializers/test_line.py b/tests/functional_test/test_serializers/test_line.py index ed98d8f5..89b26c04 100644 --- a/tests/functional_test/test_serializers/test_line.py +++ b/tests/functional_test/test_serializers/test_line.py @@ -20,6 +20,9 @@ @final class TestStringLineSerializer(BaseTestIncrementalSerializer): #### Serializers + + BUFFER_LIMIT = 1024 + @pytest.fixture(scope="class", params=list(_NEWLINES)) @staticmethod def newline(request: pytest.FixtureRequest) -> Literal["LF", "CR", "CRLF"]: @@ -31,9 +34,9 @@ def encoding(request: pytest.FixtureRequest) -> str: return getattr(request, "param") @pytest.fixture(scope="class") - @staticmethod - def serializer(newline: Literal["LF", "CR", "CRLF"], encoding: str) -> StringLineSerializer: - return StringLineSerializer(newline, encoding=encoding) + @classmethod + def serializer(cls, newline: Literal["LF", "CR", "CRLF"], encoding: str) -> StringLineSerializer: + return StringLineSerializer(newline, encoding=encoding, limit=cls.BUFFER_LIMIT) @pytest.fixture(scope="class") @staticmethod @@ -84,23 +87,38 @@ def complete_data_for_incremental_deserialize(complete_data: bytes, newline: Lit #### Invalid data - @pytest.fixture(scope="class", params=["unicode-error"]) + @pytest.fixture(scope="class", params=["unicode_error"]) @staticmethod def invalid_complete_data(request: pytest.FixtureRequest) -> bytes: match getattr(request, "param"): - case "unicode-error": + case "unicode_error": return "é".encode("latin-1") case _: pytest.fail("Invalid fixture parameter") - @pytest.fixture - @staticmethod - def invalid_partial_data() -> bytes: - pytest.skip("Cannot be tested") + @pytest.fixture(scope="class", params=["unicode_error", "limit_overrun_without_newline", "limit_overrun_with_newline"]) + @classmethod + def invalid_partial_data(cls, request: pytest.FixtureRequest, newline: Literal["CR", "LF", "CRLF"]) -> bytes: + match getattr(request, "param"): + case "unicode_error": + return "é".encode("latin-1") + _NEWLINES[newline] + case "limit_overrun_without_newline": + return b"4" * (cls.BUFFER_LIMIT + 10) + case "limit_overrun_with_newline": + return b"4" * (cls.BUFFER_LIMIT + 10) + _NEWLINES[newline] + case _: + pytest.fail("Invalid fixture parameter") + + @pytest.fixture(scope="class") + @classmethod + def invalid_partial_data_extra_data(cls, invalid_partial_data: bytes) -> bytes: + if len(invalid_partial_data) > cls.BUFFER_LIMIT: + return b"" + return b"remaining_data" #### Other @pytest.fixture(scope="class") @staticmethod - def oneshot_extra_data() -> bytes: - pytest.skip("Does not recognize extra data") + def oneshot_extra_data(newline: Literal["CR", "LF", "CRLF"]) -> bytes: + return _NEWLINES[newline] + b"remaining_data" diff --git a/tests/functional_test/test_serializers/test_namedtuple.py b/tests/functional_test/test_serializers/test_namedtuple.py index 58dc1ffb..c6855267 100644 --- a/tests/functional_test/test_serializers/test_namedtuple.py +++ b/tests/functional_test/test_serializers/test_namedtuple.py @@ -2,6 +2,7 @@ from __future__ import annotations +import struct from typing import Any, NamedTuple, final from easynetwork.serializers.struct import NamedTupleStructSerializer @@ -27,6 +28,10 @@ class Point(NamedTuple): STRUCT_FORMAT = f"!{''.join(map(POINT_FIELD_FORMATS.__getitem__, Point._fields))}" +def pack_point(p: Point, *, encoding: str = "utf-8") -> bytes: + return struct.pack(STRUCT_FORMAT, p.name.encode(encoding), p.x, p.y) + + @final class TestNamedTupleStructSerializer(BaseTestIncrementalSerializer): #### Serializers @@ -67,9 +72,7 @@ def packet_to_serialize(request: Any) -> Any: @pytest.fixture(scope="class") @classmethod def expected_complete_data(cls, packet_to_serialize: Point) -> bytes: - import struct - - return struct.pack(STRUCT_FORMAT, packet_to_serialize.name.encode(), packet_to_serialize.x, packet_to_serialize.y) + return pack_point(packet_to_serialize) #### Incremental Serialize @@ -94,12 +97,18 @@ def complete_data_for_incremental_deserialize(complete_data: bytes) -> bytes: #### Invalid data - @pytest.fixture(scope="class") + @pytest.fixture(scope="class", params=["missing_data", "unicode_error"]) @staticmethod - def invalid_complete_data(complete_data: bytes) -> bytes: - return complete_data[:-1] # Missing data + def invalid_complete_data(request: pytest.FixtureRequest) -> bytes: + match request.param: + case "missing_data": + return pack_point(Point("string", -4, b"y"))[:-3] + case "unicode_error": + return pack_point(Point("é", -4, b"y"), encoding="latin-1") + case _: + pytest.fail("Invalid parameter") - @pytest.fixture + @pytest.fixture(scope="class") @staticmethod def invalid_partial_data() -> bytes: - pytest.skip("Cannot be tested") + return pack_point(Point("é", -4, b"y"), encoding="latin-1") diff --git a/tests/functional_test/test_serializers/test_pickle.py b/tests/functional_test/test_serializers/test_pickle.py index 1127ae18..85f90688 100644 --- a/tests/functional_test/test_serializers/test_pickle.py +++ b/tests/functional_test/test_serializers/test_pickle.py @@ -91,17 +91,7 @@ def complete_data( #### Invalid data - @pytest.fixture + @pytest.fixture(scope="class") @staticmethod def invalid_complete_data() -> bytes: pytest.skip("pickle.Unpickler() raises SystemError for some invalid inputs :)") - - @pytest.fixture - @staticmethod - def invalid_partial_data() -> bytes: - pytest.skip("pickle.Unpickler() raises SystemError for some invalid inputs :)") - - @pytest.fixture(scope="class") - @staticmethod - def invalid_partial_data_extra_data() -> bytes | None: - return None diff --git a/tests/unit_test/test_serializers/test_abc.py b/tests/unit_test/test_serializers/test_abc.py index c1e8baff..d71a9b63 100644 --- a/tests/unit_test/test_serializers/test_abc.py +++ b/tests/unit_test/test_serializers/test_abc.py @@ -437,10 +437,9 @@ def test____incremental_deserialize____reached_limit( mock_deserialize_func.assert_not_called() if separator_found: assert str(exc_info.value) == "Separator is found, but chunk is longer than limit" - assert exc_info.value.remaining_data == b"" else: assert str(exc_info.value) == "Separator is not found, and chunk exceed the limit" - assert exc_info.value.remaining_data == b"\r" + assert exc_info.value.remaining_data == b"" assert exc_info.value.error_info is None diff --git a/tests/unit_test/test_serializers/test_json.py b/tests/unit_test/test_serializers/test_json.py index cf1df0e7..e557861f 100644 --- a/tests/unit_test/test_serializers/test_json.py +++ b/tests/unit_test/test_serializers/test_json.py @@ -654,3 +654,4 @@ def test____raw_parse____reached_limit(self, start_frame: bytes, end_frame: byte assert str(exc_info.value) == "JSON object's end frame is found, but chunk is longer than limit" else: assert str(exc_info.value) == "JSON object's end frame is not found, and chunk exceed the limit" + assert exc_info.value.remaining_data == b"" diff --git a/tests/unit_test/test_serializers/test_line.py b/tests/unit_test/test_serializers/test_line.py index 5b7d1990..5c9783c0 100644 --- a/tests/unit_test/test_serializers/test_line.py +++ b/tests/unit_test/test_serializers/test_line.py @@ -162,6 +162,26 @@ def test____deserialize____decode_string( assert isinstance(line, str) assert line == "abc" + @pytest.mark.parametrize("position", ["beginning", "between"]) + def test____deserialize____newline_in_string_error( + self, + position: Literal["beginning", "between"], + serializer: StringLineSerializer, + ) -> None: + # Arrange + separator = serializer.separator.decode() + match position: + case "beginning": + data = f"{separator}a".encode() + case "between": + data = f"a{separator}b".encode() + case _: + pytest.fail("Invalid fixture") + + # Act & Assert + with pytest.raises(DeserializeError, match=r"^Newline found in string$"): + serializer.deserialize(data) + @pytest.mark.parametrize("with_newlines", [False, True], ids=lambda boolean: f"with_newlines=={boolean}") @pytest.mark.parametrize("encoding", ["ascii", "utf-8"], indirect=True) def test____deserialize____decode_string_error( diff --git a/tests/unit_test/test_serializers/test_tools.py b/tests/unit_test/test_serializers/test_tools.py index 649f39a3..26e0eb91 100644 --- a/tests/unit_test/test_serializers/test_tools.py +++ b/tests/unit_test/test_serializers/test_tools.py @@ -249,10 +249,9 @@ def test____read_until____reached_limit(self, separator_found: bytes) -> None: # Assert if separator_found: assert str(exc_info.value) == "Separator is found, but chunk is longer than limit" - assert exc_info.value.remaining_data == b"" else: assert str(exc_info.value) == "Separator is not found, and chunk exceed the limit" - assert exc_info.value.remaining_data == b"\r" + assert exc_info.value.remaining_data == b"" def test____read_until____empty_separator(self) -> None: # Arrange