Code review (code-review skill, xhigh) of057c1fd: 6 findings, all fixed. - unsubscribe_slot handles a failing reader: the legacy OpcManager's delete() raises when the server session is gone, which still stopped the main loop. The failure is notified (OPC_UNSUBSCRIBE_ERROR_<slot>:<server>) and logged, the slot is dropped from that reader's books (as the v2 engine already does) so it can be subscribed again, and the other readers are still unsubscribed. Repo convention: a single server failure must not kill the pod. - update_slot_config unsubscribes a slot deleted from Redis right away (_unsubscribe_removed_slots) instead of leaving it to the snapshot diff in update_ingestor_manager, which leaked the subscription if anything in between failed. The later diff call is now a no-op. - manage_leases unsubscribes the extra slots before giving their leases back, so another pod never takes a slot this pod is still reading. - unsubscribe_slot iterates a snapshot of the readers (list(...) # NOSONAR, as in pool.close_all / ServerReader.shutdown): the loop awaits, so a reader added or removed meanwhile must not break the iteration. - Contract test with the real readers (ServerReader over the real SubscriptionEngine, and the legacy OpcManager): both expose `subscriptions` keyed by slot and both are unsubscribed (subscription deleted, pool unpinned). - The reader test double moves to tests/unit/conftest.py (reader_double). 283 unit tests; the 3 new behavior tests fail on057c1fd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
21 lines
585 B
Python
21 lines
585 B
Python
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
from pytest import fixture
|
|
|
|
|
|
@fixture
|
|
def reader_double():
|
|
"""Factory of OPC reader doubles holding subscriptions for the given slots.
|
|
|
|
Like the real readers (ServerReader, OpcManager), `subscriptions` is keyed by slot
|
|
and `unsubscribe` drops the slot from it.
|
|
"""
|
|
|
|
def make(*slots):
|
|
reader = MagicMock()
|
|
reader.subscriptions = {slot: MagicMock() for slot in slots}
|
|
reader.unsubscribe = AsyncMock(side_effect=lambda slot: reader.subscriptions.pop(slot))
|
|
return reader
|
|
|
|
return make
|