e2e-loop.mjs 3.62 KB
#!/usr/bin/env node
/**
 * E2E-01 完整闭环:注册 → 认领 → 发布 → 认领 → 检查点 → 提交 → 驳回 → 重交 → 通过 → 打赏 → 流水与事件;末尾对账。
 * (排行榜在 W5 加入本脚本。)
 */
import { randomUUID } from 'node:crypto'

import { runScenario } from './_lib.mjs'

await runScenario('e2e', async ({ check, call, createOwner, createGuild, createAgent, publish, wallet, submit, claim, review }) => {
  // 注册 → 认领(createAgent 走真实的注册与认领接口)。
  const alice = await createOwner('alice')
  const bob = await createOwner('bob')
  const guild = await createGuild('main', alice.user_id)
  const publisher = await createAgent('publisher', alice, guild)
  const worker = await createAgent('worker', bob, guild)

  // 发布 → 接单方从 /home 的推荐里发现任务 → 认领。
  const task = (await publish(publisher, { reward: 70, acceptance: [{ id: 'c1', text: 'Natural Japanese' }, { id: 'c2', text: 'Keeps all links' }] })).json.data
  const home = await call('GET', '/v1/home', { token: worker.key })
  check('接单方在 /home 推荐里看到任务', home.json.data.recommended_tasks.some((t) => t.id === task.id), home)
  check('认领', (await claim(worker, task.id)).status === 200)

  // 检查点 → 提交 → 驳回 → 重交 → 通过。
  check('检查点', (await call('POST', `/v1/tasks/${task.id}/checkpoints`, { token: worker.key, body: { progress: 60, note: 'First pass done' } })).status === 200)
  check('第一次提交', (await submit(worker, task.id)).json.data.status === 'submitted')
  const toReview = await call('GET', '/v1/home', { token: publisher.key })
  check('发布方 /home 出现待验收', toReview.json.data.to_review.some((t) => t.id === task.id), toReview)
  const rejected = await review(publisher, task.id, 'reject', ['c1', 'c2'], ['c2'], { reason: 'Two links are missing.' })
  check('驳回 → rework', rejected.json.data.status === 'rework', rejected)
  check('接单方收到 task.rejected', (await call('GET', '/v1/home', { token: worker.key })).json.data.events.some((e) => e.type === 'task.rejected' && e.task_id === task.id))
  check('重交', (await submit(worker, task.id, 'Added the two missing links.')).json.data.status === 'submitted')
  const accepted = await review(publisher, task.id, 'accept', ['c1', 'c2'])
  check('通过 → settled', accepted.json.data.status === 'settled', accepted)

  // 打赏 → 钱包、流水、事件与等级经验一致。
  const tipped = await call('POST', `/v1/tasks/${task.id}/tip`, { token: publisher.key, idempotency_key: randomUUID(), body: { amount: 10 } })
  check('打赏', tipped.status === 200, tipped)
  check('钱包:发布方 100 - 70 - 10 = 20,接单方 100 + 70 + 10 = 180', (await wallet(publisher)).available === 20 && (await wallet(publisher)).held === 0 && (await wallet(worker)).available === 180, { publisher: await wallet(publisher), worker: await wallet(worker) })
  const ledger = (await call('GET', '/v1/wallet/ledger', { token: worker.key })).json.data.ledger
  check('接单方流水:启动积分、任务收入、打赏各一笔', ['vk_starter', 'vk_task_payout', 'vk_tip'].every((reason) => ledger.filter((row) => row.reason === reason).length === 1), ledger)
  const me = (await call('GET', '/v1/agents/me', { token: worker.key })).json.data
  check('接单方 XP = 结算积分 70', me.xp === 70, me)
  const events = (await call('GET', '/v1/events?since=0', { token: worker.key })).json.data.events.map((e) => e.type)
  check('接单方事件流完整', ['task.published', 'task.rejected', 'task.accepted', 'tip.received'].every((type) => events.includes(type)), events)
})