Files
wehub-resource-sync 1443d3fdf9
Ruff Format Check / Ruff Format & Lint (push) Failing after 7m39s
Deploy VitePress site to Pages / build (push) Failing after 9m11s
Deploy VitePress site to Pages / Deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:32:26 +08:00

172 lines
5.8 KiB
Python

from __future__ import annotations
from types import SimpleNamespace
import pytest
from yuxi.knowledge.base import KnowledgeBase
from yuxi.services.file_preview import MAX_BINARY_PREVIEW_SIZE_BYTES
class FakeKnowledgeBase(KnowledgeBase):
@property
def kb_type(self) -> str:
return "fake"
async def _create_kb_instance(self, kb_id: str, config: dict):
return None
async def _initialize_kb_instance(self, instance) -> None:
pass
async def index_file(self, kb_id: str, file_id: str, operator_id: str | None = None) -> dict:
return {}
async def update_content(self, kb_id: str, file_ids: list[str], params: dict | None = None) -> list[dict]:
return []
async def aquery(self, query_text: str, kb_id: str, **kwargs) -> list[dict]:
return []
def get_query_params_config(self, kb_id: str, **kwargs) -> dict:
return {"options": []}
async def delete_file(self, kb_id: str, file_id: str) -> None:
pass
async def get_file_basic_info(self, kb_id: str, file_id: str) -> dict:
return {}
async def get_file_content(self, kb_id: str, file_id: str) -> dict:
return {}
async def get_file_info(self, kb_id: str, file_id: str) -> dict:
return {}
async def _save_metadata(self) -> None:
pass
class FakeMinioClient:
KB_BUCKETS = {"parsed": "knowledgebases"}
def __init__(self) -> None:
self.objects: dict[tuple[str, str], bytes] = {}
async def astat_file(self, bucket_name: str, object_name: str) -> int | None:
content = self.objects.get((bucket_name, object_name))
return len(content) if content is not None else None
async def adownload_file(self, bucket_name: str, object_name: str) -> bytes:
return self.objects[(bucket_name, object_name)]
async def aupload_file(
self,
bucket_name: str,
object_name: str,
data: bytes,
content_type: str | None = None,
) -> SimpleNamespace:
assert content_type == "application/pdf"
self.objects[(bucket_name, object_name)] = data
return SimpleNamespace(url=f"http://localhost:9000/{bucket_name}/{object_name}")
def make_kb(tmp_path) -> FakeKnowledgeBase:
kb = FakeKnowledgeBase(str(tmp_path))
kb.databases_meta["db1"] = {"metadata": {}}
kb.test_file_meta = {
"file_id": "file1",
"kb_id": "db1",
"filename": "demo.docx",
"path": "minio://knowledgebases/db1/upload/demo.docx",
"markdown_file": "minio://knowledgebases/db1/parsed/file1.md",
"status": "parsed",
}
async def load_file_meta(kb_id: str, file_id: str, *, refresh: bool = False) -> dict:
assert kb_id == "db1"
assert file_id == "file1"
return kb.test_file_meta
kb._load_file_meta = load_file_meta
return kb
def test_office_file_entry_exposes_logical_file_availability(tmp_path) -> None:
kb = make_kb(tmp_path)
entry = kb._knowledge_file_entry("db1", "file1", kb.test_file_meta)
assert entry["has_original_file"] is True
assert entry["has_parsed_markdown"] is True
assert "preview_modes" not in entry
assert "default_preview_mode" not in entry
@pytest.mark.asyncio
async def test_read_office_pdf_preview_converts_and_caches_pdf(tmp_path, monkeypatch) -> None:
kb = make_kb(tmp_path)
minio_client = FakeMinioClient()
minio_client.objects[("knowledgebases", "db1/upload/demo.docx")] = b"office"
minio_client.objects[("knowledgebases", "db1/parsed/file1.md")] = b"# parsed"
convert_calls = 0
async def fake_convert(filename: str, content: bytes) -> bytes:
nonlocal convert_calls
convert_calls += 1
assert filename == "demo.docx"
assert content == b"office"
return b"%PDF-1.4\nconverted"
monkeypatch.setattr("yuxi.storage.minio.get_minio_client", lambda: minio_client)
monkeypatch.setattr("yuxi.knowledge.base.convert_office_to_pdf", fake_convert)
response = await kb.read_file_preview("db1", "file1")
cached_response = await kb.read_file_preview("db1", "file1")
assert response["preview_type"] == "pdf"
assert response["supported"] is True
assert response["binary"] is True
assert response["content"] == b"%PDF-1.4\nconverted"
assert response["media_type"] == "application/pdf"
assert cached_response["content"] == b"%PDF-1.4\nconverted"
assert minio_client.objects[("knowledgebases", "db1/preview/file1.pdf")] == b"%PDF-1.4\nconverted"
assert convert_calls == 1
@pytest.mark.asyncio
async def test_non_docx_pptx_office_files_do_not_get_pdf_preview(tmp_path, monkeypatch) -> None:
kb = make_kb(tmp_path)
kb.test_file_meta["filename"] = "demo.xlsx"
minio_client = FakeMinioClient()
minio_client.objects[("knowledgebases", "db1/upload/demo.docx")] = b"PK\x03\x04excel"
monkeypatch.setattr("yuxi.storage.minio.get_minio_client", lambda: minio_client)
entry = kb._knowledge_file_entry("db1", "file1", kb.test_file_meta)
response = await kb.read_file_preview("db1", "file1")
assert entry["has_original_file"] is True
assert entry["has_parsed_markdown"] is True
assert response["preview_type"] == "unsupported"
assert response["supported"] is False
@pytest.mark.asyncio
async def test_read_file_preview_rejects_large_original_before_download(tmp_path, monkeypatch) -> None:
kb = make_kb(tmp_path)
kb.test_file_meta["filename"] = "large.pdf"
kb.test_file_meta["size"] = MAX_BINARY_PREVIEW_SIZE_BYTES + 1
async def fail_read(_path: str) -> bytes:
raise AssertionError("large preview should not download file content")
monkeypatch.setattr(kb, "_read_minio_bytes", fail_read)
response = await kb.read_file_preview("db1", "file1")
assert response["preview_type"] == "unsupported"
assert response["supported"] is False
assert response["limit"] == MAX_BINARY_PREVIEW_SIZE_BYTES