feat(webui): add guided setup flows

* feat(channels): add guided setup flows

* test(channels): preserve setup config values

* fix(channels): reflect saved setup state

* refactor(channels): simplify setup state metadata

* fix(channels): harden setup lifecycle

* refactor(channels): centralize setup contracts

* fix(channels): route setup actions through webui shim

* fix(channels): adapt settings for compact screens

* fix(models): preserve default preset display

* feat(models): add curated Codex catalog

* fix(webui): stop attached gateway on interrupt

* fix(webui): simplify apps catalog

* docs(webui): clarify apps and runtime features

* feat(settings): add guided capability setup

* fix(webui): harden setup and managed services

* test: keep managed runtime checks portable

* test: scope POSIX runtime coverage

* fix(webui): simplify file settings

* feat(files): bundle document reading

* fix(webui): harden setup request boundaries

* fix(webui): prevent channel setup status squeeze

* fix(settings): group provider compatibility aliases

* refactor(settings): remove redundant setup surfaces

* fix(webui): harden guided setup lifecycle

* fix(webui): preserve channel setup compatibility
This commit is contained in:
Xubin Ren
2026-07-13 13:11:46 +08:00
committed by GitHub
parent 791c7fd505
commit fe0717b385
92 changed files with 15058 additions and 1311 deletions
+75
View File
@@ -1,10 +1,15 @@
"""Tests for document text extraction utilities."""
from pathlib import Path
from zipfile import ZipFile
import pytest
from nanobot.utils.document import (
SUPPORTED_EXTENSIONS,
PdfSafetyError,
_is_text_extension,
extract_pdf_pages,
extract_text,
)
@@ -252,6 +257,76 @@ class TestExtractText:
assert result is not None
assert "Inside group" in result
def test_extract_text_rejects_oversized_office_archive(self, tmp_path, monkeypatch):
office_file = tmp_path / "oversized.docx"
with ZipFile(office_file, "w") as archive:
archive.writestr("word/document.xml", "x" * 32)
monkeypatch.setattr("nanobot.utils.document._MAX_OFFICE_UNCOMPRESSED_SIZE", 16)
assert "Office document expands beyond" in (extract_text(office_file) or "")
def test_extract_text_stops_streaming_xlsx_at_text_limit(self, tmp_path, monkeypatch):
from openpyxl import Workbook, load_workbook
xlsx_file = tmp_path / "large.xlsx"
wb = Workbook(write_only=True)
ws = wb.create_sheet()
for index in range(100):
ws.append([f"row-{index}-" + "x" * 20])
wb.save(xlsx_file)
visited = 0
real_load_workbook = load_workbook
def tracked_load_workbook(*args, **kwargs):
workbook = real_load_workbook(*args, **kwargs)
worksheet = workbook[workbook.sheetnames[0]]
original_iter_rows = worksheet.iter_rows
def tracked_rows(*row_args, **row_kwargs):
nonlocal visited
for row in original_iter_rows(*row_args, **row_kwargs):
visited += 1
yield row
worksheet.iter_rows = tracked_rows
return workbook
monkeypatch.setattr("openpyxl.load_workbook", tracked_load_workbook)
monkeypatch.setattr("nanobot.utils.document._MAX_TEXT_LENGTH", 80)
result = extract_text(xlsx_file)
assert result is not None
assert "truncated at 80 chars" in result
assert visited < 100
def test_extract_pdf_pages_rejects_large_content_stream(self, tmp_path, monkeypatch):
class _Contents:
@staticmethod
def get_data():
return b"x" * 17
class _Page:
@staticmethod
def get_contents():
return _Contents()
@staticmethod
def extract_text():
return "should not be reached"
class _Reader:
def __init__(self, *_args, **_kwargs):
self.pages = [_Page()]
monkeypatch.setattr("pypdf.PdfReader", _Reader)
monkeypatch.setattr("nanobot.utils.document._MAX_PDF_CONTENT_STREAM_SIZE", 16)
with pytest.raises(PdfSafetyError, match="content stream exceeds"):
extract_pdf_pages(tmp_path / "large.pdf")
def test_extract_text_pdf_not_found(self, tmp_path: Path):
"""Test that missing PDF files return error string."""
missing_pdf = tmp_path / "nonexistent.pdf"