163 lines
6.6 KiB
Python
163 lines
6.6 KiB
Python
import os
|
|
import tempfile
|
|
import unittest
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
|
|
from app.api.task_briefs import (
|
|
GenerateTaskBriefRequest,
|
|
TemplateCreateRequest,
|
|
create_task_brief_template,
|
|
generate_task_brief,
|
|
get_task_brief_contract_sync,
|
|
list_task_brief_center,
|
|
list_task_brief_runs,
|
|
list_task_brief_templates,
|
|
)
|
|
from app.models import Base, Integration, User
|
|
from app.services.task_brief_service import DEFAULT_TEMPLATE_FALLBACK
|
|
|
|
|
|
class TaskBriefApiTest(unittest.IsolatedAsyncioTestCase):
|
|
async def asyncSetUp(self) -> None:
|
|
tmp = tempfile.NamedTemporaryFile(prefix="pmflow-task-brief-api-", suffix=".db", delete=False)
|
|
tmp.close()
|
|
self.db_path = tmp.name
|
|
self.engine = create_async_engine(f"sqlite+aiosqlite:///{self.db_path}", future=True)
|
|
self.session_factory = async_sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)
|
|
async with self.engine.begin() as conn:
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
|
|
async def asyncTearDown(self) -> None:
|
|
await self.engine.dispose()
|
|
if getattr(self, "db_path", None):
|
|
try:
|
|
os.remove(self.db_path)
|
|
except FileNotFoundError:
|
|
pass
|
|
|
|
async def test_template_generate_and_list(self) -> None:
|
|
async with self.session_factory() as db:
|
|
user = User(
|
|
tenant_id=1,
|
|
username="brief_admin",
|
|
email="brief_admin@example.com",
|
|
password_hash="hash",
|
|
name="模板管理员",
|
|
role="ADMIN",
|
|
status="active",
|
|
is_active=True,
|
|
)
|
|
db.add(user)
|
|
await db.commit()
|
|
await db.refresh(user)
|
|
|
|
created = await create_task_brief_template(
|
|
body=TemplateCreateRequest(
|
|
name="默认增量模板",
|
|
description="用于 planner 增量任务包",
|
|
content="# Template Body",
|
|
),
|
|
db=db,
|
|
current_user=user,
|
|
)
|
|
self.assertEqual(created["code"], 0)
|
|
template_id = str(created["data"]["id"])
|
|
|
|
templates = await list_task_brief_templates(db=db, current_user=user)
|
|
self.assertEqual(templates["code"], 0)
|
|
self.assertGreaterEqual(len(templates["data"]["items"]), 1)
|
|
|
|
generated = await generate_task_brief(
|
|
body=GenerateTaskBriefRequest(
|
|
title="Signal Planner Increment",
|
|
template_id=template_id,
|
|
changed_files=[
|
|
"backend/app/services/signal_planner_service.py",
|
|
"frontend/src/pages/ModelRoutingConfigPage.tsx",
|
|
],
|
|
scope_summary="后端 Planner + 前端配置页联动",
|
|
),
|
|
db=db,
|
|
current_user=user,
|
|
)
|
|
self.assertEqual(generated["code"], 0)
|
|
self.assertEqual(generated["data"]["run"]["status"], "completed")
|
|
|
|
center = await list_task_brief_center(db=db, current_user=user)
|
|
self.assertEqual(center["code"], 0)
|
|
self.assertGreaterEqual(len(center["data"]["items"]), 1)
|
|
|
|
runs = await list_task_brief_runs(db=db, current_user=user)
|
|
self.assertEqual(runs["code"], 0)
|
|
self.assertGreaterEqual(len(runs["data"]["items"]), 1)
|
|
|
|
sync_board = await get_task_brief_contract_sync(db=db, current_user=user)
|
|
self.assertEqual(sync_board["code"], 0)
|
|
self.assertIn("all_synced", sync_board["data"])
|
|
self.assertIsInstance(sync_board["data"].get("files"), list)
|
|
|
|
async def test_default_template_upgrade_and_placeholder_render(self) -> None:
|
|
async with self.session_factory() as db:
|
|
user = User(
|
|
tenant_id=1,
|
|
username="brief_admin_upgrade",
|
|
email="brief_admin_upgrade@example.com",
|
|
password_hash="hash",
|
|
name="模板管理员",
|
|
role="ADMIN",
|
|
status="active",
|
|
is_active=True,
|
|
)
|
|
db.add(user)
|
|
await db.commit()
|
|
await db.refresh(user)
|
|
|
|
db.add(
|
|
Integration(
|
|
tenant_id=1,
|
|
type="task_briefs",
|
|
is_active=True,
|
|
config={
|
|
"templates": [
|
|
{
|
|
"id": "tpl_default_signal_planner",
|
|
"name": "Signal Planner 默认模板",
|
|
"description": "系统初始化模板(可按需修改)",
|
|
"content": DEFAULT_TEMPLATE_FALLBACK,
|
|
"updated_at": "2026-01-01T00:00:00",
|
|
"updated_by": 0,
|
|
"created_at": "2026-01-01T00:00:00",
|
|
}
|
|
],
|
|
"runs": [],
|
|
"briefs": [],
|
|
},
|
|
)
|
|
)
|
|
await db.commit()
|
|
|
|
templates = await list_task_brief_templates(db=db, current_user=user)
|
|
self.assertEqual(templates["code"], 0)
|
|
default_tpl = next((item for item in templates["data"]["items"] if item.get("id") == "tpl_default_signal_planner"), None)
|
|
self.assertIsNotNone(default_tpl)
|
|
self.assertIn("## Verification", str(default_tpl["content"]))
|
|
self.assertNotEqual(str(default_tpl["content"]).strip(), DEFAULT_TEMPLATE_FALLBACK.strip())
|
|
|
|
generated = await generate_task_brief(
|
|
body=GenerateTaskBriefRequest(
|
|
title="Default Template Render",
|
|
changed_files=["backend/app/services/task_brief_service.py"],
|
|
scope_summary="验证默认模板占位符渲染",
|
|
),
|
|
db=db,
|
|
current_user=user,
|
|
)
|
|
self.assertEqual(generated["code"], 0)
|
|
content = str(generated["data"]["brief"]["content"] or "")
|
|
self.assertIn("backend/app/services/task_brief_service.py", content)
|
|
self.assertIn("验证默认模板占位符渲染", content)
|
|
self.assertNotIn("{{CHANGED_FILES}}", content)
|
|
self.assertNotIn("{{SCOPE_SUMMARY}}", content)
|
|
self.assertNotIn("{{GENERATED_AT}}", content)
|