import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; import test from 'node:test'; import { coordinate as reconcile, listRecentRuns, main, RUN_WINDOW_MS, sweepIssues, SWEEP_WINDOW_MS, } from '../dispatch-comment-builds.mjs'; import { parseBuild, readReceipt, receiptBody, resolveBuildTask, } from '../comment-queue.mjs'; const coordinate = (client, number) => reconcile(client, number, 0); const task = { targetBranch: 'apps/demo', taskType: '创建新系统', requirements: 'Original', acceptanceCriteria: 'Works', sampleData: '是', }; const bot = { login: 'github-actions[bot]', type: 'Bot' }; const owner = { login: 'gchust', type: 'User' }; const issue = { number: 2, state: 'open', user: owner, author_association: 'OWNER', body: '### 目标分支\napps/demo\n### 任务类型\n创建新系统\n### 业务需求\nOriginal\n### 验收要求\nWorks', }; const command = ( id, body = `/build\nFeature ${id}`, user = owner, association = 'OWNER', ) => ({ id, body, user, author_association: association, }); const run = (id, build = 0, status = 'completed', conclusion = 'success') => ({ id, display_title: `Factory issue #2 build ${build}`, status, conclusion, run_attempt: 1, }); function fixture({ comments = [], runs = [], jobs = [], pulls = [] } = {}) { const calls = []; const client = { repository: 'gchust/nb3-factory', getIssue: async () => issue, getRepository: async () => ({ default_branch: 'develop' }), getRef: async () => null, async addComment(number, body) { const value = { id: 1000 + comments.length, body, user: bot }; comments.push(value); return value; }, async request(method, route, options = {}) { calls.push({ method, route, ...options }); if (route === '/issues/2/comments') return [...comments]; if (method === 'PATCH' && route.startsWith('/issues/comments/')) { const comment = comments.find( (item) => item.id === Number(route.split('/').pop()), ); comment.body = options.body.body; return comment; } if (route === '/actions/workflows/code-agent-task.yml/runs') return { workflow_runs: runs }; if (route.endsWith('/jobs')) return { jobs }; if (route === '/pulls') return pulls.map((pull) => ({ base: { ref: 'apps/demo' }, ...pull })); if (route === '/dispatches') return null; if (route === '/issues') return [issue]; throw new Error(`Unexpected request ${method} ${route}`); }, }; return { client, comments, calls, receipts: () => comments.map(readReceipt).filter(Boolean), dispatches: () => calls.filter((call) => call.route === '/dispatches'), }; } test('only a standalone /build command with a prompt is accepted', () => { assert.equal(parseBuild('/build\r\n\r\nAdd orders'), 'Add orders'); for (const body of [ '/build', '/builder\ntext', 'please /build\ntext', '/build do this', ]) assert.equal(parseBuild(body), null); }); test('receipt JSON cannot break out of its marker and owner cannot spoof bot state', () => { const data = { id: 20, task, prompt: '-->\n/build\n