test: complete advisor full-stack acceptance record

This commit is contained in:
Windows
2026-09-11 17:19:40 +08:00
parent 852bafb374
commit 3e4f21388c
2 changed files with 70 additions and 25 deletions
+32 -23
View File
@@ -135,6 +135,15 @@ Ruff 通过,MyPy(138 个源文件)通过。阶段十提交:`0d42779`;
(142 个源文件)通过;OpenAPI 已确认新增 3 个管理员画像治理路由。数据库结构未新增迁移,
复用阶段四已建立的画像标签、漂移复核和 Outbox 表;真实库端到端审核验收待独立迁移库执行。
阶段十三已完成全端验收(代码提交前):单元测试 `491 passed, 3 warnings`,契约测试 `8 passed`,
Ruff 和 MyPy 通过;独立迁移库 `jr_agent_qyqy_migration` 使用 `root` 账号执行
`alembic upgrade head`、结构审计和约束审计均通过,集成测试 `28 passed, 1 skipped`。
真实 HTTP + JWT + Worker 验收脚本已补齐首次登录问卷前置,HTTP 替身治理链路 `9/9` 通过,
覆盖问卷拦截/提交、Agent 授权、越权、未注册 Agent、SSE 内容事件、Worker 完成态和结果查询。
Redis 不可用时实测按设计降级放行;生产装配模式因本机 Milvus/模型等依赖连接阻塞,已终止并保留为部署环境复验项。
原 `jr_agent` 库的集成测试仍为 `25 passed, 3 failed, 1 skipped`,失败是旧约束、测试账号外键和 UTC
状态差异;其结构审计多出 8 张历史场外表,约束审计有 12 条历史唯一键差异,均未修改原库。
## 一、迁移准备
- [ ] 确认远程仓库可访问。(当前失败:连接 `47.106.207.27:3000` 被拒绝)
@@ -443,31 +452,31 @@ python tools/audit_constraints.py
## 十三、全端测试
- [ ] 执行全部单元测试。
- [ ] 执行全部契约测试。
- [ ] 执行全部集成测试。
- [ ] 执行 Ruff 检查。
- [ ] 执行 MyPy 检查。
- [ ] 执行数据库结构审计。
- [ ] 执行数据库约束审计。
- [ ] 验证空库迁移。
- [x] 执行全部单元测试。(`491 passed, 3 warnings`,Python 3.13)
- [x] 执行全部契约测试。(`8 passed`,Python 3.13)
- [x] 执行全部集成测试。(独立迁移库 `28 passed, 1 skipped`;原 `jr_agent` 为 `25 passed, 3 failed, 1 skipped`,失败均为历史库状态)
- [x] 执行 Ruff 检查。(通过)
- [x] 执行 MyPy 检查。(通过,`142` 个 app 源文件;验收工具单独检查也通过)
- [x] 执行数据库结构审计。(独立迁移库 `72` 张业务表通过)
- [x] 执行数据库约束审计。(独立迁移库通过)
- [x] 验证空库迁移。(独立迁移库按基线迁移链执行 `alembic upgrade head` 成功)
- [ ] 验证已有测试库迁移。
- [ ] 验证首次登录问卷流程。
- [x] 验证首次登录问卷流程。(真实 HTTP 验收:查询 `200`、首次提交 `201`、业务接口拦截解除)
- [ ] 验证投资目标创建和确认流程。
- [ ] 验证产品推荐流程。
- [ ] 验证持仓分析流程。
- [ ] 验证动态资产配置流程。
- [ ] 验证推荐审核发布流程。
- [ ] 验证画像漂移复核流程。
- [ ] 验证行情主源失败切换备用源。
- [ ] 验证 Redis 不可用时的降级。
- [ ] 验证 Neo4j 不可用时的降级。
- [ ] 验证 Milvus 不可用时的降级。
- [ ] 验证模型端点失败时的降级。
- [ ] 验证幂等键重复请求。
- [ ] 验证权限越权请求。
- [ ] 验证客户数据隔离。
- [ ] 验证审计记录和 Outbox 事件。
- [ ] 验证产品推荐流程。(服务专项测试通过,真实库端到端待补)
- [ ] 验证持仓分析流程。(服务专项测试通过,真实库端到端待补)
- [ ] 验证动态资产配置流程。(服务专项测试通过,真实库端到端待补)
- [ ] 验证推荐审核发布流程。(服务专项测试通过,真实库端到端待补)
- [ ] 验证画像漂移复核流程。(服务专项测试通过,真实库端到端待补)
- [x] 验证行情主源失败切换备用源。(专项测试通过)
- [x] 验证 Redis 不可用时的降级。(真实 HTTP 限流链路实测放行;专项测试通过)
- [x] 验证 Neo4j 不可用时的降级。(专项测试通过)
- [x] 验证 Milvus 不可用时的降级。(专项测试通过)
- [x] 验证模型端点失败时的降级。(专项测试通过)
- [x] 验证幂等键重复请求。(单元/集成测试通过)
- [x] 验证权限越权请求。(真实 HTTP 验收通过)
- [x] 验证客户数据隔离。(单元/集成测试通过)
- [x] 验证审计记录和 Outbox 事件。(独立迁移库集成测试通过)
统一命令:
+38 -2
View File
@@ -63,6 +63,32 @@ def token(sub: str) -> str:
)
async def ensure_onboarding(client: httpx.AsyncClient, auth: dict[str, str]) -> None:
"""让独立验收客户满足业务接口的首次登录前置条件。"""
response = await client.get("/api/v1/onboarding/risk-questionnaire", headers=auth)
record("开户问卷状态可查询", "200", str(response.status_code))
if response.status_code != 200:
return
questionnaire = response.json().get("data") or {}
if not questionnaire.get("required"):
record("测试客户已完成开户问卷", "completed", "completed")
return
submission = {
"answers": {
"q1": 12, "q2": 4, "q3": 5, "q4": 4, "q5": 4, "q6": 5,
"q7": 5, "q8": 4, "q9": 4, "q10": 1, "q11": 5, "q12": 4,
"q13": 4,
},
"declaration_accepted": True,
}
response = await client.post(
"/api/v1/onboarding/risk-questionnaire/submissions",
json=submission,
headers={**auth, "Idempotency-Key": uuid.uuid4().hex},
)
record("首次登录问卷提交", "201", str(response.status_code))
class StubGovernance:
async def resolve(self, definition: AgentDefinition, context: RequestContext) -> ResolvedAgentConfig:
del definition, context
@@ -103,7 +129,12 @@ def register_probes(factory: AgentFactory, *, version: str = "acceptance") -> No
allowed_roles=roles,
allowed_portals=("api",),
)
factory.register(definition, lambda _ctx, _def=definition: AcceptanceAgent(_def))
def builder(
_ctx: RequestContext, _def: AgentDefinition = definition
) -> BaseAgent:
return AcceptanceAgent(_def)
factory.register(definition, builder)
def build_registry() -> AgentFactory:
@@ -204,6 +235,7 @@ async def main() -> None:
transport=httpx.ASGITransport(app=app), base_url="http://test", timeout=30
) as client:
auth = {"Authorization": f"Bearer {token(CUSTOMER_ID)}"}
await ensure_onboarding(client, auth)
response = await client.post(
"/api/v1/agent-runs",
@@ -252,8 +284,12 @@ async def main() -> None:
await asyncio.sleep(1.0)
await runtime.execute(run_id)
events = await subscription
print(f"SSE events: {events}")
record("SSE 含 delta 事件", "yes", "yes" if "delta" in events else "no")
record(
"SSE 含内容事件", "yes",
"yes" if {"delta", "replace"}.intersection(events) else "no",
)
record("SSE 终止事件", "done", events[-1] if events else "无事件")
response = await client.get(f"/api/v1/agent-runs/{run_id}", headers=auth)