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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
98 lines · 3.0 KB Raw History
// Orphan cleanup deletes blobs the link table says nothing points at. A space
// record is a reference the table can be missing — spaces turned on after the
// records existed, a link write that failed — so the guard that cross-checks
// record content has to read space records as well as public ones.
import { describe, expect, it } from 'vitest';
import { PersonalDataServer } from '../packages/core/src/pds.js';
import { createFakeSpaceStorage } from './helpers/fake-space-storage.js';

const DID = 'did:plc:reapaccount23456789012';
const SPACE = 'at://did:plc:authority/space/com.example.forum/general';
const BLOB_CID = 'bafkreiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';

/** @param {string} cid */
function postWith(cid) {
  return {
    $type: 'com.example.post',
    image: { $type: 'blob', ref: { $link: cid }, mimeType: 'image/png' },
  };
}

/**
 * A PDS holding one blob that nothing in the public repo references, and one
 * space record that does.
 * @param {{withSpaces: boolean}} opts
 */
function createPds({ withSpaces }) {
  /** @type {string[]} */
  const deleted = [];
  const spaceRecord = {
    space: SPACE,
    collection: 'com.example.post',
    rkey: 'a',
    value: postWith(BLOB_CID),
  };

  const pds = new PersonalDataServer({
    actorStorage: /** @type {any} */ ({
      async getDid() {
        return DID;
      },
      async listAllRecords() {
        return [];
      },
      async getRecord() {
        return null;
      },
      // The link table has never heard of the space record.
      async getOrphanedBlobs() {
        return [BLOB_CID];
      },
      async linkBlobToRecord() {},
      async deleteBlob(/** @type {string} */ cid) {
        deleted.push(cid);
      },
    }),
    sharedStorage: /** @type {any} */ ({}),
    blobs: /** @type {any} */ ({
      async delete(/** @type {string} */ _did, /** @type {string} */ cid) {
        deleted.push(cid);
      },
    }),
    spaceStorage: withSpaces
      ? createFakeSpaceStorage({
          spaces: [{ uri: SPACE }],
          records: { [SPACE]: [{ ...spaceRecord, cid: 'bafyspace' }] },
        })
      : undefined,
    jwtSecret: 'test-secret',
  });

  return { pds, deleted };
}

describe('orphan reaping with permissioned data', () => {
  it('spares a blob only a space record references', async () => {
    const { pds, deleted } = createPds({ withSpaces: true });
    await pds.reapOrphanedBlobs(DID, Date.now() + 1000);
    expect(deleted).toEqual([]);
  });

  it('counts a space record among the referenced blobs', async () => {
    const { pds } = createPds({ withSpaces: true });
    const refs = await pds.collectReferencedBlobs();
    expect(refs).toEqual([
      {
        cid: BLOB_CID,
        recordUri: `${SPACE}/${DID}/com.example.post/a`,
        recordTime: null,
      },
    ]);
  });

  it('reaps the same blob when the account holds no spaces', async () => {
    const { pds, deleted } = createPds({ withSpaces: false });
    await pds.reapOrphanedBlobs(DID, Date.now() + 1000);
    expect(deleted).toContain(BLOB_CID);
  });
});