Skip to content

Commit 29cb386

Browse files
committed
Fix pyright type errors and test file coverage
1 parent 2c5e19a commit 29cb386

2 files changed

Lines changed: 43 additions & 68 deletions

File tree

tests/test_event_roundtrip.py

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@
3737
_registry = SubscriptionRegistry()
3838
_retained_store = RetainedValueStore()
3939
_topic_descriptors: list[EventTopicDescriptor] = [
40-
EventTopicDescriptor(pattern="test/+", description="Test topic"),
41-
EventTopicDescriptor(pattern="retained/value", description="Retained", retained=True),
40+
EventTopicDescriptor(pattern="test/+", description="Test topic", schema=None),
41+
EventTopicDescriptor(pattern="retained/value", description="Retained", retained=True, schema=None),
4242
]
4343

4444

4545
async def _on_subscribe_events(
4646
ctx: RequestContext[ServerSession, Any],
4747
params: EventSubscribeParams,
4848
) -> EventSubscribeResult:
49-
subscribed = []
49+
subscribed: list[SubscribedTopic] = []
5050
for pattern in params.topics:
5151
await _registry.add("test-session", pattern)
5252
subscribed.append(SubscribedTopic(pattern=pattern))
@@ -106,18 +106,18 @@ async def _message_handler(
106106
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
107107
) -> None:
108108
if isinstance(message, Exception):
109-
raise message
109+
raise message # pragma: no cover
110110

111111

112112
async def _run_server(server_session: ServerSession, server: Server) -> None:
113113
async for message in server_session.incoming_messages:
114114
if isinstance(message, Exception):
115-
raise message
115+
raise message # pragma: no cover
116116
if isinstance(message, RequestResponder):
117117
with message:
118118
req = message.request
119119
handler = server.request_handlers.get(type(req.root))
120-
if handler:
120+
if handler: # pragma: no branch
121121
token = request_ctx.set(
122122
RequestContext(
123123
request_id=message.request_id,
@@ -204,7 +204,7 @@ async def event_handler(params: EventParams):
204204
assert received_events[0].timestamp == explicit_ts
205205

206206
tg.cancel_scope.cancel()
207-
except (anyio.ClosedResourceError, anyio.EndOfStream):
207+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
208208
pass
209209

210210

@@ -250,7 +250,7 @@ async def test_subscribe_receives_retained_values():
250250
assert sub_result.retained[0].payload == "cached"
251251

252252
tg.cancel_scope.cancel()
253-
except (anyio.ClosedResourceError, anyio.EndOfStream):
253+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
254254
pass
255255

256256

@@ -294,7 +294,7 @@ async def test_unsubscribe_stops_matching():
294294
assert matches == set()
295295

296296
tg.cancel_scope.cancel()
297-
except (anyio.ClosedResourceError, anyio.EndOfStream):
297+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
298298
pass
299299

300300

@@ -360,7 +360,7 @@ async def event_handler(params: EventParams):
360360
assert received_events[0].event_id == "evt-match"
361361

362362
tg.cancel_scope.cancel()
363-
except (anyio.ClosedResourceError, anyio.EndOfStream):
363+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
364364
pass
365365

366366

@@ -408,7 +408,7 @@ async def test_list_events():
408408
assert by_pattern["retained/value"].retained is True
409409

410410
tg.cancel_scope.cancel()
411-
except (anyio.ClosedResourceError, anyio.EndOfStream):
411+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
412412
pass
413413

414414

@@ -421,8 +421,8 @@ async def _on_subscribe_events_with_rejection(
421421
params: EventSubscribeParams,
422422
) -> EventSubscribeResult:
423423
"""Subscribe handler that rejects undeclared topic patterns."""
424-
subscribed = []
425-
rejected = []
424+
subscribed: list[SubscribedTopic] = []
425+
rejected: list[RejectedTopic] = []
426426
for pattern in params.topics:
427427
if pattern in _declared_patterns:
428428
await _registry.add("test-session", pattern)
@@ -444,19 +444,7 @@ async def subscribe_handler(req: EventSubscribeRequest):
444444
result = await _on_subscribe_events_with_rejection(ctx, req.params)
445445
return types.ServerResult(result)
446446

447-
async def unsubscribe_handler(req: EventUnsubscribeRequest):
448-
ctx = request_ctx.get()
449-
result = await _on_unsubscribe_events(ctx, req.params)
450-
return types.ServerResult(result)
451-
452-
async def list_handler(req: EventListRequest):
453-
ctx = request_ctx.get()
454-
result = await _on_list_events(ctx, req.params)
455-
return types.ServerResult(result)
456-
457447
server.request_handlers[EventSubscribeRequest] = subscribe_handler
458-
server.request_handlers[EventUnsubscribeRequest] = unsubscribe_handler
459-
server.request_handlers[EventListRequest] = list_handler
460448
return server
461449

462450

@@ -499,7 +487,7 @@ async def test_subscribe_rejects_undeclared_topic():
499487
assert sub_result.rejected[0].reason == "unknown_topic"
500488

501489
tg.cancel_scope.cancel()
502-
except (anyio.ClosedResourceError, anyio.EndOfStream):
490+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
503491
pass
504492

505493

@@ -534,7 +522,7 @@ async def test_topic_matches_subscriptions_recompiles_on_cache_miss():
534522

535523
# Non-matching topic exercises the return False path.
536524
assert client_session._topic_matches_subscriptions("other/thing") is False
537-
except (anyio.ClosedResourceError, anyio.EndOfStream):
525+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
538526
pass
539527

540528

@@ -582,5 +570,5 @@ async def test_subscribe_events_skips_recompile_for_cached_pattern():
582570
assert client_session._subscription_regex_cache["test/+"] is cached_regex
583571

584572
tg.cancel_scope.cancel()
585-
except (anyio.ClosedResourceError, anyio.EndOfStream):
573+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
586574
pass

tests/test_event_types.py

Lines changed: 27 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def test_roundtrip(self):
6363

6464
class TestEventTopicDescriptor:
6565
def test_basic(self):
66-
d = EventTopicDescriptor(pattern="foo/bar", description="A topic", retained=True)
66+
d = EventTopicDescriptor(pattern="foo/bar", description="A topic", retained=True, schema=None)
6767
assert d.pattern == "foo/bar"
6868
assert d.description == "A topic"
6969
assert d.retained is True
@@ -84,7 +84,7 @@ def test_defaults(self):
8484
def test_with_topics(self):
8585
c = EventsCapability(
8686
topics=[
87-
EventTopicDescriptor(pattern="a/b", description="Alpha-bravo", retained=True),
87+
EventTopicDescriptor(pattern="a/b", description="Alpha-bravo", retained=True, schema=None),
8888
],
8989
instructions="Subscribe to a/b for updates",
9090
)
@@ -117,11 +117,13 @@ def test_inherits_meta(self):
117117
)
118118
assert p.meta is None
119119
# _meta field should be serializable
120-
p2 = EventParams(
121-
topic="test/topic",
122-
eventId="abc123",
123-
payload="hello",
124-
_meta={"related_request_id": "req-1"},
120+
p2 = EventParams.model_validate(
121+
{
122+
"topic": "test/topic",
123+
"eventId": "abc123",
124+
"payload": "hello",
125+
"_meta": {"related_request_id": "req-1"},
126+
}
125127
)
126128
data = p2.model_dump(by_alias=True)
127129
assert data["_meta"] == {"related_request_id": "req-1"}
@@ -240,7 +242,7 @@ def test_unsubscribe_result(self):
240242
def test_list_result(self):
241243
r = EventListResult(
242244
topics=[
243-
EventTopicDescriptor(pattern="x/y", description="desc"),
245+
EventTopicDescriptor(pattern="x/y", description="desc", schema=None),
244246
]
245247
)
246248
data = r.model_dump(by_alias=True, mode="json")
@@ -256,29 +258,29 @@ class TestInvalidEventEffect:
256258
def test_invalid_type_rejected(self):
257259
"""EventEffect with an invalid type literal should be rejected by Pydantic."""
258260
with pytest.raises(ValidationError):
259-
EventEffect(type="bogus_effect")
261+
EventEffect.model_validate({"type": "bogus_effect"})
260262

261263
def test_invalid_priority_rejected(self):
262264
"""EventEffect with an invalid priority literal should be rejected."""
263265
with pytest.raises(ValidationError):
264-
EventEffect(type="inject_context", priority="super_duper")
266+
EventEffect.model_validate({"type": "inject_context", "priority": "super_duper"})
265267

266268

267269
class TestInvalidEventParams:
268270
def test_missing_topic_rejected(self):
269271
"""EventParams missing required 'topic' field should fail validation."""
270272
with pytest.raises(ValidationError):
271-
EventParams(eventId="e1", payload="x")
273+
EventParams.model_validate({"eventId": "e1", "payload": "x"})
272274

273275
def test_missing_event_id_rejected(self):
274276
"""EventParams missing required 'event_id' field should fail validation."""
275277
with pytest.raises(ValidationError):
276-
EventParams(topic="a/b", payload="x")
278+
EventParams.model_validate({"topic": "a/b", "payload": "x"})
277279

278280
def test_missing_payload_rejected(self):
279281
"""EventParams missing required 'payload' field should fail validation."""
280282
with pytest.raises(ValidationError):
281-
EventParams(topic="a/b", eventId="e1")
283+
EventParams.model_validate({"topic": "a/b", "eventId": "e1"})
282284

283285

284286
# ---------------------------------------------------------------------------
@@ -293,58 +295,43 @@ async def _on_subscribe_events(
293295
ctx: RequestContext[ServerSession, Any],
294296
params: EventSubscribeParams,
295297
) -> EventSubscribeResult:
296-
subscribed = []
298+
subscribed: list[SubscribedTopic] = []
297299
for pattern in params.topics:
298300
await _registry.add("test-session", pattern)
299301
subscribed.append(SubscribedTopic(pattern=pattern))
300302
return EventSubscribeResult(subscribed=subscribed)
301303

302304

303-
async def _on_unsubscribe_events(
304-
ctx: RequestContext[ServerSession, Any],
305-
params: EventUnsubscribeParams,
306-
) -> EventUnsubscribeResult:
307-
for pattern in params.topics:
308-
await _registry.remove("test-session", pattern)
309-
return EventUnsubscribeResult(unsubscribed=params.topics)
310-
311-
312305
def _create_test_server() -> Server:
313306
server = Server("test-events-server")
314307

315308
# Register event handlers via request_handlers dict (keyed by type)
316309
async def subscribe_handler(req: EventSubscribeRequest):
317310
ctx = server.request_context
318-
result = await _on_subscribe_events(ctx, req.root.params if hasattr(req, "root") else req.params)
319-
return types.ServerResult(result)
320-
321-
async def unsubscribe_handler(req: EventUnsubscribeRequest):
322-
ctx = server.request_context
323-
result = await _on_unsubscribe_events(ctx, req.root.params if hasattr(req, "root") else req.params)
311+
result = await _on_subscribe_events(ctx, req.params)
324312
return types.ServerResult(result)
325313

326314
server.request_handlers[EventSubscribeRequest] = subscribe_handler
327-
server.request_handlers[EventUnsubscribeRequest] = unsubscribe_handler
328315
return server
329316

330317

331318
async def _message_handler(
332319
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
333320
) -> None:
334321
if isinstance(message, Exception):
335-
raise message
322+
raise message # pragma: no cover
336323

337324

338325
async def _run_server(server_session: ServerSession, server: Server) -> None:
339326
async for message in server_session.incoming_messages:
340327
if isinstance(message, Exception):
341-
raise message
328+
raise message # pragma: no cover
342329
if isinstance(message, RequestResponder):
343330
with message:
344331
req = message.request
345332
# v1.27.0: request_handlers keyed by type
346333
handler = server.request_handlers.get(type(req.root))
347-
if handler:
334+
if handler: # pragma: no branch
348335
from mcp.server.lowlevel.server import request_ctx
349336

350337
token = request_ctx.set(
@@ -423,7 +410,7 @@ async def event_handler(params: EventParams):
423410
assert len(received_events[0].event_id) > 0
424411

425412
tg.cancel_scope.cancel()
426-
except (anyio.ClosedResourceError, anyio.EndOfStream):
413+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
427414
pass
428415

429416

@@ -479,7 +466,7 @@ async def handle_event(params: EventParams):
479466
assert received_events[0].payload == "via-decorator"
480467

481468
tg.cancel_scope.cancel()
482-
except (anyio.ClosedResourceError, anyio.EndOfStream):
469+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
483470
pass
484471

485472

@@ -534,7 +521,7 @@ async def event_handler(params: EventParams):
534521
assert received_events[0].topic == "anything/goes"
535522

536523
tg.cancel_scope.cancel()
537-
except (anyio.ClosedResourceError, anyio.EndOfStream):
524+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
538525
pass
539526

540527

@@ -598,7 +585,7 @@ async def event_handler(params: EventParams):
598585
assert received_events[0].payload == "match"
599586

600587
tg.cancel_scope.cancel()
601-
except (anyio.ClosedResourceError, anyio.EndOfStream):
588+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
602589
pass
603590

604591

@@ -647,7 +634,7 @@ async def test_handle_event_with_no_handler():
647634
# If we get here without exception, the test passes
648635

649636
tg.cancel_scope.cancel()
650-
except (anyio.ClosedResourceError, anyio.EndOfStream):
637+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
651638
pass
652639

653640

@@ -757,7 +744,7 @@ async def event_handler(params: EventParams):
757744
assert evt.expires_at == future
758745

759746
tg.cancel_scope.cancel()
760-
except (anyio.ClosedResourceError, anyio.EndOfStream):
747+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
761748
pass
762749

763750

@@ -816,5 +803,5 @@ async def event_handler(params: EventParams):
816803
assert ts <= datetime.now(timezone.utc)
817804

818805
tg.cancel_scope.cancel()
819-
except (anyio.ClosedResourceError, anyio.EndOfStream):
806+
except (anyio.ClosedResourceError, anyio.EndOfStream): # pragma: no cover
820807
pass

0 commit comments

Comments
 (0)