mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-30 12:25:29 +08:00
108 lines
4.1 KiB
Python
108 lines
4.1 KiB
Python
"""Regression fixtures are local; no external model/parser/provider requests."""
|
|
import asyncio
|
|
import threading
|
|
|
|
import pytest
|
|
|
|
from test_shared_state import load_plugin, bind, StorageFixture, CONNECTORS
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_offload_slot_is_held_through_repeated_cancellation():
|
|
pc, _ = load_plugin('LangRAG')
|
|
plugin = bind(pc, StorageFixture())
|
|
assert hasattr(plugin, 'offload'), 'CPU parsing/chunking needs installation bounded offload'
|
|
entered = threading.Event()
|
|
release = threading.Event()
|
|
count = 0
|
|
lock = threading.Lock()
|
|
def job():
|
|
nonlocal count
|
|
with lock:
|
|
count += 1
|
|
if count == 2:
|
|
entered.set()
|
|
release.wait(2)
|
|
return 'done'
|
|
tasks = [asyncio.create_task(plugin.offload.run(job)) for _ in range(5)]
|
|
try:
|
|
for _ in range(100):
|
|
if entered.is_set():
|
|
break
|
|
await asyncio.sleep(.005)
|
|
assert entered.is_set()
|
|
tasks[0].cancel()
|
|
await asyncio.sleep(.01)
|
|
tasks[0].cancel()
|
|
await asyncio.sleep(.01)
|
|
assert count == 2, 'Cancelled waiters must not release running thread slots'
|
|
release.set()
|
|
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
assert isinstance(results[0], asyncio.CancelledError)
|
|
assert results[1:] == ['done'] * 4
|
|
finally:
|
|
release.set()
|
|
await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_parser_rejects_oversized_input_before_decode():
|
|
load_plugin('LangRAG')
|
|
from components.knowledge_engine.parser import FileParser
|
|
with pytest.raises(ValueError, match='limit'):
|
|
await FileParser().parse(b'x' * (16 * 1024 * 1024 + 1), 'doc.txt')
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize('name', CONNECTORS)
|
|
async def test_connector_storage_failure_is_not_missing_and_refuses_mutations(name):
|
|
pc, ec = load_plugin(name)
|
|
store = StorageFixture()
|
|
engine = ec()
|
|
engine.plugin = bind(pc, store)
|
|
store.fail = True
|
|
# Unreadable fence state is "unknown", never "unfenced": the mutation is
|
|
# refused with the Host failure instead of being dispatched.
|
|
with pytest.raises(RuntimeError, match='offline'):
|
|
await engine.delete_document('kb', 'doc')
|
|
with pytest.raises(RuntimeError, match='offline'):
|
|
await engine.on_knowledge_base_create('kb', {})
|
|
assert not store.data
|
|
# The unreadable state is not cached either: once Host storage answers, the
|
|
# same mutation is retried and proceeds rather than staying refused.
|
|
store.fail = False
|
|
await engine.on_knowledge_base_create('kb', {})
|
|
assert store.data
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telemetry_cancellation_clear_and_failure_fencing():
|
|
pc, _ = load_plugin('LangRAG')
|
|
store = StorageFixture()
|
|
plugin = bind(pc, store)
|
|
await plugin.initialize()
|
|
store.release.clear()
|
|
task = asyncio.create_task(plugin.telemetry.record_delete(collection_id='kb', document_id='old', status='completed', duration_ms=1))
|
|
await store.started.wait()
|
|
task.cancel()
|
|
clear = asyncio.create_task(plugin.telemetry.clear())
|
|
await asyncio.sleep(.01)
|
|
with pytest.raises(RuntimeError, match='fenced'):
|
|
await clear
|
|
store.release.set()
|
|
await asyncio.gather(task, return_exceptions=True)
|
|
other = bind(pc, store)
|
|
await other.initialize()
|
|
# A fresh reader may see the detached commit, but a failed clear cannot
|
|
# acknowledge deletion or introduce a later event.
|
|
assert all(event['document_id'] != 'new' for event in (await other.telemetry.snapshot())['recent']['delete'])
|
|
store.fail = True
|
|
with pytest.raises(RuntimeError, match='fenced'):
|
|
await plugin.telemetry.record_delete(collection_id='kb', document_id='new', status='completed', duration_ms=1)
|
|
with pytest.raises(RuntimeError, match='fenced'):
|
|
await plugin.telemetry.snapshot()
|
|
store.fail = False
|
|
with pytest.raises(RuntimeError, match='fenced'):
|
|
await plugin.telemetry.clear()
|
|
assert all(event['document_id'] != 'new' for event in (await other.telemetry.snapshot())['recent']['delete'])
|