diff --git a/src/pygwire/__init__.py b/src/pygwire/__init__.py index 963448a..db1f80d 100644 --- a/src/pygwire/__init__.py +++ b/src/pygwire/__init__.py @@ -9,11 +9,16 @@ FrontendConnection, ) from pygwire.constants import ConnectionPhase, ProtocolVersion, TransactionStatus -from pygwire.exceptions import ProtocolError, PygwireError +from pygwire.exceptions import ( + DecodingError, + FramingError, + ProtocolError, + PygwireError, + StateMachineError, +) from pygwire.state_machine import ( BackendStateMachine, FrontendStateMachine, - StateMachineError, ) __version__ = version("pygwire") @@ -24,6 +29,8 @@ "BackendStateMachine", "Connection", "ConnectionPhase", + "DecodingError", + "FramingError", "FrontendConnection", "FrontendMessageDecoder", "FrontendStateMachine", diff --git a/src/pygwire/connection.py b/src/pygwire/connection.py index 1c1512a..ded078b 100644 --- a/src/pygwire/connection.py +++ b/src/pygwire/connection.py @@ -64,11 +64,11 @@ def on_send(self, data: bytes) -> None: from pygwire.codec import BackendMessageDecoder, FrontendMessageDecoder from pygwire.constants import ConnectionPhase +from pygwire.exceptions import StateMachineError from pygwire.messages import PGMessage from pygwire.state_machine import ( BackendStateMachine, FrontendStateMachine, - StateMachineError, ) logger = logging.getLogger(__name__) diff --git a/src/pygwire/exceptions.py b/src/pygwire/exceptions.py index 442f7cc..449f69b 100644 --- a/src/pygwire/exceptions.py +++ b/src/pygwire/exceptions.py @@ -1,8 +1,11 @@ """Pygwire exception hierarchy.""" __all__ = [ + "DecodingError", + "FramingError", "ProtocolError", "PygwireError", + "StateMachineError", ] @@ -12,3 +15,15 @@ class PygwireError(Exception): class ProtocolError(PygwireError): """Raised when protocol framing or content is invalid.""" + + +class FramingError(ProtocolError): + """Raised when message framing is invalid (size, identifier, truncation).""" + + +class DecodingError(ProtocolError): + """Raised when a message payload cannot be decoded.""" + + +class StateMachineError(ProtocolError): + """Raised when an invalid message is sent/received for the current state.""" diff --git a/src/pygwire/framing.py b/src/pygwire/framing.py index f3ee133..0562532 100644 --- a/src/pygwire/framing.py +++ b/src/pygwire/framing.py @@ -23,7 +23,7 @@ from typing import TYPE_CHECKING from pygwire.constants import ConnectionPhase, MessageDirection -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import FramingError from pygwire.messages import ( NEGOTIATION_REGISTRY, STANDARD_REGISTRY, @@ -82,7 +82,7 @@ def try_parse( (message, bytes_consumed) if successful, None if insufficient data Raises: - ProtocolError: If message is malformed or unknown + FramingError: If message is malformed or unknown """ ... @@ -117,7 +117,7 @@ def try_parse( (length,) = _LENGTH_STRUCT.unpack_from(buf, pos) if length > self._max_message_size: - raise ProtocolError( + raise FramingError( f"Startup message length {length} exceeds maximum allowed size " f"({self._max_message_size})" ) @@ -129,17 +129,17 @@ def try_parse( payload_end = pos + length payload = buf[payload_start:payload_end] if len(payload) < 4: - raise ProtocolError("Startup message payload too short for version code") + raise FramingError("Startup message payload too short for version code") (version_code,) = _LENGTH_STRUCT.unpack_from(payload) msg_cls = STARTUP_REGISTRY.lookup(version_code) if msg_cls is None: - raise ProtocolError(f"Unknown startup message version code: {version_code:#010x}") + raise FramingError(f"Unknown startup message version code: {version_code:#010x}") try: msg = msg_cls.decode(payload) except struct.error as e: - raise ProtocolError(f"{msg_cls.__name__} message truncated or malformed: {e}") from e + raise FramingError(f"{msg_cls.__name__} message truncated or malformed: {e}") from e return msg, length @@ -170,13 +170,13 @@ def try_parse( msg_cls = NEGOTIATION_REGISTRY.lookup(byte_value, phase) if msg_cls is None: - raise ProtocolError(f"Unknown negotiation byte: {byte_value!r} in phase {phase.name}") + raise FramingError(f"Unknown negotiation byte: {byte_value!r} in phase {phase.name}") payload = buf[pos : pos + 1] try: msg = msg_cls.decode(payload) except struct.error as e: - raise ProtocolError(f"{msg_cls.__name__} message malformed: {e}") from e + raise FramingError(f"{msg_cls.__name__} message malformed: {e}") from e return msg, 1 @@ -212,7 +212,7 @@ def try_parse( identifier = bytes((buf[pos],)) (length,) = _LENGTH_STRUCT.unpack_from(buf, pos + 1) if length > self._max_message_size: - raise ProtocolError( + raise FramingError( f"Message length {length} exceeds maximum allowed size ({self._max_message_size})" ) @@ -226,14 +226,14 @@ def try_parse( msg_cls = STANDARD_REGISTRY.lookup(identifier, phase, direction) if msg_cls is None: - raise ProtocolError( + raise FramingError( f"Unknown message identifier: {identifier!r} in phase {phase.name} " f"for direction {direction.value}" ) try: msg = msg_cls.decode(payload) except struct.error as e: - raise ProtocolError(f"{msg_cls.__name__} message truncated or malformed: {e}") from e + raise FramingError(f"{msg_cls.__name__} message truncated or malformed: {e}") from e return msg, total diff --git a/src/pygwire/messages/_auth.py b/src/pygwire/messages/_auth.py index 15267d5..469af68 100644 --- a/src/pygwire/messages/_auth.py +++ b/src/pygwire/messages/_auth.py @@ -8,7 +8,7 @@ from typing import ClassVar, Self from pygwire.constants import ConnectionPhase, MessageDirection -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import DecodingError from pygwire.messages._base import BackendMessage, FrontendMessage, _read_cstring from ._registry import NEGOTIATION_REGISTRY, STANDARD_REGISTRY @@ -41,14 +41,14 @@ def to_wire(self) -> bytes: @classmethod def decode(cls, payload: memoryview) -> Self: if len(payload) < 1: - raise ProtocolError("SSLResponse payload is empty") + raise DecodingError("SSLResponse payload is empty") byte = bytes(payload[0:1]) if byte == b"S": return cls(accepted=True) elif byte == b"N": return cls(accepted=False) else: - raise ProtocolError(f"Unexpected SSL response byte: {byte!r}") + raise DecodingError(f"Unexpected SSL response byte: {byte!r}") @NEGOTIATION_REGISTRY.register(b"G", ConnectionPhase.GSS_NEGOTIATION) @@ -75,14 +75,14 @@ def to_wire(self) -> bytes: @classmethod def decode(cls, payload: memoryview) -> Self: if len(payload) < 1: - raise ProtocolError("GSSResponse payload is empty") + raise DecodingError("GSSResponse payload is empty") byte = bytes(payload[0:1]) if byte == b"G": return cls(accepted=True) elif byte == b"N": return cls(accepted=False) else: - raise ProtocolError(f"Unexpected GSS response byte: {byte!r}") + raise DecodingError(f"Unexpected GSS response byte: {byte!r}") # ═══════════════════════════════════════════════════════════════════════════ @@ -137,7 +137,7 @@ def decode(cls, payload: memoryview) -> Self: (code,) = _INT32.unpack_from(payload) sub_cls = _AUTH_SUBTYPE_REGISTRY.get(code) if sub_cls is None: - raise ProtocolError(f"Unknown authentication code: {code}") + raise DecodingError(f"Unknown authentication code: {code}") return sub_cls.decode(payload) # type: ignore[return-value] @@ -323,7 +323,7 @@ def decode(cls, payload: memoryview) -> Self: try: pwd, _ = _read_cstring(payload, 0) return cls(password=pwd) - except ProtocolError: + except DecodingError: return cls(password=bytes(payload)) diff --git a/src/pygwire/messages/_base.py b/src/pygwire/messages/_base.py index 6a50699..9acff48 100644 --- a/src/pygwire/messages/_base.py +++ b/src/pygwire/messages/_base.py @@ -6,7 +6,7 @@ from dataclasses import dataclass from typing import ClassVar, Self -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import DecodingError # --------------------------------------------------------------------------- # Base message classes @@ -123,4 +123,4 @@ def _read_cstring(payload: memoryview, offset: int) -> tuple[str, int]: value = bytes(payload[offset:end]).decode("utf-8") return value, end + 1 except ValueError: - raise ProtocolError("Unterminated string in payload") from None + raise DecodingError("Unterminated string in payload") from None diff --git a/src/pygwire/state_machine.py b/src/pygwire/state_machine.py index deb674f..0c1d59c 100644 --- a/src/pygwire/state_machine.py +++ b/src/pygwire/state_machine.py @@ -41,7 +41,7 @@ from pygwire import messages from pygwire.constants import ConnectionPhase -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import StateMachineError __all__ = [ "BackendStateMachine", @@ -60,10 +60,6 @@ class MessageAction(StrEnum): RECEIVE = "receive" -class StateMachineError(ProtocolError): - """Raised when an invalid message is sent/received for the current state.""" - - class _Transition: """Transition to a fixed phase.""" diff --git a/tests/unit/test_codec.py b/tests/unit/test_codec.py index a4e3036..a312d93 100644 --- a/tests/unit/test_codec.py +++ b/tests/unit/test_codec.py @@ -11,7 +11,7 @@ from pygwire.connection import BackendConnection, FrontendConnection from pygwire.constants import ProtocolVersion, TransactionStatus -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import FramingError from pygwire.messages import ( AuthenticationOk, AuthenticationSASL, @@ -292,16 +292,16 @@ def test_startup_transitions_to_standard_after_startup_message(self): assert isinstance(msgs[0], Query) def test_startup_message_too_short_raises_error(self): - """Test that short startup message raises ProtocolError.""" + """Test that short startup message raises FramingError.""" conn = BackendConnection() - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): list(conn.receive(b"\x00\x00\x00\x06\x00\x01")) def test_unknown_startup_version_raises_error(self): - """Test unknown startup version code raises ProtocolError.""" + """Test unknown startup version code raises FramingError.""" conn = BackendConnection() wire = b"\x00\x00\x00\x08\xff\xff\xff\xff" - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): list(conn.receive(wire)) @@ -416,14 +416,14 @@ def test_unknown_backend_message_identifier(self): """Test that unknown backend message identifier raises error.""" conn = FrontendConnection(initial_phase=ConnectionPhase.READY, strict=False) wire = b"x\x00\x00\x00\x04" - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): list(conn.receive(wire)) def test_unknown_frontend_message_identifier(self): """Test that unknown frontend message identifier raises error.""" conn = BackendConnection(initial_phase=ConnectionPhase.READY, strict=False) wire = b"Z\x00\x00\x00\x05I" # 'Z' is backend ReadyForQuery, not a frontend message - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): list(conn.receive(wire)) diff --git a/tests/unit/test_framing.py b/tests/unit/test_framing.py index 79f757a..d043888 100644 --- a/tests/unit/test_framing.py +++ b/tests/unit/test_framing.py @@ -5,7 +5,7 @@ import pytest from pygwire.constants import ConnectionPhase, MessageDirection -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import FramingError, ProtocolError from pygwire.framing import ( NegotiationFraming, StandardFraming, @@ -92,40 +92,40 @@ def test_insufficient_data_for_payload(self): assert result is None def test_message_exceeds_max_size(self): - """Test that oversized messages raise ProtocolError.""" + """Test that oversized messages raise FramingError.""" # Create a message that claims to be huge wire = struct.pack("!I", 2 * 1024 * 1024 * 1024) # 2 GB framing = StartupFraming(max_message_size=1024 * 1024) # 1 MB limit - with pytest.raises(ProtocolError, match="exceeds maximum allowed size"): + with pytest.raises(FramingError, match="exceeds maximum allowed size"): framing.try_parse( memoryview(wire), 0, ConnectionPhase.STARTUP, MessageDirection.FRONTEND ) def test_payload_too_short_for_version_code(self): - """Test that payload shorter than 4 bytes raises ProtocolError.""" + """Test that payload shorter than 4 bytes raises FramingError.""" # Length = 5 (header) + 2 (payload) = 7, but payload needs 4 bytes for version wire = struct.pack("!I", 6) + b"ab" framing = StartupFraming() - with pytest.raises(ProtocolError, match="payload too short for version code"): + with pytest.raises(FramingError, match="payload too short for version code"): framing.try_parse( memoryview(wire), 0, ConnectionPhase.STARTUP, MessageDirection.FRONTEND ) def test_unknown_version_code_raises_error(self): - """Test that unknown version code raises ProtocolError.""" + """Test that unknown version code raises FramingError.""" # Create message with invalid version code wire = struct.pack("!II", 8, 0xDEADBEEF) # Invalid version code framing = StartupFraming() - with pytest.raises(ProtocolError, match="Unknown startup message version code"): + with pytest.raises(FramingError, match="Unknown startup message version code"): framing.try_parse( memoryview(wire), 0, ConnectionPhase.STARTUP, MessageDirection.FRONTEND ) def test_malformed_message_raises_error(self): - """Test that malformed message payload raises ProtocolError.""" + """Test that malformed message payload raises ProtocolError (FramingError or DecodingError).""" # StartupMessage with correct version but truncated params wire = struct.pack("!II", 12, 0x00030000) + b"user" # Missing null terminators @@ -242,9 +242,9 @@ def test_insufficient_data(self): assert result is None def test_unknown_negotiation_byte(self): - """Test that unknown negotiation byte raises ProtocolError.""" + """Test that unknown negotiation byte raises FramingError.""" framing = NegotiationFraming() - with pytest.raises(ProtocolError, match="Unknown negotiation byte"): + with pytest.raises(FramingError, match="Unknown negotiation byte"): framing.try_parse( memoryview(b"X"), 0, @@ -253,10 +253,10 @@ def test_unknown_negotiation_byte(self): ) def test_invalid_byte_in_phase(self): - """Test that valid byte in wrong phase raises ProtocolError.""" + """Test that valid byte in wrong phase raises FramingError.""" # 'G' is valid for GSS but not SSL framing = NegotiationFraming() - with pytest.raises(ProtocolError, match="Unknown negotiation byte.*SSL_NEGOTIATION"): + with pytest.raises(FramingError, match="Unknown negotiation byte.*SSL_NEGOTIATION"): framing.try_parse( memoryview(b"G"), 0, @@ -265,7 +265,7 @@ def test_invalid_byte_in_phase(self): ) def test_malformed_message_raises_error(self): - """Test that decode errors are wrapped in ProtocolError.""" + """Test that decode errors are wrapped in FramingError.""" # This should never happen in practice since negotiation messages # are just single bytes, but test the error handling path framing = NegotiationFraming() @@ -360,25 +360,25 @@ def test_insufficient_data_for_payload(self): assert result is None def test_message_exceeds_max_size(self): - """Test that oversized messages raise ProtocolError.""" + """Test that oversized messages raise FramingError.""" # Create a message that claims to be huge wire = b"Q" + struct.pack("!I", 2 * 1024 * 1024 * 1024) # 2 GB framing = StandardFraming(max_message_size=1024 * 1024) # 1 MB limit - with pytest.raises(ProtocolError, match="exceeds maximum allowed size"): + with pytest.raises(FramingError, match="exceeds maximum allowed size"): framing.try_parse(memoryview(wire), 0, ConnectionPhase.READY, MessageDirection.FRONTEND) def test_unknown_message_identifier(self): - """Test that unknown identifier raises ProtocolError.""" + """Test that unknown identifier raises FramingError.""" # Invalid identifier '@' with valid length (not used in PostgreSQL protocol) wire = b"@" + struct.pack("!I", 4) framing = StandardFraming() - with pytest.raises(ProtocolError, match="Unknown message identifier"): + with pytest.raises(FramingError, match="Unknown message identifier"): framing.try_parse(memoryview(wire), 0, ConnectionPhase.READY, MessageDirection.FRONTEND) def test_unknown_identifier_in_phase(self): - """Test that valid identifier in wrong phase raises ProtocolError.""" + """Test that valid identifier in wrong phase raises FramingError.""" # Parse ('P') is valid in EXTENDED_QUERY/READY but not in AUTHENTICATING from pygwire.messages import Parse @@ -386,7 +386,7 @@ def test_unknown_identifier_in_phase(self): wire = msg.to_wire() framing = StandardFraming() - with pytest.raises(ProtocolError, match="Unknown message identifier"): + with pytest.raises(FramingError, match="Unknown message identifier"): framing.try_parse( memoryview(wire), 0, @@ -395,12 +395,12 @@ def test_unknown_identifier_in_phase(self): ) def test_malformed_message_raises_error(self): - """Test that malformed message payload raises ProtocolError.""" + """Test that malformed message payload raises FramingError.""" # AuthenticationOk with truncated payload (should have 8 bytes total) wire = b"R" + struct.pack("!I", 2) + b"" # Length too short framing = StandardFraming() - with pytest.raises(ProtocolError, match="truncated or malformed"): + with pytest.raises(FramingError, match="truncated or malformed"): framing.try_parse( memoryview(wire), 0, @@ -598,7 +598,7 @@ def test_custom_max_message_size(self): # Test startup framing respects limit huge_startup = struct.pack("!I", max_size + 1) - with pytest.raises(ProtocolError, match="exceeds maximum"): + with pytest.raises(FramingError, match="exceeds maximum"): startup_framing.try_parse( memoryview(huge_startup), 0, @@ -608,7 +608,7 @@ def test_custom_max_message_size(self): # Test standard framing respects limit huge_standard = b"Q" + struct.pack("!I", max_size + 1) - with pytest.raises(ProtocolError, match="exceeds maximum"): + with pytest.raises(FramingError, match="exceeds maximum"): standard_framing.try_parse( memoryview(huge_standard), 0, diff --git a/tests/unit/test_malformed_payloads.py b/tests/unit/test_malformed_payloads.py index 4510d35..3432075 100644 --- a/tests/unit/test_malformed_payloads.py +++ b/tests/unit/test_malformed_payloads.py @@ -5,7 +5,7 @@ import pytest from pygwire.codec import BackendMessageDecoder, FrontendMessageDecoder -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import DecodingError, FramingError from pygwire.messages import ( AuthenticationSASL, Bind, @@ -73,8 +73,8 @@ def test_row_description_truncated_field_count(self): corrupted = wire[:1] + wire[1:5] + struct.pack("!H", 5) + wire[7:] # Feed the corrupted message and try to read - # This should raise ProtocolError due to incomplete data or struct.unpack failure - with pytest.raises(ProtocolError): + # This should raise DecodingError due to incomplete data or struct.unpack failure + with pytest.raises(DecodingError): decoder.feed(corrupted) next(decoder) @@ -90,8 +90,8 @@ def test_data_row_truncated_column_count(self): # Wire format: 'D' + Int32(length) + Int16(column_count) + columns corrupted = wire[:1] + wire[1:5] + struct.pack("!H", 10) + wire[7:] - # Either feed or read should raise ProtocolError - with pytest.raises(ProtocolError): + # Either feed or read should raise FramingError + with pytest.raises(FramingError): decoder.feed(corrupted) next(decoder) @@ -111,7 +111,7 @@ def test_authentication_sasl_truncated_mechanisms(self): new_length = len(truncated) - 1 truncated = truncated[:1] + struct.pack("!I", new_length) + truncated[5:] - with pytest.raises(ProtocolError): + with pytest.raises(DecodingError): decoder.feed(truncated) next(decoder) @@ -134,7 +134,7 @@ def test_bind_truncated_parameter_values(self): new_length = len(truncated) - 1 truncated = truncated[:1] + struct.pack("!I", new_length) + truncated[5:] - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(truncated) next(decoder) @@ -159,7 +159,7 @@ def test_error_response_truncated_fields(self): new_length = len(truncated) - 1 truncated = truncated[:1] + struct.pack("!I", new_length) + truncated[5:] - with pytest.raises(ProtocolError): + with pytest.raises(DecodingError): decoder.feed(truncated) next(decoder) @@ -177,7 +177,7 @@ def test_parameter_description_negative_count(self): length = len(payload) + 4 wire = b"t" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) @@ -190,7 +190,7 @@ def test_row_description_negative_field_count(self): length = len(payload) + 4 wire = b"T" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(DecodingError): decoder.feed(wire) next(decoder) @@ -203,7 +203,7 @@ def test_data_row_negative_column_count(self): length = len(payload) + 4 wire = b"D" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) @@ -216,7 +216,7 @@ def test_copy_in_response_negative_column_count(self): length = len(payload) + 4 wire = b"G" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) @@ -229,7 +229,7 @@ def test_function_call_negative_argument_count(self): length = len(payload) + 4 wire = b"F" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) @@ -314,7 +314,7 @@ def test_data_row_empty_payload(self): # 'D' + length(4) + empty payload wire = b"D" + struct.pack("!I", 4) - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) @@ -325,7 +325,7 @@ def test_row_description_empty_payload(self): # 'T' + length(4) + empty payload wire = b"T" + struct.pack("!I", 4) - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) @@ -338,7 +338,7 @@ def test_notification_response_too_short(self): length = len(payload) + 4 wire = b"A" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(DecodingError): decoder.feed(wire) next(decoder) @@ -366,7 +366,7 @@ def test_bind_null_parameter_with_invalid_length(self): length = len(payload) + 4 wire = b"B" + struct.pack("!I", length) + payload - with pytest.raises(ProtocolError): + with pytest.raises(FramingError): decoder.feed(wire) next(decoder) diff --git a/tests/unit/test_messages_auth.py b/tests/unit/test_messages_auth.py index f482ee8..1ff5d7c 100644 --- a/tests/unit/test_messages_auth.py +++ b/tests/unit/test_messages_auth.py @@ -2,7 +2,7 @@ import pytest -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import DecodingError from pygwire.messages import ( Authentication, AuthenticationCleartextPassword, @@ -52,8 +52,8 @@ def test_decode_not_supported(self): assert response.accepted is False def test_decode_invalid_raises_error(self): - """Test decoding invalid byte raises ProtocolError.""" - with pytest.raises(ProtocolError, match="Unexpected SSL response byte"): + """Test decoding invalid byte raises DecodingError.""" + with pytest.raises(DecodingError, match="Unexpected SSL response byte"): SSLResponse.decode(memoryview(b"X")) @@ -86,8 +86,8 @@ def test_decode_not_supported(self): assert response.accepted is False def test_decode_invalid_raises_error(self): - """Test decoding invalid byte raises ProtocolError.""" - with pytest.raises(ProtocolError, match="Unexpected GSS response byte"): + """Test decoding invalid byte raises DecodingError.""" + with pytest.raises(DecodingError, match="Unexpected GSS response byte"): GSSResponse.decode(memoryview(b"X")) @@ -501,9 +501,9 @@ class TestAuthenticationDispatcher: """Tests for Authentication message dispatcher.""" def test_unknown_auth_code_raises_error(self): - """Test that unknown auth code raises ProtocolError.""" + """Test that unknown auth code raises DecodingError.""" wire = b"\x00\x00\x99\x99" # Unknown auth code - with pytest.raises(ProtocolError, match="Unknown authentication code"): + with pytest.raises(DecodingError, match="Unknown authentication code"): Authentication.decode(memoryview(wire)) def test_dispatcher_routes_to_correct_subclass(self): diff --git a/tests/unit/test_messages_startup.py b/tests/unit/test_messages_startup.py index 6c1acb3..3208632 100644 --- a/tests/unit/test_messages_startup.py +++ b/tests/unit/test_messages_startup.py @@ -3,7 +3,7 @@ import pytest from pygwire.constants import ProtocolVersion -from pygwire.exceptions import ProtocolError +from pygwire.exceptions import DecodingError from pygwire.messages import ( CancelRequest, GSSEncRequest, @@ -127,11 +127,11 @@ def test_params_initialization(self): assert msg.params == params def test_decode_unterminated_string_raises_error(self): - """Test that unterminated string raises ProtocolError.""" + """Test that unterminated string raises DecodingError.""" # Create malformed payload: version + "user" without null terminator wire = ProtocolVersion.V3_0.to_bytes(4, "big") + b"user" - with pytest.raises(ProtocolError, match="Unterminated string"): + with pytest.raises(DecodingError, match="Unterminated string"): StartupMessage.decode(memoryview(wire)) def test_special_characters_in_params(self):