test: complete advisor full-stack acceptance record
This commit is contained in:
+32
-23
@@ -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 事件。(独立迁移库集成测试通过)
|
||||
|
||||
统一命令:
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user