chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* Reducing a runner's check listing to this repository's runs, naming the log
* blob a finished check carries, and reading that blob as text.
*/
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import {
checkLogUrl,
checkLook,
duration,
groupBySha,
latestBySha,
latestPerWorkflow,
loadRefChecks,
readLog,
runDuration,
summarize,
summaryWords,
workflowNames,
workflowOf,
} from '../src/lib/checks.js';
import { account } from '../src/lib/git.js';
describe('latestBySha', () => {
it('keys the newest run of each workflow by its commit', () => {
const sha = 'a'.repeat(40);
const map = latestBySha([
{
sha,
workflow: 'ci',
status: 'success',
startedAt: '2026-01-02T00:00:00Z',
},
{
sha,
workflow: 'ci',
status: 'failure',
startedAt: '2026-01-01T00:00:00Z',
},
{
sha: 'b'.repeat(40),
status: 'failure',
startedAt: '2026-01-03T00:00:00Z',
},
]);
expect(map.size).toBe(2);
expect(map.get(sha)?.map((run) => run.status)).toEqual(['success']);
expect(map.get('b'.repeat(40))?.map((run) => run.status)).toEqual([
'failure',
]);
});
it('keeps one entry per workflow, so no workflow hides another', () => {
const sha = 'a'.repeat(40);
const map = latestBySha([
{
sha,
workflow: 'publish',
status: 'success',
startedAt: '2026-01-02T00:00:00Z',
},
{
sha,
workflow: 'ci',
status: 'failure',
startedAt: '2026-01-01T00:00:00Z',
},
]);
expect(map.get(sha)?.map(workflowOf)).toEqual(['publish', 'ci']);
});
});
describe('groupBySha', () => {
const run = (sha, workflow, startedAt) => ({
sha,
workflow,
status: 'success',
startedAt,
});
it('gathers each commit runs, newest commit first', () => {
const groups = groupBySha([
run('b', 'ci', '2026-01-03T00:00:00Z'),
run('a', 'ci', '2026-01-02T00:00:00Z'),
run('b', 'lint', '2026-01-01T00:00:00Z'),
]);
expect(groups.map((group) => group.sha)).toEqual(['b', 'a']);
expect(groups[0].runs.map(workflowOf)).toEqual(['ci', 'lint']);
});
it('keeps a re-run beside the run it repeated', () => {
const groups = groupBySha([
run('a', 'ci', '2026-01-02T00:00:00Z'),
run('a', 'ci', '2026-01-01T00:00:00Z'),
]);
expect(groups).toHaveLength(1);
expect(groups[0].runs).toHaveLength(2);
});
});
describe('latestPerWorkflow', () => {
it('keeps the newest run of each workflow', () => {
const latest = latestPerWorkflow([
{ sha: 'a', workflow: 'ci', status: 'success', startedAt: 'b' },
{ sha: 'a', workflow: 'ci', status: 'failure', startedAt: 'a' },
{ sha: 'a', workflow: 'lint', status: 'success', startedAt: 'a' },
]);
expect(latest.map((run) => [workflowOf(run), run.status])).toEqual([
['ci', 'success'],
['lint', 'success'],
]);
});
});
describe('summarize', () => {
const run = (workflow, status) => ({
sha: 'a'.repeat(40),
workflow,
status,
startedAt: '2026-01-01T00:00:00Z',
});
it('reads a set every workflow passed as passed', () => {
expect(
summarize([run('ci', 'success'), run('publish', 'success')]),
).toEqual({ status: 'success', passed: 2, total: 2 });
});
it('lets one failure decide the whole set', () => {
expect(
summarize([run('publish', 'success'), run('ci', 'failure')]),
).toEqual({ status: 'failure', passed: 1, total: 2 });
});
it('names an errored run rather than calling it failed', () => {
expect(summarize([run('ci', 'error')]).status).toBe('error');
});
it('reads an unfinished set as running', () => {
expect(
summarize([run('ci', 'success'), run('publish', 'running')]),
).toEqual({ status: 'running', passed: 1, total: 2 });
});
it('answers null for a commit no workflow reported on', () => {
expect(summarize([])).toBe(null);
});
});
describe('summaryWords', () => {
const run = (workflow, status) => ({
sha: 'a'.repeat(40),
workflow,
status,
startedAt: '2026-01-01T00:00:00Z',
});
it('names the workflow where there is only one', () => {
const runs = [run('ci', 'failure')];
expect(summaryWords(summarize(runs), runs)).toBe('ci failed');
});
it('counts the workflows where there are several', () => {
const runs = [run('ci', 'failure'), run('publish', 'success')];
expect(summaryWords(summarize(runs), runs)).toBe('1 of 2 workflows passed');
});
it('says what an unfinished set is still doing', () => {
const runs = [run('ci', 'success'), run('publish', 'running')];
expect(summaryWords(summarize(runs), runs)).toBe(
'1 of 2 workflows passed, the rest running',
);
});
});
describe('workflowOf', () => {
it('reads a run that names no workflow as ci', () => {
expect(workflowOf({})).toBe('ci');
expect(workflowOf({ workflow: 'publish' })).toBe('publish');
});
});
describe('workflowNames', () => {
it('names each workflow once, in the order a reader meets it', () => {
expect(
workflowNames([
{ workflow: 'publish' },
{ workflow: 'ci' },
{ workflow: 'publish' },
{},
]),
).toEqual(['publish', 'ci']);
});
});
describe('checkLook', () => {
it('gives each known status its own words', () => {
expect(checkLook('success').label).toBe('passed');
expect(checkLook('failure').label).toBe('failed');
expect(checkLook('error').label).toBe('errored');
expect(checkLook('running').label).toBe('running');
});
it('reads a status this build does not know as running', () => {
expect(checkLook('cancelled').label).toBe('running');
expect(checkLook(undefined).label).toBe('running');
});
});
describe('duration', () => {
it('climbs to the largest unit that stays readable', () => {
expect(duration(0)).toBe('0ms');
expect(duration(940)).toBe('940ms');
expect(duration(1500)).toBe('2s');
expect(duration(45_000)).toBe('45s');
expect(duration(72_000)).toBe('1m 12s');
expect(duration(3_600_000)).toBe('1h 0m');
expect(duration(5_460_000)).toBe('1h 31m');
});
});
describe('runDuration', () => {
it('measures a finished run', () => {
expect(
runDuration({
sha: 'a',
status: 'success',
startedAt: '2026-01-01T00:00:00Z',
finishedAt: '2026-01-01T00:01:12Z',
}),
).toBe('1m 12s');
});
it('answers null for a run that has not finished', () => {
expect(
runDuration({
sha: 'a',
status: 'running',
startedAt: '2026-01-01T00:00:00Z',
}),
).toBe(null);
});
it('answers null when the timestamps disagree about order', () => {
expect(
runDuration({
sha: 'a',
status: 'success',
startedAt: '2026-01-01T00:01:00Z',
finishedAt: '2026-01-01T00:00:00Z',
}),
).toBe(null);
});
});
describe('checkLogUrl', () => {
const runner = { did: 'did:plc:runner', service: 'https://pds.example.com' };
it('names the log blob on the runner PDS', () => {
const url = checkLogUrl(runner, {
sha: 'a'.repeat(40),
status: 'failure',
startedAt: '2026-01-01T00:00:00Z',
logs: { ref: { $link: 'bafylog' } },
});
expect(url).toBe(
'https://pds.example.com/xrpc/com.atproto.sync.getBlob?did=did%3Aplc%3Arunner&cid=bafylog',
);
});
it('answers null for a run with no log yet', () => {
const check = {
sha: 'a'.repeat(40),
status: 'running',
startedAt: '2026-01-01T00:00:00Z',
};
expect(checkLogUrl(runner, check)).toBe(null);
expect(checkLogUrl(null, { ...check, logs: { ref: { $link: 'x' } } })).toBe(
null,
);
});
});
describe('loadRefChecks', () => {
const original = globalThis.fetch;
const originalLocation = globalThis.location;
beforeEach(() => {
// The git service decision probes this page's own origin first; the
// stubs below refuse that probe, so the account's PDS stays the service.
globalThis.location = new URL('https://site.example/');
account.pds = 'https://pds.example.com';
});
afterEach(() => {
globalThis.fetch = original;
globalThis.location = originalLocation;
account.pds = '';
});
/**
* The one read the ref badge makes: the server's latest check per branch.
* @param {unknown} answer - what dev.pdsjs.git.listChecks answers
*/
const serve = (answer) => {
globalThis.fetch = async (/** @type {string} */ url) => {
if (String(url).includes('dev.pdsjs.git.listChecks')) {
return Response.json(answer);
}
return new Response('no', { status: 404 });
};
};
it('reads the latest run of each workflow on the ref, and the run each names', async () => {
serve({
runner: 'did:plc:runner',
service: 'https://runner.example.com',
checks: [
{
ref: 'refs/heads/main',
workflow: 'ci',
sha: 'a'.repeat(40),
status: 'success',
startedAt: '2026-01-01T00:00:00Z',
check: { uri: 'at://did:plc:runner/dev.pdsjs.git.check/3ltid' },
},
{
ref: 'refs/heads/other',
workflow: 'ci',
sha: 'b'.repeat(40),
status: 'failure',
startedAt: '2026-01-01T00:00:00Z',
check: { uri: 'at://did:plc:runner/dev.pdsjs.git.check/3other' },
},
],
});
const { runner, runs } = await loadRefChecks('project', 'main');
expect(runner?.service).toBe('https://runner.example.com');
expect(runs).toHaveLength(1);
expect(runs[0].rkey).toBe('3ltid');
expect(workflowOf(runs[0])).toBe('ci');
});
it('reads a repository naming no runner as no runs', async () => {
serve({ runner: null, checks: [] });
const { runner, runs } = await loadRefChecks('other', 'main');
expect(runner).toBeNull();
expect(runs).toEqual([]);
});
});
describe('readLog', () => {
const runner = { did: 'did:plc:runner', service: 'https://pds.example.com' };
const check = {
sha: 'a'.repeat(40),
status: 'failure',
startedAt: '2026-01-01T00:00:00Z',
logs: { ref: { $link: 'bafylog' } },
};
const original = globalThis.fetch;
afterEach(() => {
globalThis.fetch = original;
});
it('strips the terminal escapes a coloured step writes', async () => {
const esc = String.fromCharCode(27);
globalThis.fetch = async () =>
new Response(`${esc}[32mok${esc}[0m\ndone\n`);
expect(await readLog(runner, check)).toBe('ok\ndone\n');
});
it('answers empty for a run with no log', async () => {
expect(await readLog(runner, { ...check, logs: undefined })).toBe('');
});
it('throws when the runner will not serve the blob', async () => {
globalThis.fetch = async () => new Response('gone', { status: 404 });
await expect(readLog(runner, check)).rejects.toThrow(
/would not serve this log/,
);
});
});