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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
70 lines · 2.6 KB Raw History
// Shared contract for the ActorStoragePort methods that let commits drop the
// blocks they leave unreachable. Every adapter that has them runs this suite.

import { describe, expect, it } from 'vitest';

/** @typedef {Required<import('@pdsjs/core/ports').ActorStoragePort>} BlockStorage */

/**
 * @param {string} name
 * @param {() => Promise<BlockStorage>} open
 */
export function runBlockStorageConformance(name, open) {
  describe(`${name}: blocks`, () => {
    it('list in CID order a page at a time, and delete', async () => {
      const storage = await open();
      for (const cid of ['c', 'a', 'b']) {
        await storage.putBlock(cid, new Uint8Array([cid.charCodeAt(0)]));
      }

      const first = await storage.listBlocks(null, 2);
      expect(first.blocks.map((b) => b.cid)).toEqual(['a', 'b']);
      expect(first.cursor).toBe('b');
      const rest = await storage.listBlocks(first.cursor, 2);
      expect(rest.blocks.map((b) => [b.cid, [...b.data]])).toEqual([
        ['c', [99]],
      ]);
      expect(rest.cursor).toBeNull();

      await storage.deleteBlocks(['a', 'c']);
      const left = await storage.listBlocks(null, 10);
      expect(left.blocks.map((b) => b.cid)).toEqual(['b']);
    });

    it('delete and look up more CIDs than one statement binds', async () => {
      const storage = await open();
      const cids = Array.from({ length: 250 }, (_, i) => `cid${1000 + i}`);
      for (const cid of cids) {
        await storage.putBlock(cid, new Uint8Array([1]));
        await storage.putRecord(
          `at://did:x/c/${cid}`,
          cid,
          'c',
          cid,
          new Uint8Array([1]),
        );
      }
      expect(await storage.recordCidsAmong(cids)).toHaveLength(250);
      await storage.deleteBlocks(cids.slice(0, 240));
      const left = await storage.listBlocks(null, 500);
      expect(left.blocks.map((b) => b.cid)).toEqual(cids.slice(240));
    });

    it('say which CIDs a record still has', async () => {
      const storage = await open();
      const value = new Uint8Array([1]);
      await storage.putRecord('at://did:x/c/1', 'kept', 'c', '1', value);
      await storage.putRecord('at://did:x/c/2', 'kept', 'c', '2', value);
      expect(await storage.recordCidsAmong(['kept', 'gone'])).toEqual(['kept']);
    });

    it('are not collected until a sweep says so', async () => {
      const storage = await open();
      expect(await storage.getBlocksCollected()).toBe(false);
      await storage.setBlocksCollected(true);
      expect(await storage.getBlocksCollected()).toBe(true);
      await storage.setBlocksCollected(false);
      expect(await storage.getBlocksCollected()).toBe(false);
    });
  });
}