chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { describe, expect, it } from 'vitest';
import { parseWorkflow, refMatches, workflowRunsOn } from '../src/workflow.js';
const minimal = JSON.stringify({ steps: [{ run: 'true' }] });
describe('parseWorkflow', () => {
it('reads name, steps, refs and env', () => {
const workflow = parseWorkflow(
JSON.stringify({
name: 'checks',
steps: [
{ name: 'install', run: 'pnpm install' },
{ name: 'test', run: 'npm test' },
],
refs: ['refs/heads/main'],
env: { NODE_ENV: 'test' },
}),
);
expect(workflow.name).toBe('checks');
expect(workflow.steps).toEqual([
{ name: 'install', run: 'pnpm install' },
{ name: 'test', run: 'npm test' },
]);
expect(workflow.refs).toEqual(['refs/heads/main']);
expect(workflow.env).toEqual({ NODE_ENV: 'test' });
});
it('defaults the name, the refs, the env and the secrets', () => {
const workflow = parseWorkflow(minimal);
expect(workflow.name).toBe('ci');
expect(workflow.refs).toEqual(['refs/heads/*']);
expect(workflow.env).toEqual({});
expect(workflow.secrets).toEqual([]);
});
it('reads the declared secret names', () => {
const workflow = parseWorkflow(
JSON.stringify({
steps: [{ run: 'true' }],
secrets: ['NPM_TOKEN', 'SITE_TOKEN'],
}),
);
expect(workflow.secrets).toEqual(['NPM_TOKEN', 'SITE_TOKEN']);
});
it('numbers an unnamed step from one', () => {
expect(parseWorkflow(minimal).steps[0].name).toBe('step 1');
});
it('reads YAML, keeping a block scalar as written', () => {
const workflow = parseWorkflow(
[
'# the shell a step runs',
'name: release',
'refs:',
' - refs/tags/v*',
'env:',
' PDS_URL: "https://pds.example.com"',
'secrets: [NPM_TOKEN]',
'steps:',
' - name: guard',
' run: |',
' set -e',
' if [ -z "${NPM_TOKEN:-}" ]; then',
' echo "no token"',
' exit 1',
' fi',
].join('\n'),
);
expect(workflow.name).toBe('release');
expect(workflow.refs).toEqual(['refs/tags/v*']);
expect(workflow.env).toEqual({ PDS_URL: 'https://pds.example.com' });
expect(workflow.secrets).toEqual(['NPM_TOKEN']);
// The newlines are the point: a step is a script, not one escaped line.
expect(workflow.steps[0].run).toBe(
'set -e\nif [ -z "${NPM_TOKEN:-}" ]; then\n echo "no token"\n exit 1\nfi\n',
);
});
it('still reads JSON, which YAML 1.2 subsumes', () => {
const workflow = parseWorkflow(minimal);
expect(workflow.steps).toEqual([{ name: 'step 1', run: 'true' }]);
});
it('rejects malformed definitions', () => {
expect(() => parseWorkflow('steps: [')).toThrow(/cannot parse/);
expect(() => parseWorkflow('[]')).toThrow(/must be a mapping/);
expect(() => parseWorkflow('{}')).toThrow(/steps must be/);
expect(() => parseWorkflow('{"steps":[]}')).toThrow(/steps must be/);
expect(() => parseWorkflow('{"steps":["true"]}')).toThrow(
/step 0 must be an object/,
);
expect(() => parseWorkflow('{"steps":[{}]}')).toThrow(/step 0 run/);
expect(() => parseWorkflow('{"steps":[{"run":"true"}],"refs":[]}')).toThrow(
/refs must be/,
);
expect(() =>
parseWorkflow('{"steps":[{"run":"true"}],"env":{"A":1}}'),
).toThrow(/env\.A/);
expect(() => parseWorkflow('{"steps":[{"run":"true"}],"env":[]}')).toThrow(
/env must be an object/,
);
expect(() =>
parseWorkflow('{"steps":[{"run":"true"}],"secrets":{}}'),
).toThrow(/secrets must be an array/);
expect(() =>
parseWorkflow('{"steps":[{"run":"true"}],"secrets":[1]}'),
).toThrow(/secrets entry 0/);
});
});
describe('refMatches', () => {
it('matches an exact ref', () => {
expect(refMatches('refs/heads/main', 'refs/heads/main')).toBe(true);
expect(refMatches('refs/heads/main', 'refs/heads/other')).toBe(false);
});
it('treats a trailing star as a prefix', () => {
expect(refMatches('refs/heads/*', 'refs/heads/feature/a')).toBe(true);
expect(refMatches('refs/heads/*', 'refs/tags/v1')).toBe(false);
});
});
describe('workflowRunsOn', () => {
it('runs when any pattern matches', () => {
const workflow = parseWorkflow(
JSON.stringify({
steps: [{ run: 'true' }],
refs: ['refs/tags/*', 'refs/heads/main'],
}),
);
expect(workflowRunsOn(workflow, 'refs/heads/main')).toBe(true);
expect(workflowRunsOn(workflow, 'refs/tags/v1')).toBe(true);
expect(workflowRunsOn(workflow, 'refs/heads/topic')).toBe(false);
});
it('runs on any branch by default', () => {
const workflow = parseWorkflow(minimal);
expect(workflowRunsOn(workflow, 'refs/heads/topic')).toBe(true);
expect(workflowRunsOn(workflow, 'refs/tags/v1')).toBe(false);
});
});