import test from 'node:test';
import assert from 'node:assert/strict';
import { mkdtemp, mkdir, readFile, readdir, writeFile, rm, realpath, cp, symlink, rename, access } from 'node:fs/promises';
import path from 'node:path';
import { tmpdir } from 'node:os';
import { createRequire } from 'node:module';
import * as streaming from './chat-catalog-testops-streaming.mjs';
const { parseDocument } = createRequire(import.meta.url)('yaml');
import { fullCatalogCases, catalogTargetSha } from './chat-full-catalog-testops-manifest.mjs';
import { hash, model } from './chat-completion-testops-contract.mjs';
import { run } from './chat-completion-testops-cli.mjs';
import { prepareCaseBundle, readCaseBundle, publishCaseBundle, publicationSet } from './chat-catalog-testops-streaming.mjs';
const context = { catalog: 'full', runId: '123', attempt: '1', workflowSha: 'a'.repeat(40), targetSha: catalogTargetSha };
const caseIds = { 'tasks.create-backlog': 388, 'documents-artifacts.write-plan': 26, 'projects.project-defaults': 100 };
const cases = fullCatalogCases.filter(row => Object.hasOwn(caseIds, row.key)).map(row => ({ ...row, caseId: caseIds[row.key],
registeredFullName: row.aliases[0] ?? row.fullName }));
const selection = { schema: 'paperclip.chat-completion-testops-selection/v1', ...context, projectId: 1,
planId: 102, jobId: 100, launchId: 12, allureJobRunId: '77', cases };
const digest = hash(JSON.stringify(selection)), current = cases.find(row => row.caseId === 388), runUrl = 'https://github.com/paperclipai/paperclip/actions/runs/123';
async function fixture(t, failed = false, owned = current, runtimeContext = context, runtimeSelection = selection) {
const base = await realpath(await mkdtemp(path.join(tmpdir(), 'chat-stream-test-')));
t.after(() => rm(base, { recursive: true, force: true }));
const root = path.join(base, 'projection'), output = path.join(base, 'allure'); await mkdir(root);
const original = { executionId: owned.executionId, suiteId: 'chat-features', caseId: owned.scenario,
profileId: 'runner-opencode', provider: 'opencode', environmentId: 'local', model, runtimeMode: 'native', attempt: 1,
source: { sha: catalogTargetSha, workflowRunUrl: runUrl }, status: failed ? 'failed' : 'passed', cleanup: failed ? 'failed' : 'passed',
runIds: failed ? [] : Array.from({ length: owned.minimumRuns }, (_, i) => `synthetic-owned-run-${i}`), matcherResults: failed ? [] : Array.from({ length: owned.checks ?? 2 }, (_, i) => ({
matcher: { kind: 'json_path', path: `owned.check${i}`, expected: true }, passed: true })), screenshots: [] };
const files = new Map([['normalized-results.json', Buffer.from(JSON.stringify({ schema: 'paperclip.runner-e2e.campaign/v2',
campaignId: 'gha-123-1', retries: 0, results: [original] }))], ['junit.xml', Buffer.from('')]]);
const binding = { schema: 'paperclip.chat-completion-testops-export/v1', ...context, campaignId: 'gha-123-1',
executionIds: [owned.executionId], files: [...files].map(([name, bytes]) => ({ path: name, bytes: bytes.length, sha256: hash(bytes) })),
privateEvidence: [{ executionId: owned.executionId, requiredEvidenceComplete: !failed,
finalScreenshot: failed ? null : { sha256: 'e'.repeat(64), bytes: 24 } }] };
for (const [name, bytes] of files) await writeFile(path.join(root, name), bytes);
await writeFile(path.join(root, 'binding.json'), JSON.stringify(binding));
const prepared = await prepareCaseBundle(root, output, runtimeContext, runtimeSelection, owned.executionId, hash(JSON.stringify(runtimeSelection)));
return { root, output, prepared };
}
function server(prepared, { existing = false, uncertain = false, apply = true, scheduled = false, processingReads = 0 } = {}) {
let present = existing, posts = 0, pendingReads = processingReads; const calls = [];
const resultId = scheduled ? 3347 : 501;
const detail = () => {
const pending = !present || pendingReads-- > 0;
return { id: resultId, testCaseId: 388, projectId: 1, launchId: 12,
fullName: current.registeredFullName, jobRun: { id: 77, launchId: 12, url: runUrl }, links: [],
...(!pending ? { status: prepared.exported[0].status } : {}) };
};
const api = async (route, method = 'GET', body) => {
calls.push({ route, method });
if (method === 'POST') {
posts++; assert.equal(route, '/api/jobrun/77/upload');
assert.deepEqual([...body.keys()], ['file','file','file']);
const result = [...body.values()].find(value => value.name.endsWith('-result.json'));
const parsed = JSON.parse(await result.text());
assert.equal(parsed.status, prepared.exported[0].status); assert.equal(parsed.labels[0].value, '388');
if (apply) present = true;
if (uncertain) throw new Error('SYNTHETIC_PRIVATE_RESPONSE_MUST_NOT_ESCAPE');
return { filesCount: 3 };
}
if (route === '/api/jobrun/77') return { id: 77, job: { id: 100, type: 'github' }, stage: 'finished', launchId: 12, url: runUrl };
if (route === '/api/launch/12') return { id: 12, projectId: 1, closed: false };
if (route.startsWith('/api/testresult?')) return { content: scheduled ? cases.map(row => ({
id: row.caseId === 388 ? resultId : row.caseId === 100 ? 3346 : 3345, testCaseId: row.caseId }))
: [...(present ? [{ id: resultId, testCaseId: 388 }] : []), { id: 502, testCaseId: 26 }],
totalElements: scheduled ? 3 : present ? 2 : 1, totalPages: 1, number: 0 };
if (route === `/api/testresult/${resultId}`) return detail();
if (route.startsWith('/api/testresult/attachment?')) return { content: prepared.exported[0].attachments.map((row, index) => ({
...row, id: 700 + index, missed: false, fromTestCase: false })), totalElements: 2, totalPages: 1 };
throw new Error(`Unexpected test route ${route}`);
};
return { api, calls, get posts() { return posts; } };
}
const immediate = { now: () => 0, wait: async () => {} };
async function publish(f, s, mark = async () => {}, options = immediate) {
return publishCaseBundle(s.api, f.root, f.output, f.prepared, context, selection, current.executionId, digest, mark, options);
}
test('one frozen singleton bundle publishes into the bound shared launch beside siblings', async t => {
const f = await fixture(t), s = server(f.prepared); let markers = 0;
const names = await readdir(f.output), value = await publish(f, s, async () => { markers++; });
assert.equal(value.verification, 'VERIFIED'); assert.equal(markers, 1); assert.equal(s.posts, 1);
assert.equal(value.jobRunStageBefore, 'finished'); assert.equal(value.jobRunStageAfter, 'finished');
assert.deepEqual(await readdir(f.output), names);
await assert.rejects(prepareCaseBundle(f.root, f.output, context, selection, current.executionId, digest), /allure_output/);
});
test('an existing result is verified without another mutation or a regenerated UUID', async t => {
const f = await fixture(t), s = server(f.prepared, { existing: true });
const value = await publish(f, s, async () => assert.fail('must not mark another upload'));
assert.equal(value.uploadSent, false); assert.equal(s.posts, 0); assert.equal(value.verification, 'VERIFIED');
});
test('an existing unknown outcome remains readback-only and never requests another upload', async t => {
const f = await fixture(t), s = server(f.prepared, { existing: true }), original = s.api;
s.api = async (...args) => { const row = await original(...args); if (args[0] === '/api/testresult/501') row.status = 'unknown'; return row; };
await assert.rejects(publish(f, s, async () => assert.fail('must not mark another upload')), /upload_unverified/);
assert.equal(s.posts, 0);
});
test('three scheduled launch rows permit one initial upload and remain unready until an outcome is processed', async t => {
const f = await fixture(t), s = server(f.prepared, { scheduled: true, processingReads: 1 });
let markers = 0, waits = 0;
const value = await publish(f, s, async () => { markers++; }, { now: () => 0, wait: async () => { waits++; } });
assert.equal(value.verification, 'VERIFIED'); assert.equal(value.uploadSent, true);
assert.equal(value.exported.status, 'passed'); assert.equal(value.results[0].resultId, 3347);
assert.equal(s.posts, 1); assert.equal(markers, 1); assert.equal(waits, 1);
});
test('permanently scheduled readback stops at the original poll bound without another upload', async t => {
const f = await fixture(t), s = server(f.prepared, { scheduled: true, processingReads: Infinity });
let waits = 0;
await assert.rejects(publish(f, s, async () => {}, { now: () => 0, wait: async () => { waits++; } }), /upload_unverified/);
assert.equal(s.posts, 1); assert.equal(waits, 11);
});
test('scheduled details with a foreign jobrun, contradictory source or malformed status refuse before upload', async t => {
for (const change of [row => { row.jobRun.id = 78; }, row => { row.jobRun.url = runUrl + '0'; },
row => { row.status = null; }]) {
const f = await fixture(t), s = server(f.prepared, { scheduled: true }), original = s.api;
s.api = async (...args) => { const row = await original(...args); if (args[0] === '/api/testresult/3347') change(row); return row; };
await assert.rejects(publish(f, s, async () => assert.fail('must not mark an invalid result')), /result_mismatch/);
assert.equal(s.posts, 0);
}
});
test('an uncertain applied upload reconciles by GET while an absent upload stops without resend', async t => {
for (const apply of [true, false]) {
const f = await fixture(t), s = server(f.prepared, { uncertain: true, apply, scheduled: true });
if (apply) {
const value = await publish(f, s); assert.equal(value.verification, 'VERIFIED');
assert.equal(value.uncertainUpload.kind, 'transport_or_response_failure');
assert.ok(!JSON.stringify(value).includes('SYNTHETIC_PRIVATE'));
} else await assert.rejects(publish(f, s), error => error.message === 'chat_completion_testops_stream_upload_uncertain'
&& !JSON.stringify(error).includes('SYNTHETIC_PRIVATE'));
assert.equal(s.posts, 1);
}
});
test('failed originals remain failed, including zero-run setup or cleanup failure', async t => {
const f = await fixture(t, true), s = server(f.prepared), value = await publish(f, s);
assert.equal(value.exported.status, 'failed'); assert.equal(value.exported.cleanup, 'failed');
assert.equal(value.verification, 'VERIFIED');
});
test('changed bundle bytes, source, selection digest and foreign case refuse before transport', async t => {
const f = await fixture(t); let calls = 0;
const noApi = async () => { calls++; assert.fail('untrusted bundle must not reach API'); };
await assert.rejects(publishCaseBundle(noApi, f.root, f.output, f.prepared, { ...context, workflowSha: 'b'.repeat(40) }, selection,
current.executionId, digest, async () => {}), /selection_binding/);
await assert.rejects(readCaseBundle(f.root, f.output, f.prepared, context, selection, current.executionId, 'b'.repeat(64)), /stream_binding/);
await assert.rejects(readCaseBundle(f.root, f.output, f.prepared, context, selection, 'foreign', digest), /stream_scope/);
await writeFile(path.join(f.output, f.prepared.files[0].name), 'SYNTHETIC_PRIVATE_TAMPER');
await assert.rejects(publishCaseBundle(noApi, f.root, f.output, f.prepared, context, selection, current.executionId, digest, async () => {}), /stream_bundle/);
assert.equal(calls, 0);
});
test('final coverage authenticates existing per-case bytes and keeps mixed original grades without re-export', async t => {
const base = await realpath(await mkdtemp(path.join(tmpdir(), 'chat-stream-final-')));
t.after(() => rm(base, { recursive: true, force: true }));
const bundles = path.join(base, 'bundles'), publications = path.join(base, 'publications');
await mkdir(bundles); await mkdir(publications);
for (const [index, row] of cases.entries()) {
const f = await fixture(t, index === 1, row);
const bundle = path.join(bundles, `chat-catalog-case-bundle-123-1-${index}`);
const receipt = path.join(publications, `chat-catalog-case-publication-123-1-${index}`);
await mkdir(bundle); await mkdir(receipt);
await cp(f.root, path.join(bundle, 'projection'), { recursive: true });
await cp(f.output, path.join(bundle, 'allure'), { recursive: true });
await writeFile(path.join(bundle, 'prepared.json'), JSON.stringify(f.prepared));
await writeFile(path.join(receipt, 'publication.json'), JSON.stringify({ ...f.prepared,
schema: 'paperclip.chat-catalog-testops-case-publication/v1', exported: f.prepared.exported[0],
verification: 'VERIFIED', results: [{ executionId: row.executionId, caseId: row.caseId, attachmentsVerified: true }] }));
}
const rows = await publicationSet(bundles, publications, context, selection, digest);
assert.deepEqual(rows.map(row => row.status), ['passed','failed','passed']);
const receiptPath = path.join(publications, 'chat-catalog-case-publication-123-1-1', 'publication.json');
const bad = JSON.parse(await readFile(receiptPath)); bad.exported.status = 'passed';
await writeFile(receiptPath, JSON.stringify(bad));
await assert.rejects(publicationSet(bundles, publications, context, selection, digest), /stream_publications/);
await rm(path.dirname(receiptPath), { recursive: true });
await assert.rejects(publicationSet(bundles, publications, context, selection, digest), /stream_publications/);
});
test('the real CLI prepares without TestOps credentials and retains closed failed or uncertain publication witnesses', async t => {
for (const uncertain of [false, true]) {
const f = await fixture(t, true), base = path.dirname(f.root);
await rm(f.output, { recursive: true });
const selectionRoot = path.join(base, 'chat-completion-testops-selection'); await mkdir(selectionRoot);
await writeFile(path.join(selectionRoot, 'testops-pilot-selection.json'), JSON.stringify(selection));
const event = { repository: { full_name: 'paperclipai/paperclip', private: false, default_branch: 'master' },
ref: 'refs/heads/master', sender: { id: 42 }, inputs: { operation: 'evaluate', ALLURE_JOB_RUN_ID: '77' } };
const eventPath = path.join(base, 'event.json'); await writeFile(eventPath, JSON.stringify(event));
const env = { GITHUB_ACTIONS: 'true', GITHUB_EVENT_NAME: 'workflow_dispatch', GITHUB_REPOSITORY: event.repository.full_name,
GITHUB_REF: event.ref, GITHUB_WORKFLOW_REF: 'paperclipai/paperclip/.github/workflows/chat-catalog-testops.yml@refs/heads/master',
GITHUB_SHA: context.workflowSha, GITHUB_RUN_ATTEMPT: '1', GITHUB_RUN_ID: '123', GITHUB_ACTOR_ID: '42',
TESTOPS_ALLOWED_ACTOR_IDS: '[42]', GITHUB_RUN_NUMBER: '1', GITHUB_EVENT_PATH: eventPath,
GH_TOKEN: 'SYNTHETIC_GITHUB_SECRET', RUNNER_TEMP: base, GITHUB_WORKSPACE: base, ALLURE_JOB_RUN_ID: '77',
ALLURE_RESULTS: f.output, CHAT_TESTOPS_EXECUTION_ID: current.executionId, EXPECTED_SELECTION_SHA256: digest };
const exec = async () => ({ stdout: JSON.stringify({ id: 123, run_attempt: 1, run_number: 1, workflow_id: 99,
head_sha: context.workflowSha, head_branch: 'master', event: 'workflow_dispatch', path: '.github/workflows/chat-catalog-testops.yml',
repository: event.repository, actor: { id: 42 }, triggering_actor: { id: 42 } }) });
assert.equal(await run('prepare-case-upload', f.root, env, { exec, fetchImpl: () => assert.fail('reader must not authenticate to TestOps') }), 0);
const preparedPath = path.join(base, 'chat-completion-testops-prepare-case-upload', 'prepared.json');
const prepared = JSON.parse(await readFile(preparedPath)), s = server(prepared, { uncertain, apply: !uncertain });
const fetchImpl = async (url, options) => {
if (url.endsWith('/api/uaa/oauth/token')) return Response.json({ access_token: 'SYNTHETIC_ACCESS_TOKEN' });
if (uncertain && s.posts && new URL(url).pathname === '/api/jobrun/77') return Response.json({ id: 78 });
return Response.json(await s.api(new URL(url).pathname + new URL(url).search, options.method, options.body));
};
let captured = ''; const capture = t.mock.method(process.stderr, 'write', value => { captured += value; return true; });
assert.equal(await run('upload-case', f.root, { ...env, ALLURE_TOKEN: 'SYNTHETIC_TESTOPS_SECRET',
CHAT_TESTOPS_PREPARED_FILE: preparedPath }, { exec, fetchImpl }), 1);
capture.mock.restore();
const state = path.join(base, 'chat-completion-testops-upload-case');
assert.equal(s.posts, 1);
const receipt = JSON.parse(await readFile(path.join(state, uncertain ? 'failure.json' : 'publication.json')));
if (uncertain) {
assert.equal(receipt.code, 'chat_completion_testops_stream_upload_uncertain');
assert.equal(receipt.uploadFailure.kind, 'transport_or_response_failure');
await assert.rejects(readFile(path.join(state, 'publication.json')), { code: 'ENOENT' });
} else { assert.equal(receipt.verification, 'VERIFIED'); assert.equal(receipt.exported.status, 'failed'); }
assert.deepEqual(JSON.parse(await readFile(path.join(state, 'once-upload_case.json'))),
{ stage: 'upload_case', status: 'STARTED_NO_AUTOMATIC_RESEND' });
assert.doesNotMatch(captured + JSON.stringify(receipt), /SYNTHETIC_|ACCESS_TOKEN/);
const yaml = await readFile(new URL('../../.github/workflows/chat-catalog-testops-case.yml', import.meta.url), 'utf8');
for (const filename of ['publication.json', 'failure.json', 'once-upload_case.json']) {
assert.ok(yaml.includes(`/chat-completion-testops-upload-case/${filename}`));
}
}
});
test('source host admission follows only the fixed same-commit callee with exact named caller secret references', async () => {
const { parse } = await import('yaml');
const { paidWorker } = await import('./chat-completion-testops-host-control.mjs');
const main = parse(await readFile(new URL('../../.github/workflows/chat-catalog-testops.yml', import.meta.url), 'utf8'));
const callee = parse(await readFile(new URL('../../.github/workflows/chat-catalog-testops-case.yml', import.meta.url), 'utf8'));
assert.equal(paidWorker(main, callee).environment, 'runner-e2e-private-paid');
for (const change of [
value => { value.jobs.chat.uses = 'foreign/repository/workflow.yml@master'; },
value => { value.jobs.chat.secrets = 'inherit'; },
value => { value.jobs.chat_external.secrets.ALLURE_TOKEN = ''; },
value => { value.jobs.chat.with.selection_sha256 = 'arbitrary'; },
]) { const mutant = structuredClone(main); change(mutant); assert.throws(() => paidWorker(mutant, callee), /boundary/); }
const wrong = structuredClone(callee); wrong.jobs.chat.environment = 'runner-e2e-testops-reporting';
assert.throws(() => paidWorker(main, wrong), /boundary/);
});
test('the final CLI verifies all authenticated mixed-grade publications without re-export or upload and refuses a contradictory merged grade', async t => {
for (const contradiction of [false, true]) {
const base = await realpath(await mkdtemp(path.join(tmpdir(), 'chat-stream-cli-final-')));
t.after(() => rm(base, { recursive: true, force: true }));
const bundles = path.join(base, 'bundles'), publications = path.join(base, 'publications'), merged = path.join(base, 'merged');
for (const root of [bundles, publications, merged]) await mkdir(root);
const actual = { ...context, operation: 'evaluate', actorId: 42, triggeringActorId: 42, workflowId: 99 };
const originals = [], privateEvidence = [], exported = [];
for (const [index, row] of cases.entries()) {
const f = await fixture(t, index === 1, row, actual);
const bundle = path.join(bundles, `chat-catalog-case-bundle-123-1-${index}`);
const publication = path.join(publications, `chat-catalog-case-publication-123-1-${index}`);
await mkdir(bundle); await mkdir(publication);
await cp(f.root, path.join(bundle, 'projection'), { recursive: true });
await cp(f.output, path.join(bundle, 'allure'), { recursive: true });
await writeFile(path.join(bundle, 'prepared.json'), JSON.stringify(f.prepared));
await writeFile(path.join(publication, 'publication.json'), JSON.stringify({ ...f.prepared,
schema: 'paperclip.chat-catalog-testops-case-publication/v1', exported: f.prepared.exported[0], verification: 'VERIFIED',
results: [{ executionId: row.executionId, caseId: row.caseId, attachmentsVerified: true }] }));
originals.push(JSON.parse(await readFile(path.join(f.root, 'normalized-results.json'))).results[0]);
privateEvidence.push(...JSON.parse(await readFile(path.join(f.root, 'binding.json'))).privateEvidence);
exported.push(f.prepared.exported[0]);
}
if (contradiction) Object.assign(originals[0], { status: 'failed', cleanup: 'failed', runIds: [], matcherResults: [] });
const files = new Map([['normalized-results.json', Buffer.from(JSON.stringify({ schema: 'paperclip.runner-e2e.campaign/v2',
campaignId: 'gha-123-1', retries: 0, results: originals }))], ['junit.xml', Buffer.from('')]]);
for (const [name, bytes] of files) await writeFile(path.join(merged, name), bytes);
await writeFile(path.join(merged, 'binding.json'), JSON.stringify({ schema: 'paperclip.chat-completion-testops-export/v1',
...context, campaignId: 'gha-123-1', executionIds: cases.map(row => row.executionId), privateEvidence,
files: [...files].map(([name, bytes]) => ({ path: name, bytes: bytes.length, sha256: hash(bytes) })) }));
const selectionRoot = path.join(base, 'chat-completion-testops-selection'); await mkdir(selectionRoot);
await writeFile(path.join(selectionRoot, 'testops-pilot-selection.json'), JSON.stringify(selection));
const event = { repository: { full_name: 'paperclipai/paperclip', private: false, default_branch: 'master' },
ref: 'refs/heads/master', sender: { id: 42 }, inputs: { operation: 'evaluate', ALLURE_JOB_RUN_ID: '77' } };
const eventPath = path.join(base, 'event.json'); await writeFile(eventPath, JSON.stringify(event));
const env = { GITHUB_ACTIONS: 'true', GITHUB_EVENT_NAME: 'workflow_dispatch', GITHUB_REPOSITORY: event.repository.full_name,
GITHUB_REF: event.ref, GITHUB_WORKFLOW_REF: 'paperclipai/paperclip/.github/workflows/chat-catalog-testops.yml@refs/heads/master',
GITHUB_SHA: context.workflowSha, GITHUB_RUN_ATTEMPT: '1', GITHUB_RUN_ID: '123', GITHUB_ACTOR_ID: '42',
TESTOPS_ALLOWED_ACTOR_IDS: '[42]', GITHUB_RUN_NUMBER: '1', GITHUB_EVENT_PATH: eventPath,
GH_TOKEN: 'SYNTHETIC_GITHUB_SECRET', RUNNER_TEMP: base, GITHUB_WORKSPACE: base, ALLURE_JOB_RUN_ID: '77',
ALLURE_TOKEN: 'SYNTHETIC_REPORTING_SECRET', EXPECTED_SELECTION_SHA256: digest,
CHAT_TESTOPS_BUNDLES: bundles, CHAT_TESTOPS_PUBLICATIONS: publications, ALLURE_RESULTS: path.join(base, 'must-not-export') };
const exec = async () => ({ stdout: JSON.stringify({ id: 123, run_attempt: 1, run_number: 1, workflow_id: 99,
head_sha: context.workflowSha, head_branch: 'master', event: 'workflow_dispatch', path: '.github/workflows/chat-catalog-testops.yml',
repository: event.repository, actor: { id: 42 }, triggering_actor: { id: 42 } }) });
const details = [];
const fetchImpl = async (url, options) => {
const u = new URL(url), route = u.pathname + u.search;
if (u.pathname === '/api/uaa/oauth/token') return Response.json({ access_token: 'SYNTHETIC_ACCESS_TOKEN' });
assert.equal(options.method, 'GET', 'final verification must not upload or change TestOps');
if (route === '/api/jobrun/77') return Response.json({ id: 77, job: { id: 100, type: 'github' }, stage: 'finished', launchId: 12, url: runUrl });
if (route === '/api/launch/12') return Response.json({ id: 12, projectId: 1 });
if (u.pathname === '/api/testresult') return Response.json({ content: cases.map((row, i) => ({ id: 501 + i, testCaseId: row.caseId })), totalElements: 3, totalPages: 1, number: 0 });
const match = u.pathname.match(/^\/api\/testresult\/(\d+)$/);
if (match) {
const i = Number(match[1]) - 501; details.push(cases[i].caseId);
return Response.json({ id: Number(match[1]), projectId: 1, launchId: 12, testCaseId: cases[i].caseId, fullName: cases[i].registeredFullName,
jobRun: { id: 77, launchId: 12, url: runUrl }, links: [], status: exported[i].status });
}
assert.equal(u.pathname, '/api/testresult/attachment');
const i = Number(u.searchParams.get('testResultId')) - 501;
return Response.json({ content: exported[i].attachments.map((row, index) => ({ ...row, id: 700 + index, missed: false, fromTestCase: false })), totalElements: 2, totalPages: 1 });
};
const before = await Promise.all((await readdir(bundles)).map(async name => [name, hash(await readFile(path.join(bundles, name, 'prepared.json')))]));
let stderr = ''; const capture = t.mock.method(process.stderr, 'write', value => { stderr += value; return true; });
assert.equal(await run('verify-stream', merged, env, { exec, fetchImpl }), 1);
capture.mock.restore();
const state = path.join(base, 'chat-completion-testops-verify-stream');
if (contradiction) {
assert.equal(JSON.parse(await readFile(path.join(state, 'failure.json'))).code, 'chat_completion_testops_upload_receipt');
assert.deepEqual(details, []);
} else {
const verified = JSON.parse(await readFile(path.join(state, 'verification.json')));
assert.equal(verified.verification, 'VERIFIED'); assert.deepEqual(details.sort((a,b) => a-b), [26,100,388]);
assert.deepEqual(verified.results.map(row => row.status), ['passed','failed','passed']);
}
await assert.rejects(readFile(env.ALLURE_RESULTS), { code: 'ENOENT' });
assert.deepEqual(await Promise.all((await readdir(bundles)).map(async name => [name, hash(await readFile(path.join(bundles, name, 'prepared.json')))])), before);
assert.doesNotMatch(stderr, /SYNTHETIC_/);
}
});
// Exact download-path decision from pinned download-artifact 3e5f45b, lines185–194.
// Even merge-multiple:false flattens a one-artifact pattern result.
const pinnedDownloadPath = (resolvedPath, artifact, artifacts, isSingleArtifactDownload, mergeMultiple = false) =>
isSingleArtifactDownload || mergeMultiple || artifacts.length === 1 ? resolvedPath : path.join(resolvedPath, artifact.name);
async function downloaded(t, selected, available = selected.cases.length, runtimeContext = context) {
const base = await realpath(await mkdtemp(path.join(tmpdir(), 'chat-stream-download-')));
t.after(() => rm(base, { recursive: true, force: true }));
const workflow = parseDocument(await readFile(new URL('../../.github/workflows/chat-catalog-testops.yml', import.meta.url), 'utf8')).toJS();
const roots = { bundle: path.join(base, 'catalog-bundles'), publication: path.join(base, 'catalog-publications') };
const active = step => !step.if || (step.if.includes('!= null') ? selected.cases.length > 1 : selected.cases.length === 1);
for (const role of ['bundle', 'publication']) {
const steps = workflow.jobs.verify.steps.filter(step => step.uses?.startsWith('actions/download-artifact@')
&& (step.with.pattern ?? step.with.name ?? '').startsWith(`chat-catalog-case-${role}-`) && active(step));
assert.equal(steps.length, 1);
const step = steps[0], artifacts = Array.from({ length: available }, (_, i) => ({ name: `chat-catalog-case-${role}-123-1-${i}` }));
await mkdir(roots[role]);
for (const [index, artifact] of artifacts.entries()) {
const destination = pinnedDownloadPath(path.join(base, step.with.path.replaceAll('${{ github.run_id }}', '123')),
artifact, artifacts, Boolean(step.with.name));
await mkdir(destination, { recursive: true });
const row = selected.cases[index];
if (role === 'bundle') {
const f = await fixture(t, index === 1, row, runtimeContext, selected);
await cp(f.root, path.join(destination, 'projection'), { recursive: true });
await cp(f.output, path.join(destination, 'allure'), { recursive: true });
await writeFile(path.join(destination, 'prepared.json'), JSON.stringify(f.prepared));
} else {
// Use the matching bundle's frozen UUID bytes; never independently export the receipt.
const bundlePath = artifacts.length === 1 && selected.cases.length > 1 ? roots.bundle
: path.join(roots.bundle, `chat-catalog-case-bundle-123-1-${index}`);
let prepared;
try { prepared = JSON.parse(await readFile(path.join(bundlePath, 'prepared.json'))); }
catch { prepared = JSON.parse(await readFile(path.join(roots.bundle, 'prepared.json'))); }
await writeFile(path.join(destination, 'publication.json'), JSON.stringify({ ...prepared,
schema: 'paperclip.chat-catalog-testops-case-publication/v1', exported: prepared.exported[0], verification: 'VERIFIED',
results: [{ executionId: row.executionId, caseId: row.caseId, attachmentsVerified: true }] }));
// The unchanged real upload-case producer retains this marker on a fresh POST.
await writeFile(path.join(destination, 'once-upload_case.json'), JSON.stringify({ stage: 'upload_case', status: 'STARTED_NO_AUTOMATIC_RESEND' }));
}
}
}
return { base, workflow, bundles: roots.bundle, publications: roots.publication, digest: hash(JSON.stringify(selected)) };
}
test('actual pinned singleton download reaches safe gather', async t => {
const selected = { ...selection, cases: [current] }, f = await downloaded(t, selected);
const output = path.join(f.base, 'catalog-projections');
await streaming.gatherCaseProjections(f.bundles, output, context, selected, f.digest);
assert.deepEqual(await readdir(output), ['chat-catalog-case-bundle-123-1-0']);
});
test('actual pinned singleton download reaches final publication validation', async t => {
const selected = { ...selection, cases: [current] }, f = await downloaded(t, selected);
const exported = await publicationSet(f.bundles, f.publications, context, selected, f.digest);
assert.deepEqual(exported.map(row => row.status), ['passed']);
});
test('actual pinned multi download preserves all mixed grades and missing singleton among a multi selection still refuses', async t => {
const f = await downloaded(t, selection);
assert.deepEqual((await publicationSet(f.bundles, f.publications, context, selection, f.digest)).map(row => row.status), ['passed','failed','passed']);
const incomplete = await downloaded(t, selection, 1);
await assert.rejects(publicationSet(incomplete.bundles, incomplete.publications, context, selection, incomplete.digest));
});
test('named layouts reject empty, mixed, foreign, malformed and symlink entries before gathering', async t => {
const selected = { ...selection, cases: [current] };
const changes = [
async f => rm(path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0'), { recursive: true }),
async f => writeFile(path.join(f.bundles, 'prepared.json'), '{}'),
async f => rename(path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0'), path.join(f.bundles, 'chat-catalog-case-bundle-999-1-0')),
async f => writeFile(path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0', 'unknown.json'), '{}'),
async f => {
const directory = path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0');
await rename(directory, path.join(f.base, 'outside'));
await symlink(path.join(f.base, 'outside'), directory);
},
async f => {
const file = path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0', 'projection', 'normalized-results.json');
await rename(file, path.join(f.base, 'outside.json')); await symlink(path.join(f.base, 'outside.json'), file);
},
async f => {
const file = path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0', 'prepared.json');
const prepared = JSON.parse(await readFile(file)); prepared.targetSha = 'f'.repeat(40);
await writeFile(file, JSON.stringify(prepared));
},
async f => {
const file = path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0', 'allure');
await rename(file, path.join(f.base, 'outside')); await symlink(path.join(f.base, 'outside'), file);
},
];
for (const change of changes) {
const f = await downloaded(t, selected); await change(f);
const output = path.join(f.base, 'must-not-gather');
await assert.rejects(streaming.gatherCaseProjections(f.bundles, output, context, selected, f.digest));
await assert.rejects(access(output));
await assert.rejects(publicationSet(f.bundles, f.publications, context, selected, f.digest));
}
for (const change of [
async f => writeFile(path.join(f.publications, 'chat-catalog-case-publication-123-1-0', 'failure.json'), JSON.stringify({ command: 'upload-case', code: 'chat_completion_testops_upload_unverified' })),
async f => writeFile(path.join(f.publications, 'chat-catalog-case-publication-123-1-0', 'once-upload_case.json'), JSON.stringify({ stage: 'upload_case', status: 'COMPLETE' })),
async f => {
const file = path.join(f.publications, 'chat-catalog-case-publication-123-1-0', 'once-upload_case.json');
await rename(file, path.join(f.base, 'marker')); await symlink(path.join(f.base, 'marker'), file);
},
async f => writeFile(path.join(f.publications, 'extra'), '{}'),
async f => writeFile(path.join(f.publications, 'chat-catalog-case-publication-123-1-0', 'extra'), '{}'),
async f => {
const file = path.join(f.publications, 'chat-catalog-case-publication-123-1-0', 'publication.json');
await rename(file, path.join(f.base, 'outside')); await symlink(path.join(f.base, 'outside'), file);
},
]) {
const f = await downloaded(t, selected); await change(f);
await assert.rejects(publicationSet(f.bundles, f.publications, context, selected, f.digest));
}
});
test('the owning gather CLI authenticates selection and copies only unchanged projections without TestOps access', async t => {
const selected = { ...selection, cases: [current] };
const actual = { ...context, operation: 'evaluate', actorId: 42, triggeringActorId: 42, workflowId: 99 };
const f = await downloaded(t, selected, 1, actual), selectionRoot = path.join(f.base, 'chat-completion-testops-selection');
await mkdir(selectionRoot); await writeFile(path.join(selectionRoot, 'testops-pilot-selection.json'), JSON.stringify(selected));
const event = { repository: { full_name: 'paperclipai/paperclip', private: false, default_branch: 'master' },
ref: 'refs/heads/master', sender: { id: 42 }, inputs: { operation: 'evaluate', ALLURE_JOB_RUN_ID: '77' } };
const eventPath = path.join(f.base, 'event.json'); await writeFile(eventPath, JSON.stringify(event));
const env = { GITHUB_ACTIONS: 'true', GITHUB_EVENT_NAME: 'workflow_dispatch', GITHUB_REPOSITORY: event.repository.full_name,
GITHUB_REF: event.ref, GITHUB_WORKFLOW_REF: 'paperclipai/paperclip/.github/workflows/chat-catalog-testops.yml@refs/heads/master',
GITHUB_SHA: context.workflowSha, GITHUB_RUN_ATTEMPT: '1', GITHUB_RUN_ID: '123', GITHUB_ACTOR_ID: '42',
TESTOPS_ALLOWED_ACTOR_IDS: '[42]', GITHUB_RUN_NUMBER: '1', GITHUB_EVENT_PATH: eventPath,
GH_TOKEN: 'SYNTHETIC_GITHUB_SECRET', RUNNER_TEMP: f.base, GITHUB_WORKSPACE: f.base, ALLURE_JOB_RUN_ID: '77',
EXPECTED_SELECTION_SHA256: f.digest, CHAT_TESTOPS_BUNDLES: f.bundles };
const exec = async () => ({ stdout: JSON.stringify({ id: 123, run_attempt: 1, run_number: 1, workflow_id: 99,
head_sha: context.workflowSha, head_branch: 'master', event: 'workflow_dispatch', path: '.github/workflows/chat-catalog-testops.yml',
repository: event.repository, actor: { id: 42 }, triggering_actor: { id: 42 } }) });
const output = path.join(f.base, 'projections');
assert.equal(await run('gather-case-projections', output, { ...env, GITHUB_RUN_ATTEMPT: '2' }, { exec,
fetchImpl: async () => assert.fail('unsupported retry must not contact TestOps') }), 1);
await assert.rejects(access(output));
assert.equal(await run('gather-case-projections', output, env, { exec, fetchImpl: async () => assert.fail('metadata gather must not contact TestOps') }), 0);
assert.deepEqual(await readdir(output), ['chat-catalog-case-bundle-123-1-0']);
const copied = path.join(output, 'chat-catalog-case-bundle-123-1-0');
assert.deepEqual((await readdir(copied)).sort(), ['binding.json', 'junit.xml', 'normalized-results.json']);
for (const name of await readdir(copied)) assert.deepEqual(await readFile(path.join(copied, name)),
await readFile(path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0', 'projection', name)));
const gather = f.workflow.jobs.aggregate.steps.find(step => step.name?.startsWith('Gather only'));
assert.match(gather.run, /chat-completion-testops-cli.mjs gather-case-projections catalog-projections/);
for (const job of ['aggregate', 'verify']) {
const downloads = f.workflow.jobs[job].steps.filter(step => step.with?.name?.startsWith('chat-catalog-case-bundle-'));
assert.equal(downloads.length, 1); assert.equal(downloads[0].with.name, 'chat-catalog-case-bundle-${{ github.run_id }}-1-0');
assert.equal(downloads[0].with.path, 'catalog-bundles/chat-catalog-case-bundle-${{ github.run_id }}-1-0');
}
});
test('singleton destination uses trusted run naming; the admitted producer remains first-attempt-only', async () => {
const workflow = parseDocument(await readFile(new URL('../../.github/workflows/chat-catalog-testops.yml', import.meta.url), 'utf8')).toJS();
const callee = parseDocument(await readFile(new URL('../../.github/workflows/chat-catalog-testops-case.yml', import.meta.url), 'utf8')).toJS();
for (const role of ['bundle', 'publication']) {
const step = workflow.jobs.verify.steps.find(step => step.with?.name?.startsWith(`chat-catalog-case-${role}-`));
const name = step.with.name.replaceAll('${{ github.run_id }}', '987654321');
const destination = step.with.path.replaceAll('${{ github.run_id }}', '987654321');
assert.equal(path.basename(destination), name);
assert.equal(pinnedDownloadPath(destination, { name }, [{ name }], true), destination);
const job = role === 'bundle' ? callee.jobs.report : callee.jobs.upload;
const producer = job.steps.find(step => step.with?.name?.startsWith(`chat-catalog-case-${role}-`));
assert.equal(producer.with.name.replaceAll('${{ github.run_id }}', '987654321').replaceAll('${{ fromJSON(inputs.cell).index }}', '0'), name);
if (role === 'publication') {
assert.match(producer.with.path, /once-upload_case.json/);
assert.match(producer.with.path, /failure.json/);
}
}
});
test('the unchanged upload CLI real publication and once files validate after a named singleton download', async t => {
const selected = { ...selection, cases: [current] };
const actual = { ...context, operation: 'evaluate', actorId: 42, triggeringActorId: 42, workflowId: 99 };
const f = await downloaded(t, selected, 1, actual), bundle = path.join(f.bundles, 'chat-catalog-case-bundle-123-1-0');
const preparedFile = path.join(bundle, 'prepared.json'), prepared = JSON.parse(await readFile(preparedFile));
const selectionRoot = path.join(f.base, 'chat-completion-testops-selection');
await mkdir(selectionRoot); await writeFile(path.join(selectionRoot, 'testops-pilot-selection.json'), JSON.stringify(selected));
const event = { repository: { full_name: 'paperclipai/paperclip', private: false, default_branch: 'master' },
ref: 'refs/heads/master', sender: { id: 42 }, inputs: { operation: 'evaluate', ALLURE_JOB_RUN_ID: '77' } };
const eventPath = path.join(f.base, 'event.json'); await writeFile(eventPath, JSON.stringify(event));
const env = { GITHUB_ACTIONS: 'true', GITHUB_EVENT_NAME: 'workflow_dispatch', GITHUB_REPOSITORY: event.repository.full_name,
GITHUB_REF: event.ref, GITHUB_WORKFLOW_REF: 'paperclipai/paperclip/.github/workflows/chat-catalog-testops.yml@refs/heads/master',
GITHUB_SHA: context.workflowSha, GITHUB_RUN_ATTEMPT: '1', GITHUB_RUN_ID: '123', GITHUB_ACTOR_ID: '42',
TESTOPS_ALLOWED_ACTOR_IDS: '[42]', GITHUB_RUN_NUMBER: '1', GITHUB_EVENT_PATH: eventPath,
GH_TOKEN: 'SYNTHETIC_GITHUB_SECRET', RUNNER_TEMP: f.base, GITHUB_WORKSPACE: f.base, ALLURE_JOB_RUN_ID: '77',
ALLURE_TOKEN: 'SYNTHETIC_REPORT_TOKEN', EXPECTED_SELECTION_SHA256: f.digest,
ALLURE_RESULTS: path.join(bundle, 'allure'), CHAT_TESTOPS_PREPARED_FILE: preparedFile, CHAT_TESTOPS_EXECUTION_ID: current.executionId };
const exec = async () => ({ stdout: JSON.stringify({ id: 123, run_attempt: 1, run_number: 1, workflow_id: 99,
head_sha: context.workflowSha, head_branch: 'master', event: 'workflow_dispatch', path: '.github/workflows/chat-catalog-testops.yml',
repository: event.repository, actor: { id: 42 }, triggering_actor: { id: 42 } }) });
const transport = server(prepared);
const fetchImpl = async (url, options) => {
const u = new URL(url);
if (u.pathname === '/api/uaa/oauth/token') return Response.json({ access_token: 'SYNTHETIC_ACCESS_TOKEN' });
const value = await transport.api(u.pathname + u.search, options.method, options.body);
if (u.pathname === '/api/testresult') {
value.content = value.content.filter(row => row.testCaseId === current.caseId);
value.totalElements = value.content.length;
}
return Response.json(value);
};
assert.equal(await run('upload-case', path.join(bundle, 'projection'), env, { exec, fetchImpl }), 0);
assert.equal(transport.posts, 1);
const emitted = path.join(f.base, 'chat-completion-testops-upload-case');
assert.deepEqual((await readdir(emitted)).sort(), ['once-upload_case.json', 'publication.json']);
const target = path.join(f.publications, 'chat-catalog-case-publication-123-1-0');
await rm(target, { recursive: true }); await cp(emitted, target, { recursive: true });
const exported = await publicationSet(f.bundles, f.publications, actual, selected, f.digest);
assert.equal(exported[0].status, 'passed'); assert.equal(transport.posts, 1, 'finalizer must not resend');
});