ForcePilot/backend/test/test_chat_stream_attachment_materialize.py

120 lines
4.2 KiB
Python

from __future__ import annotations
import io
from pathlib import Path
import pytest
from yuxi.services import conversation_service as cs
class _DummyUpload:
def __init__(self, *, filename: str, content_type: str | None, data: bytes):
self.filename = filename
self.content_type = content_type
self._buffer = io.BytesIO(data)
async def read(self, size: int = -1) -> bytes:
return self._buffer.read(size)
async def seek(self, offset: int) -> int:
return self._buffer.seek(offset)
def test_build_attachment_storage_path_uses_thread_local_uploads_dir(tmp_path: Path, monkeypatch) -> None:
monkeypatch.setattr(cs.app_config, "save_dir", str(tmp_path))
virtual_path, host_path = cs._build_attachment_storage_path(
user_id="u-1",
thread_id="t-1",
file_name="demo.txt",
)
assert virtual_path == "/home/gem/user-data/uploads/attachments/demo.md"
assert host_path == tmp_path / "threads" / "t-1" / "user-data" / "uploads" / "attachments" / "demo.md"
def test_serialize_attachment_includes_original_file_fields() -> None:
serialized = cs.serialize_attachment(
{
"file_id": "f-1",
"file_name": "demo.txt",
"file_type": "text/plain",
"file_size": 5,
"status": "parsed",
"uploaded_at": "2026-03-25T00:00:00+00:00",
"path": "/home/gem/user-data/uploads/attachments/demo.md",
"artifact_url": "/api/chat/thread/t-1/artifacts/home/gem/user-data/uploads/attachments/demo.md",
"original_path": "/home/gem/user-data/uploads/demo.txt",
"original_artifact_url": "/api/chat/thread/t-1/artifacts/home/gem/user-data/uploads/demo.txt",
"minio_url": None,
}
)
assert serialized["path"] == "/home/gem/user-data/uploads/attachments/demo.md"
assert serialized["original_path"] == "/home/gem/user-data/uploads/demo.txt"
assert serialized["original_artifact_url"] == "/api/chat/thread/t-1/artifacts/home/gem/user-data/uploads/demo.txt"
@pytest.mark.asyncio
async def test_materialize_attachment_files_keeps_original_file_when_markdown_conversion_unsupported(
tmp_path: Path,
monkeypatch,
) -> None:
monkeypatch.setattr(cs.app_config, "save_dir", str(tmp_path))
upload = _DummyUpload(filename="demo.pdf", content_type="application/pdf", data=b"%PDF-test")
result = await cs._materialize_attachment_files(
thread_id="t-1",
upload=upload,
file_name="demo.pdf",
file_content=b"%PDF-test",
)
assert result["status"] == "uploaded"
assert result["path"] == "/home/gem/user-data/uploads/demo.pdf"
assert result["original_path"] == "/home/gem/user-data/uploads/demo.pdf"
assert (
tmp_path / "threads" / "t-1" / "user-data" / "uploads" / "demo.pdf"
).read_bytes() == b"%PDF-test"
@pytest.mark.asyncio
async def test_materialize_attachment_files_writes_markdown_copy_when_conversion_succeeds(
tmp_path: Path,
monkeypatch,
) -> None:
monkeypatch.setattr(cs.app_config, "save_dir", str(tmp_path))
async def _fake_convert(_upload):
return cs.ConversionResult(
file_id="f-1",
file_name="demo.txt",
file_type="text/plain",
file_size=5,
markdown="hello\nworld",
truncated=False,
)
monkeypatch.setattr(cs, "_convert_upload_to_markdown", _fake_convert)
upload = _DummyUpload(filename="demo.txt", content_type="text/plain", data=b"hello")
result = await cs._materialize_attachment_files(
thread_id="t-1",
upload=upload,
file_name="demo.txt",
file_content=b"hello",
)
assert result["status"] == "parsed"
assert result["path"] == "/home/gem/user-data/uploads/attachments/demo.md"
assert result["original_path"] == "/home/gem/user-data/uploads/demo.txt"
assert result["file_path"] == "/home/gem/user-data/uploads/attachments/demo.md"
assert result["markdown"] == "hello\nworld"
assert (tmp_path / "threads" / "t-1" / "user-data" / "uploads" / "demo.txt").read_bytes() == b"hello"
assert (
tmp_path / "threads" / "t-1" / "user-data" / "uploads" / "attachments" / "demo.md"
).read_text(encoding="utf-8") == "hello\nworld"