from __future__ import annotations import pytest from pydantic import TypeAdapter, ValidationError from yuxi.gateway.protocol import GatewayFrame, RequestFrame, ResponseFrame, EventFrame from yuxi.gateway.protocol.errors import ErrorCode, ErrorDetail _adapter = TypeAdapter(GatewayFrame) class TestGatewayFrameUnion: def test_parse_request_frame(self): data = {"type": "req", "id": "r1", "method": "ping", "params": {}} frame = _adapter.validate_python(data) assert isinstance(frame, RequestFrame) assert frame.id == "r1" assert frame.method == "ping" def test_parse_response_frame(self): data = {"type": "res", "id": "r1", "ok": True} frame = _adapter.validate_python(data) assert isinstance(frame, ResponseFrame) assert frame.ok is True def test_parse_event_frame(self): data = {"type": "event", "event": "typing"} frame = _adapter.validate_python(data) assert isinstance(frame, EventFrame) assert frame.event == "typing" def test_unknown_type_raises(self): with pytest.raises(ValidationError): _adapter.validate_python({"type": "unknown", "id": "x", "ok": True}) def test_parse_response_with_error(self): error_data = {"code": -32600, "message": "bad request", "retryable": False} data = {"type": "res", "id": "r1", "ok": False, "error": error_data} frame = _adapter.validate_python(data) assert isinstance(frame, ResponseFrame) assert frame.ok is False assert frame.error is not None assert isinstance(frame.error, ErrorDetail) assert frame.error.code == ErrorCode.INVALID_REQUEST def test_parse_event_with_payload(self): data = { "type": "event", "event": "message.received", "payload": {"text": "hello"}, "seq": 1, "state_version": 0, } frame = _adapter.validate_python(data) assert isinstance(frame, EventFrame) assert frame.payload == {"text": "hello"} assert frame.seq == 1 def test_missing_type_raises(self): with pytest.raises(ValidationError): _adapter.validate_python({"id": "x", "ok": True}) def test_discriminator_distinguishes_types(self): req = _adapter.validate_python({"type": "req", "id": "req-1", "method": "test"}) res = _adapter.validate_python({"type": "res", "id": "req-1", "ok": True}) evt = _adapter.validate_python({"type": "event", "event": "test.event"}) assert isinstance(req, RequestFrame) assert isinstance(res, ResponseFrame) assert isinstance(evt, EventFrame) assert not isinstance(req, ResponseFrame) assert not isinstance(req, EventFrame) def test_model_dump_roundtrip(self): data = {"type": "req", "id": "r1", "method": "ping", "params": {}} frame = _adapter.validate_python(data) output = _adapter.dump_python(frame, mode="json") assert output["type"] == "req" assert output["id"] == "r1" class TestPackageImport: def test_import_gateway_package(self): import yuxi.gateway # noqa: F401 def test_import_gateway_protocol(self): from yuxi.gateway import protocol # noqa: F401 def test_import_all_public_types(self): from yuxi.gateway import ( RequestFrame, ResponseFrame, EventFrame, GatewayFrame, ErrorCode, ErrorDetail, PROTOCOL_VERSION, negotiate_version, ConnectParams, HelloFeatures, HelloOk, handle_handshake, NonEmptyString, PositiveInt, ChannelStatusRequest, ChannelStartRequest, ChannelLogoutRequest, ) assert RequestFrame is not None assert ResponseFrame is not None assert EventFrame is not None assert GatewayFrame is not None assert ErrorCode is not None assert ErrorDetail is not None assert PROTOCOL_VERSION == 1 assert negotiate_version is not None assert ConnectParams is not None assert HelloFeatures is not None assert HelloOk is not None assert handle_handshake is not None assert NonEmptyString is not None assert PositiveInt is not None assert ChannelStatusRequest is not None assert ChannelStartRequest is not None assert ChannelLogoutRequest is not None def test_import_from_protocol_submodule(self): from yuxi.gateway.protocol import RequestFrame, GatewayFrame assert RequestFrame is not None assert GatewayFrame is not None