181 lines
6.0 KiB
Python
181 lines
6.0 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
|
|
import pytest
|
|
|
|
from app.gateway import deps
|
|
from app.gateway.routers import artifacts
|
|
|
|
|
|
class _FakeThreadStore:
|
|
def __init__(self, rows: list[dict]) -> None:
|
|
self.rows = rows
|
|
self.calls: list[dict] = []
|
|
|
|
async def search(self, **kwargs) -> list[dict]:
|
|
self.calls.append(kwargs)
|
|
offset = int(kwargs.get("offset", 0))
|
|
limit = int(kwargs.get("limit", 100))
|
|
return self.rows[offset : offset + limit]
|
|
|
|
|
|
class _FakeAgentStore:
|
|
async def list_by_ids(self, agent_ids: list[str], user_id: str) -> list[dict]:
|
|
assert user_id == "owner"
|
|
agents = {
|
|
"analysis-agent": {
|
|
"id": "analysis-agent",
|
|
"name": "舆情分析智能体",
|
|
}
|
|
}
|
|
return [agents[agent_id] for agent_id in agent_ids if agent_id in agents]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_artifact_library_aggregates_generated_non_media_files(tmp_path: Path, monkeypatch):
|
|
thread_one = tmp_path / "thread-one" / "outputs"
|
|
thread_two = tmp_path / "thread-two" / "outputs"
|
|
thread_one.mkdir(parents=True)
|
|
thread_two.mkdir(parents=True)
|
|
|
|
report = thread_one / "研判报告.docx"
|
|
table = thread_one / "数据汇总.xlsx"
|
|
image = thread_one / "配图.png"
|
|
audio = thread_two / "录音.mp3"
|
|
notes = thread_two / "结论.md"
|
|
archive = thread_two / "证据.zip"
|
|
for path, content in (
|
|
(report, b"doc"),
|
|
(table, b"sheet"),
|
|
(image, b"image"),
|
|
(audio, b"audio"),
|
|
(notes, b"# notes"),
|
|
(archive, b"zip"),
|
|
):
|
|
path.write_bytes(content)
|
|
|
|
os.utime(report, (100, 100))
|
|
os.utime(table, (200, 200))
|
|
os.utime(notes, (300, 300))
|
|
os.utime(archive, (400, 400))
|
|
|
|
rows = [
|
|
{
|
|
"thread_id": "thread-one",
|
|
"display_name": "战场环境综合研判",
|
|
"metadata": {"agent_id": "analysis-agent"},
|
|
},
|
|
{
|
|
"thread_id": "thread-two",
|
|
"display_name": "生成发展报告",
|
|
"metadata": {},
|
|
},
|
|
]
|
|
store = _FakeThreadStore(rows)
|
|
|
|
class _Paths:
|
|
def sandbox_outputs_dir(self, thread_id: str, *, user_id: str) -> Path:
|
|
assert user_id == "owner"
|
|
return tmp_path / thread_id / "outputs"
|
|
|
|
async def _resolve_owner(_thread_id: str) -> str:
|
|
return "owner"
|
|
|
|
monkeypatch.setattr(artifacts, "get_paths", lambda: _Paths())
|
|
monkeypatch.setattr(artifacts, "aresolve_path_user_id", _resolve_owner)
|
|
|
|
items = await artifacts._scan_artifact_library(store)
|
|
|
|
assert [item["name"] for item in items] == ["证据.zip", "结论.md", "数据汇总.xlsx", "研判报告.docx"]
|
|
assert [item["file_kind"] for item in items] == ["archive", "text_data", "spreadsheet", "document"]
|
|
assert [item["source_type"] for item in items] == ["general_qa", "general_qa", "agent", "agent"]
|
|
assert items[-1]["thread_title"] == "战场环境综合研判"
|
|
assert items[-1]["agent_id"] == "analysis-agent"
|
|
assert all(item["name"] not in {"配图.png", "录音.mp3"} for item in items)
|
|
assert store.calls[0]["exclude_system"] is True
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_artifact_library_endpoint_filters_searches_and_pages(tmp_path: Path, monkeypatch):
|
|
output_dir = tmp_path / "thread-one" / "outputs"
|
|
agent_output_dir = tmp_path / "thread-agent" / "outputs"
|
|
output_dir.mkdir(parents=True)
|
|
agent_output_dir.mkdir(parents=True)
|
|
(output_dir / "任务报告.md").write_text("report", encoding="utf-8")
|
|
(output_dir / "任务数据.csv").write_text("a,b", encoding="utf-8")
|
|
(agent_output_dir / "智能体结论.md").write_text("agent report", encoding="utf-8")
|
|
|
|
store = _FakeThreadStore(
|
|
[
|
|
{
|
|
"thread_id": "thread-one",
|
|
"display_name": "任务研判对话",
|
|
"metadata": {},
|
|
},
|
|
{
|
|
"thread_id": "thread-agent",
|
|
"display_name": "智能体研判对话",
|
|
"metadata": {"agent_id": "analysis-agent"},
|
|
},
|
|
]
|
|
)
|
|
|
|
class _Paths:
|
|
def sandbox_outputs_dir(self, thread_id: str, *, user_id: str) -> Path:
|
|
return tmp_path / thread_id / "outputs"
|
|
|
|
async def _resolve_owner(_thread_id: str) -> str:
|
|
return "owner"
|
|
|
|
monkeypatch.setattr(artifacts, "get_paths", lambda: _Paths())
|
|
monkeypatch.setattr(artifacts, "aresolve_path_user_id", _resolve_owner)
|
|
monkeypatch.setattr(artifacts, "get_effective_user_id", lambda: "owner")
|
|
monkeypatch.setattr(deps, "get_thread_store", lambda _request: store)
|
|
monkeypatch.setattr(deps, "get_agent_store", lambda _request: _FakeAgentStore())
|
|
artifacts._artifact_library_cache.clear()
|
|
|
|
raw_endpoint = artifacts.list_artifact_library.__wrapped__
|
|
response = await raw_endpoint(
|
|
request=SimpleNamespace(),
|
|
query="研判对话",
|
|
file_kind="text_data",
|
|
source_type="general_qa",
|
|
offset=0,
|
|
limit=1,
|
|
refresh=True,
|
|
)
|
|
|
|
assert response.total == 1
|
|
assert response.offset == 0
|
|
assert response.limit == 1
|
|
assert [item.name for item in response.items] == ["任务报告.md"]
|
|
|
|
spreadsheet_response = await raw_endpoint(
|
|
request=SimpleNamespace(),
|
|
query="任务数据",
|
|
file_kind="spreadsheet",
|
|
source_type="all",
|
|
offset=0,
|
|
limit=50,
|
|
refresh=False,
|
|
)
|
|
assert spreadsheet_response.total == 1
|
|
assert spreadsheet_response.items[0].name == "任务数据.csv"
|
|
|
|
agent_response = await raw_endpoint(
|
|
request=SimpleNamespace(),
|
|
query=None,
|
|
file_kind="all",
|
|
source_type="agent",
|
|
offset=0,
|
|
limit=50,
|
|
refresh=False,
|
|
)
|
|
assert agent_response.total == 1
|
|
assert agent_response.items[0].source_type == "agent"
|
|
assert agent_response.items[0].agent_name == "舆情分析智能体"
|
|
assert agent_response.items[0].name == "智能体结论.md"
|