tests/service-progress.test.ts
Rama main · réplica de lectura del repositorio original.
import assert from 'node:assert/strict';
import test from 'node:test';
import { GitHub } from '../src/adapters/github/client.js';
import { flushProgress, queueProgress } from '../src/application/service-progress.js';
import { serviceFixture } from './service-helpers.js';
test('remote comment recovery uses an authenticated-author marker and never edits spoofed feedback', async () => {
const marker = '<!-- repopilot-progress:' + 'a'.repeat(24) + ' -->', body = marker + '\nVerification completed';
const comments = [{ id: 1, body, user: { login: 'untrusted' } }];
let writes = 0, ambiguous = true;
const client = new GitHub('owner/repo', 'synthetic-token', { transport: async (input, options) => {
const url = String(input), method = options?.method ?? 'GET';
if (url === 'https://api.github.com/user') return Response.json({ login: 'maintainer' });
if (method === 'GET') return Response.json(comments);
if (method === 'POST') {
writes++; comments.push({ id: 2, body: JSON.parse(String(options?.body)).body, user: { login: 'maintainer' } });
if (ambiguous) { ambiguous = false; throw new TypeError('synthetic response loss'); }
return Response.json({ id: 2 });
}
if (method === 'PATCH') { writes++; comments[1]!.body = JSON.parse(String(options?.body)).body; return Response.json({ id: 2 }); }
assert.fail(method);
} });
await assert.rejects(client.syncProgress(7, marker, body));
assert.equal(await client.syncProgress(7, marker, body), 2); assert.equal(writes, 1);
await client.syncProgress(7, marker, body + '\nDraft PR ready');
assert.equal(writes, 2); assert.equal(comments[0]!.body, body);
});
test('progress outbox retries are bounded, resumable and independent from the model circuit', async () => {
const { d } = await serviceFixture({ service: { progress: true, queue: { maxAttempts: 1 } } });
await queueProgress(d.services, d.config.repository, 7, 'Awaiting approval');
let calls = 0;
const github = { syncProgress: async () => { calls++; throw new Error('synthetic outage'); } };
const flush = () => flushProgress(d.services, d.config.repository, github, d.config.service!, d.signal, () => {});
await flush(); await flush(); assert.equal(calls, 1);
assert.equal((await d.budget.status()).failures, 0);
await d.services.requestControl(d.config.repository, 'resume'); await flush(); assert.equal(calls, 2);
await flush(); assert.equal(calls, 2);
await queueProgress(d.services, d.config.repository, 7, 'Verification completed');
await flush(); assert.equal(calls, 3);
});
test('delivered and blocked comments do not starve later pending feedback batches', async () => {
const { d, feedback } = await serviceFixture({ service: { progress: true, queue: { maxBatch: 1 } } });
await queueProgress(d.services, d.config.repository, 7, 'Done');
const flush = () => flushProgress(d.services, d.config.repository, d.github, d.config.service!, d.signal, line => assert.fail(line));
await flush();
await queueProgress(d.services, d.config.repository, 8, 'Awaiting approval');
await flush(); assert.ok(feedback.has(8));
assert.equal((await d.services.progress(d.config.repository)).filter(item => item.digest !== item.deliveredDigest).length, 0);
});