Files
LangBot/tests/integration/plugin/test_rag_cancellation_protocol.py
T

65 lines
2.8 KiB
Python

"""Exercise actual installed SDK RPC cancellation, not a mocked cancel contract."""
import asyncio
from types import SimpleNamespace
import pytest
from langbot_plugin.entities.io.actions.enums import RuntimeToPluginAction
from langbot_plugin.runtime.io.handler import ActionResponse
from tests.integration.plugin.test_rag_file_transfer_protocol import BINDING, protocol_stack
@pytest.mark.asyncio
@pytest.mark.parametrize('disconnect', [False, True], ids=['cancel-waiter', 'close-host-connection'])
async def test_b1_host_cancellation_does_not_fence_remote_ingest(tmp_path, monkeypatch, disconnect):
async with protocol_stack(tmp_path, monkeypatch, 'shared', BINDING) as stack:
entered, release, finished = asyncio.Event(), asyncio.Event(), asyncio.Event()
vectors = set()
selected = SimpleNamespace(_runtime_plugin_handler=stack.bridge)
monkeypatch.setattr(stack.runtime.plugin_mgr, '_get_connected_rag_plugin', lambda *_: (selected, 'engine'))
@stack.plugin.action(RuntimeToPluginAction.INGEST_DOCUMENT)
async def ingest(data):
entered.set()
await release.wait()
vectors.add('host-id')
finished.set()
return ActionResponse.success({'document_id': 'host-id', 'status': 'completed'})
@stack.plugin.action(RuntimeToPluginAction.DELETE_DOCUMENT)
async def delete(data):
vectors.discard(data['document_id'])
return ActionResponse.success({'success': True})
async def send_ingest():
with stack.core.installation_scope(BINDING):
return await stack.core.rag_ingest_document('tester', 'engine', {})
local = asyncio.create_task(send_ingest())
try:
await asyncio.wait_for(entered.wait(), 5)
if disconnect:
await stack.core.close()
result = await asyncio.gather(local, return_exceptions=True)
assert isinstance(result[0], Exception)
else:
local.cancel()
with pytest.raises(asyncio.CancelledError):
await local
assert not finished.is_set()
# The SDK has no ingest/delete barrier: a new caller can receive a
# confirmed deletion while the old plugin action can still write.
# Use the surviving Runtime->plugin connection after Host disconnect.
result = await stack.bridge.rag_delete_document('kb-a', 'host-id')
assert result == {'success': True}
assert vectors == set()
release.set()
await asyncio.wait_for(finished.wait(), 5)
assert vectors == {'host-id'}
finally:
release.set()
if not local.done():
local.cancel()
await asyncio.gather(local, return_exceptions=True)