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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
95 lines · 3.0 KB Raw History
/**
 * What a settings form offers to link: every package this account publishes,
 * read from the two registry collections. The write itself goes through OAuth
 * and a live PDS, which the browser exercises.
 */

import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { account } from '../src/lib/git.js';
import { packageKey, publishedPackages } from '../src/lib/settings.js';

const original = globalThis.fetch;

beforeEach(() => {
  account.did = 'did:plc:owner';
  account.pds = 'https://pds.example.com';
  account.handle = 'owner.example.com';
});

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

/** @param {string} name */
const record = (name) => ({ value: { name } });

/**
 * Answer each collection with the pages it holds, and a cursor while pages
 * remain.
 * @param {Record<string, Array<Array<{value: {name: string}}>>>} pages
 */
function serve(pages) {
  /** @type {string[]} */
  const asked = [];
  globalThis.fetch = async (/** @type {string|URL|Request} */ input) => {
    const url = new URL(String(input));
    asked.push(url.searchParams.get('cursor') ?? '');
    const collection = url.searchParams.get('collection') ?? '';
    const held = pages[collection] ?? [[]];
    const page = Number(url.searchParams.get('cursor') ?? '0');
    return Response.json({
      records: held[page] ?? [],
      ...(page + 1 < held.length ? { cursor: String(page + 1) } : {}),
    });
  };
  return asked;
}

describe('packageKey', () => {
  it('names a package by the pair, since a name repeats across registries', () => {
    expect(packageKey({ registry: 'npm', name: 'app' })).not.toBe(
      packageKey({ registry: 'oci', name: 'app' }),
    );
  });
});

describe('publishedPackages', () => {
  it('reads both registries and says which one holds each name', async () => {
    serve({
      'dev.pdsjs.npm.package': [[record('@scope/lib')]],
      'dev.pdsjs.oci.repo': [[record('app/server')]],
    });
    expect(await publishedPackages()).toEqual([
      { registry: 'npm', name: '@scope/lib' },
      { registry: 'oci', name: 'app/server' },
    ]);
  });

  it('follows the cursor to the end of a collection', async () => {
    const asked = serve({
      'dev.pdsjs.npm.package': [[record('one')], [record('two')]],
    });
    const packages = await publishedPackages();
    expect(packages.map((entry) => entry.name)).toEqual(['one', 'two']);
    expect(asked).toContain('1');
  });

  it('leaves out a record naming nothing', async () => {
    serve({ 'dev.pdsjs.npm.package': [[{ value: {} }, record('lib')]] });
    expect(await publishedPackages()).toEqual([
      { registry: 'npm', name: 'lib' },
    ]);
  });

  it('reads a registry the server does not serve as nothing published', async () => {
    globalThis.fetch = async () => new Response('no', { status: 404 });
    expect(await publishedPackages()).toEqual([]);
  });

  it('reads an unreachable PDS as nothing published', async () => {
    globalThis.fetch = async () => {
      throw new Error('offline');
    };
    expect(await publishedPackages()).toEqual([]);
  });
});