100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
import asyncio
|
|
import io
|
|
from types import SimpleNamespace
|
|
|
|
import rio
|
|
import components
|
|
from n8n_client import WebhookError
|
|
|
|
|
|
def view():
|
|
return SimpleNamespace(collection_name='my-store', pending_files=(), upload_error='',
|
|
upload_status='', upload_progress='', is_uploading=False,
|
|
force_refresh=lambda: None)
|
|
|
|
|
|
def pick(state, name, data=b'# Notes'):
|
|
file = rio.FileInfo(name, len(data), 'text/markdown', io.BytesIO(data))
|
|
components.KnowledgeApp.pick_file(state, rio.FilePickEvent(file))
|
|
return file
|
|
|
|
|
|
def test_partial_failure_retries_only_failed_file_and_retains_bytes(monkeypatch):
|
|
state = view()
|
|
pick(state, 'first.md')
|
|
pick(state, 'second.md')
|
|
pick(state, 'third.md')
|
|
calls = []
|
|
|
|
async def upload(name, contents, collection):
|
|
calls.append(name)
|
|
assert contents == b'# Notes'
|
|
assert collection == 'my-store'
|
|
if name == 'second.md' and calls.count(name) == 1:
|
|
raise WebhookError('Временная ошибка')
|
|
|
|
monkeypatch.setattr(components, 'client', SimpleNamespace(upload=upload))
|
|
asyncio.run(components.KnowledgeApp.upload_file(state))
|
|
assert calls == ['first.md', 'second.md', 'third.md']
|
|
assert len(state.pending_files) == 1
|
|
assert state.pending_files[0].contents == b'# Notes'
|
|
assert 'second.md' in state.upload_error
|
|
assert '2 из 3' in state.upload_status
|
|
asyncio.run(components.KnowledgeApp.upload_file(state))
|
|
assert calls == ['first.md', 'second.md', 'third.md', 'second.md']
|
|
assert not state.pending_files
|
|
assert not state.upload_error
|
|
assert not state.is_uploading
|
|
|
|
|
|
def test_invalid_files_do_not_remove_valid_selection():
|
|
state = view()
|
|
good = pick(state, 'good.md')
|
|
pick(state, 'bad.txt')
|
|
pick(state, 'empty.md', b'')
|
|
pick(state, 'another.md')
|
|
assert len(state.pending_files) == 2
|
|
assert 'bad.txt' in state.upload_error and 'empty.md' in state.upload_error
|
|
components.KnowledgeApp.remove_file(state, rio.FilePickEvent(good))
|
|
assert len(state.pending_files) == 1
|
|
assert state.pending_files[0].file.name == 'another.md'
|
|
|
|
|
|
def test_busy_queue_is_immutable_and_duplicate_submission_is_ignored(monkeypatch):
|
|
state = view()
|
|
first = pick(state, 'first.md')
|
|
calls = []
|
|
|
|
async def upload(*args):
|
|
calls.append(args)
|
|
pick(state, 'late.md')
|
|
components.KnowledgeApp.remove_file(state, rio.FilePickEvent(first))
|
|
assert len(state.pending_files) == 1
|
|
await components.KnowledgeApp.upload_file(state)
|
|
|
|
monkeypatch.setattr(components, 'client', SimpleNamespace(upload=upload))
|
|
asyncio.run(components.KnowledgeApp.upload_file(state))
|
|
assert len(calls) == 1
|
|
assert not state.pending_files
|
|
|
|
|
|
def test_read_error_does_not_block_remaining_files(monkeypatch):
|
|
state = view()
|
|
broken = pick(state, 'broken.md')
|
|
state.pending_files[0].file = SimpleNamespace(name='broken.md', read_bytes=broken_read)
|
|
pick(state, 'good.md')
|
|
calls = []
|
|
|
|
async def upload(name, *_):
|
|
calls.append(name)
|
|
|
|
monkeypatch.setattr(components, 'client', SimpleNamespace(upload=upload))
|
|
asyncio.run(components.KnowledgeApp.upload_file(state))
|
|
assert calls == ['good.md']
|
|
assert not state.pending_files
|
|
assert 'Выберите его заново' in state.upload_error
|
|
|
|
|
|
async def broken_read():
|
|
raise OSError('closed stream')
|