"""角色卡导入 API 测试。""" from __future__ import annotations import base64 import json import struct import zlib import httpx def _make_png_text(keyword: str, text: str) -> bytes: def chunk(ctype: bytes, data: bytes) -> bytes: body = ctype + data return struct.pack(">I", len(data)) + body + struct.pack(">I", zlib.crc32(body) & 0xFFFFFFFF) payload = keyword.encode("latin-1") + b"\x00" + text.encode("latin-1") return b"\x89PNG\r\n\x1a\n" + chunk(b"tEXt", payload) + chunk(b"IEND", b"") async def test_import_json_card(auth_client: httpx.AsyncClient) -> None: card = {"spec_version": "2.0", "data": {"name": "API卡", "personality": "p"}} resp = await auth_client.post( "/api/v1/characters/import-card", files={"file": ("card.json", json.dumps(card).encode("utf-8"), "application/json")}, ) assert resp.status_code == 200 data = resp.json() assert data["name"] == "API卡" assert data["character_id"].startswith("character_") async def test_import_png_card(auth_client: httpx.AsyncClient) -> None: card = {"spec_version": "2.0", "data": {"name": "PNG卡"}} text = base64.b64encode(json.dumps(card).encode("utf-8")).decode("ascii") resp = await auth_client.post( "/api/v1/characters/import-card", files={"file": ("card.png", _make_png_text("chara", text), "image/png")}, ) assert resp.status_code == 200 assert resp.json()["name"] == "PNG卡" async def test_import_card_with_lorebook(auth_client: httpx.AsyncClient) -> None: card = { "spec_version": "2.0", "data": { "name": "带书", "character_book": {"entries": [{"keys": ["a"], "content": "b"}]}, }, } resp = await auth_client.post( "/api/v1/characters/import-card", files={"file": ("card.json", json.dumps(card).encode("utf-8"), "application/json")}, ) assert resp.status_code == 200 data = resp.json() assert data["lorebook_id"] is not None assert data["entry_count"] == 1 # 导入的人物可被列出 listed = await auth_client.get("/api/v1/characters") assert any(c["id"] == data["character_id"] for c in listed.json()["items"]) async def test_export_card_round_trip(auth_client: httpx.AsyncClient) -> None: # 导入一张带世谱的卡 card = { "spec_version": "2.0", "data": { "name": "往返", "nickname": "返返", "personality": "p", "character_book": {"entries": [{"keys": ["k"], "content": "v"}]}, }, } imp = await auth_client.post( "/api/v1/characters/import-card", files={"file": ("c.json", json.dumps(card).encode("utf-8"), "application/json")}, ) assert imp.status_code == 200 cid = imp.json()["character_id"] # 导出 exp = await auth_client.get(f"/api/v1/characters/{cid}/export-card") assert exp.status_code == 200 exported = exp.json() assert exported["spec"] == "chara_card_v3" assert exported["data"]["name"] == "往返" assert exported["data"]["nickname"] == "返返" assert exported["data"]["personality"] == "p" assert exported["data"]["character_book"]["entries"][0]["keys"] == ["k"] async def test_export_unknown_character_404(auth_client: httpx.AsyncClient) -> None: exp = await auth_client.get("/api/v1/characters/character_missing/export-card") assert exp.status_code == 404