~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
183 lines · 5.4 KB Raw History
/**
 * Reading a check request record, and the repository version a requested run
 * checks against.
 */

import { afterEach, describe, expect, it } from 'vitest';
import {
  parseCheckRequest,
  readRepoRecord,
  requestedSha,
} from '../src/request.js';

const DID = 'did:plc:abc';
const SERVICE = 'https://pds.example.com';
const MAIN = 'a'.repeat(40);

const original = globalThis.fetch;

afterEach(() => {
  globalThis.fetch = original;
});

/** @param {(url: string) => Response|Promise<Response>} answer */
function serve(answer) {
  /** @type {string[]} */
  const asked = [];
  globalThis.fetch = async (/** @type {any} */ url) => {
    asked.push(String(url));
    return answer(String(url));
  };
  return asked;
}

/** @param {Partial<Record<string, unknown>>} [refs] */
function repoRecordBody(refs = {}) {
  return {
    uri: `at://${DID}/dev.pdsjs.git.repo/proj`,
    cid: 'bafyreicurrent',
    value: {
      name: 'proj',
      refs: [{ name: 'refs/heads/main', sha: MAIN }],
      bundles: [],
      createdAt: '2026-08-18T00:00:00.000Z',
      ...refs,
    },
  };
}

describe('parseCheckRequest', () => {
  it('takes the repository, the ref and an optional commit', () => {
    expect(
      parseCheckRequest({
        repo: 'proj',
        ref: 'refs/heads/main',
        requestedAt: '2026-08-19T00:00:00.000Z',
      }),
    ).toEqual({ repo: 'proj', ref: 'refs/heads/main', sha: null });

    expect(
      parseCheckRequest({ repo: 'proj', ref: 'refs/heads/main', sha: MAIN }),
    ).toEqual({ repo: 'proj', ref: 'refs/heads/main', sha: MAIN });
  });

  it('answers null for a record it cannot act on', () => {
    expect(parseCheckRequest(null)).toBe(null);
    expect(parseCheckRequest(undefined)).toBe(null);
    expect(parseCheckRequest({})).toBe(null);
    expect(parseCheckRequest({ repo: 'proj' })).toBe(null);
    expect(parseCheckRequest({ ref: 'refs/heads/main' })).toBe(null);
    expect(parseCheckRequest({ repo: '', ref: 'refs/heads/main' })).toBe(null);
    expect(parseCheckRequest({ repo: 'proj', ref: '' })).toBe(null);
    expect(parseCheckRequest({ repo: 7, ref: 'refs/heads/main' })).toBe(null);
    expect(parseCheckRequest({ repo: 'proj', ref: 7 })).toBe(null);
  });

  it('ignores a commit that is not a string', () => {
    expect(
      parseCheckRequest({ repo: 'proj', ref: 'refs/heads/main', sha: 7 })?.sha,
    ).toBe(null);
    expect(
      parseCheckRequest({ repo: 'proj', ref: 'refs/heads/main', sha: '' })?.sha,
    ).toBe(null);
  });
});

describe('readRepoRecord', () => {
  it('names the record version and its refs, read by key', async () => {
    const asked = serve(() => Response.json(repoRecordBody()));
    const record = await readRepoRecord({
      service: SERVICE,
      did: DID,
      rkey: 'proj',
    });

    expect(asked[0]).toBe(
      `${SERVICE}/xrpc/com.atproto.repo.getRecord?repo=did%3Aplc%3Aabc&collection=dev.pdsjs.git.repo&rkey=proj`,
    );
    expect(record.uri).toBe(`at://${DID}/dev.pdsjs.git.repo/proj`);
    expect(record.cid).toBe('bafyreicurrent');
    expect(record.name).toBe('proj');
    expect(record.rkey).toBe('proj');
    expect(record.refs).toEqual([{ name: 'refs/heads/main', sha: MAIN }]);
  });

  it('matches a name against the listing, since the key is minted', async () => {
    const asked = serve(() =>
      Response.json({
        records: [
          {
            uri: `at://${DID}/dev.pdsjs.git.repo/other`,
            cid: 'c1',
            value: {
              name: 'other',
              refs: [],
              bundles: [],
              createdAt: '2026-08-18T00:00:00.000Z',
            },
          },
          {
            uri: `at://${DID}/dev.pdsjs.git.repo/3lprojkey`,
            cid: 'c2',
            value: repoRecordBody().value,
          },
        ],
      }),
    );
    const record = await readRepoRecord({
      service: SERVICE,
      did: DID,
      repoName: 'proj',
    });

    expect(asked[0]).toContain('com.atproto.repo.listRecords');
    expect(record.rkey).toBe('3lprojkey');
    expect(record.name).toBe('proj');
  });

  it('throws when the PDS will not serve the record', async () => {
    serve(() => new Response('nope', { status: 404 }));
    await expect(
      readRepoRecord({ service: SERVICE, did: DID, rkey: 'proj' }),
    ).rejects.toThrow(/could not read did:plc:abc\/proj \(404\)/);
  });

  it('throws when the answer carries no record version', async () => {
    serve(() => Response.json({ value: repoRecordBody().value }));
    await expect(
      readRepoRecord({ service: SERVICE, did: DID, rkey: 'proj' }),
    ).rejects.toThrow(/no record version/);
  });

  it('throws on a record that is not a repository', async () => {
    serve(() =>
      Response.json({ uri: 'at://x', cid: 'bafy', value: { text: 'hi' } }),
    );
    await expect(
      readRepoRecord({ service: SERVICE, did: DID, rkey: 'proj' }),
    ).rejects.toThrow(/git repo record/);
  });
});

describe('requestedSha', () => {
  const refs = [{ name: 'refs/heads/main', sha: MAIN }];

  it('prefers the commit the request names', () => {
    const sha = 'b'.repeat(40);
    expect(
      requestedSha({ repo: 'proj', ref: 'refs/heads/main', sha }, refs),
    ).toBe(sha);
  });

  it('falls back to where the ref points', () => {
    expect(
      requestedSha({ repo: 'proj', ref: 'refs/heads/main', sha: null }, refs),
    ).toBe(MAIN);
  });

  it('answers null for a ref the record does not carry', () => {
    expect(
      requestedSha({ repo: 'proj', ref: 'refs/heads/gone', sha: null }, refs),
    ).toBe(null);
  });
});