Files
2026-08-21 16:00:13 +08:00

146 lines
4.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""资料导入 API 测试(api-spec §8,5a 结构解析)。"""
from collections.abc import AsyncGenerator
import pytest
from httpx import ASGITransport, AsyncClient
from app.main import app
@pytest.fixture
async def client(auth_headers: dict[str, str]) -> AsyncGenerator[AsyncClient, None]:
transport = ASGITransport(app=app)
async with AsyncClient(
transport=transport, base_url="http://test", headers=auth_headers
) as http_client:
yield http_client
@pytest.fixture
async def character_id(client: AsyncClient) -> str:
"""从认证用户获取活跃角色 ID。"""
resp = await client.get("/api/v1/characters/active")
assert resp.status_code == 200
return resp.json()["id"]
@pytest.mark.asyncio
async def test_create_import_text_parses_paragraphs(client: AsyncClient, character_id: str) -> None:
resp = await client.post(
f"/api/v1/characters/{character_id}/imports",
json={
"material_type": "background_story",
"format": "text",
"content": "第一段背景。\n\n第二段背景。",
"title": "背景资料",
},
)
assert resp.status_code == 200
job = resp.json()
assert job["id"].startswith("import_")
assert job["status"] == "parsed"
assert job["item_count"] == 2
assert job["material_type"] == "background_story"
items = await client.get(f"/api/v1/characters/{character_id}/imports/{job['id']}/items")
assert items.status_code == 200
body = items.json()
assert body["total"] == 2
assert body["items"][0]["item_type"] == "paragraph"
@pytest.mark.asyncio
async def test_create_import_chat_history_json(client: AsyncClient, character_id: str) -> None:
resp = await client.post(
f"/api/v1/characters/{character_id}/imports",
json={
"material_type": "chat_history",
"format": "json",
"content": '[{"role":"user","content":"你好"},{"role":"assistant","content":"你好呀"}]',
},
)
assert resp.status_code == 200
job = resp.json()
assert job["item_count"] == 2
items = (
await client.get(f"/api/v1/characters/{character_id}/imports/{job['id']}/items")
).json()["items"]
assert items[0]["item_type"] == "message"
assert items[0]["role"] == "user"
assert items[1]["role"] == "assistant"
@pytest.mark.asyncio
async def test_get_import_and_not_found(client: AsyncClient, character_id: str) -> None:
created = (
await client.post(
f"/api/v1/characters/{character_id}/imports",
json={"material_type": "diary", "content": "今天天气不错。"},
)
).json()
fetched = await client.get(f"/api/v1/characters/{character_id}/imports/{created['id']}")
assert fetched.status_code == 200
assert fetched.json()["id"] == created["id"]
missing = await client.get(f"/api/v1/characters/{character_id}/imports/import_missing")
assert missing.status_code == 404
@pytest.mark.asyncio
async def test_list_imports(client: AsyncClient, character_id: str) -> None:
await client.post(
f"/api/v1/characters/{character_id}/imports",
json={"material_type": "diary", "content": "条目一"},
)
await client.post(
f"/api/v1/characters/{character_id}/imports",
json={"material_type": "diary", "content": "条目二"},
)
resp = await client.get(f"/api/v1/characters/{character_id}/imports")
assert resp.status_code == 200
assert resp.json()["total"] == 2
@pytest.mark.asyncio
async def test_upload_text_file(client: AsyncClient, character_id: str) -> None:
resp = await client.post(
f"/api/v1/characters/{character_id}/imports/upload",
files={"file": ("note.txt", b"hello world\n\nsecond paragraph", "text/plain")},
data={"title": "笔记"},
)
assert resp.status_code == 200
job = resp.json()
assert job["source_type"] == "uploaded_file"
assert job["item_count"] == 2
assert job["title"] == "笔记"
@pytest.mark.asyncio
async def test_upload_audio_stores_metadata_only(client: AsyncClient, character_id: str) -> None:
resp = await client.post(
f"/api/v1/characters/{character_id}/imports/upload",
files={"file": ("sample.wav", b"fake audio bytes", "audio/wav")},
data={
"speaker_label": "人物本人",
"consent_status": "granted",
"usage_scope": "reference_only",
},
)
assert resp.status_code == 200
job = resp.json()
assert job["material_type"] == "voice_sample"
assert job["media_type"] == "audio"
assert job["status"] == "uploaded"
assert job["item_count"] == 0 # 媒体资料不切片
@pytest.mark.asyncio
async def test_create_import_rejects_empty_content(client: AsyncClient, character_id: str) -> None:
resp = await client.post(
f"/api/v1/characters/{character_id}/imports",
json={"material_type": "background_story", "content": ""},
)
assert resp.status_code == 422