chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { describe, expect, it } from 'vitest';
import {
accessLabel,
appAccessLabel,
bytesToHex,
collectionsOf,
fileSize,
nsidDomain,
policyLabel,
rkeyTime,
shortDid,
spaceParts,
until,
} from '#/lib/format.js';
describe('spaceParts', () => {
it('reads the three components a heading shows', () => {
expect(
spaceParts('at://did:plc:abcdef/space/com.example.forum/general'),
).toEqual({
spaceDid: 'did:plc:abcdef',
spaceType: 'com.example.forum',
skey: 'general',
});
});
it('refuses a public AT-URI, which addresses a repo rather than a space', () => {
expect(() =>
spaceParts('at://did:plc:abcdef/app.bsky.feed.post/3k2a'),
).toThrow();
});
});
describe('shortDid', () => {
it('leaves a short DID whole', () => {
expect(shortDid('did:web:example.com')).toBe('did:web:example.com');
});
it('keeps both ends, so two DIDs from one directory read apart', () => {
const shortened = shortDid('did:plc:c4igoshkxhzw62eme3iltpdi');
expect(shortened.startsWith('did:plc:c4igosh')).toBe(true);
expect(shortened.endsWith('ltpdi')).toBe(true);
});
});
describe('rkeyTime', () => {
it('reads the write time out of a TID', () => {
const at = rkeyTime('3mtjzqmmgxsrb');
expect(at?.getUTCFullYear()).toBe(2026);
});
it('answers null for a key that is not a TID', () => {
expect(rkeyTime('self')).toBeNull();
});
});
describe('until', () => {
// Each offset carries most of a unit in hand, because the clock moves between
// building the argument and reading it, and the count is floored.
it('counts down in the coarsest unit that still says something', () => {
expect(until(Date.now() + 45_900)).toBe('in 45 seconds');
expect(until(Date.now() + 2 * 3_600_000 + 60_000)).toBe('in 2 hours');
});
// A delegation token lives sixty seconds, so a countdown that stopped at
// minutes would show every one of them as nothing.
it('has seconds as a unit', () => {
expect(until(Date.now() + 1900)).toBe('in 1 second');
});
it('says a past time has expired', () => {
expect(until(Date.now() - 1000)).toBe('expired');
});
it('answers empty for no time at all', () => {
expect(until(null)).toBe('');
});
});
describe('fileSize', () => {
it('names the unit the number is readable in', () => {
expect(fileSize(70)).toBe('70 B');
expect(fileSize(2048)).toBe('2 KB');
expect(fileSize(3 * 1024 * 1024)).toBe('3.0 MB');
});
});
describe('bytesToHex', () => {
it('renders a commit hash the way another implementation prints it', () => {
expect(bytesToHex({ $bytes: 'AAECqv8' })).toBe('000102aaff');
});
it('answers empty for a field that carries no bytes', () => {
expect(bytesToHex(null)).toBe('');
expect(bytesToHex(undefined)).toBe('');
});
});
describe('collectionsOf', () => {
const records = [
{ collection: 'b.example.note', rkey: '3mtjzqmmgxsrb' },
{ collection: 'a.example.pin', rkey: '3mtjzqmmwlsrb' },
{ collection: 'b.example.note', rkey: 'self' },
];
it('counts each collection and sorts by name', () => {
expect(collectionsOf(records).map((c) => [c.collection, c.count])).toEqual([
['a.example.pin', 1],
['b.example.note', 2],
]);
});
it('takes the latest write time from the TIDs, ignoring keys that carry none', () => {
const notes = collectionsOf(records)[1];
expect(notes.lastWriteAt).toBe(rkeyTime('3mtjzqmmgxsrb')?.getTime());
});
it('leaves lastWriteAt null when no key is a TID', () => {
expect(
collectionsOf([{ collection: 'a.example.x', rkey: 'self' }])[0]
.lastWriteAt,
).toBeNull();
});
});
describe('policyLabel', () => {
it('names the policy', () => {
expect(
policyLabel({ policy: { $type: 'com.atproto.memberspace.policy' } }),
).toBe('Members on a list');
});
it('says so plainly for a policy it does not know', () => {
expect(policyLabel({})).toBe('Unknown');
});
});
describe('accessLabel', () => {
const members = { $type: 'com.atproto.memberspace.policy' };
it('reads public access off a memberspace', () => {
const config = { policy: { ...members, publicRead: true } };
expect(accessLabel(config, 'read')).toBe('Anyone');
expect(accessLabel(config, 'write')).toBe('Members who may write');
});
it('names the policy for one with no flags', () => {
expect(
accessLabel(
{ policy: { $type: 'com.atproto.personalspace.policy' } },
'read',
),
).toBe('The authority alone');
});
});
describe('appAccessLabel', () => {
it('counts an allow list', () => {
expect(
appAccessLabel({
policy: {
$type: 'com.atproto.memberspace.policy',
allowedApps: ['https://one.example', 'https://two.example'],
},
}),
).toBe('2 clients on an allow list');
});
it('treats an absent list as open, which is the documented default', () => {
expect(appAccessLabel({})).toBe('Any client');
});
});
describe('nsidDomain', () => {
// A space type is an NSID, and the domain it reverses to is the app the
// space belongs to, which is what serves the icon.
it('reverses the first two segments', () => {
expect(nsidDomain('my.bulletin.board')).toBe('bulletin.my');
expect(nsidDomain('social.grain.gallery')).toBe('grain.social');
expect(nsidDomain('app.bulleted.space')).toBe('bulleted.app');
});
// Two segments, not the whole authority: a favicon answers at `bsky.app`,
// never at `feed.bsky.app`.
it('stops at the registrable domain however deep the NSID goes', () => {
expect(nsidDomain('app.bsky.feed.post')).toBe('bsky.app');
expect(nsidDomain('dev.pdsjs.drive.space')).toBe('pdsjs.dev');
});
it('leaves something that is not an NSID alone', () => {
expect(nsidDomain('board')).toBe('board');
});
});