chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
import { describe, expect, it } from 'vitest';
import { issueState, issueThreads } from '../src/issues.js';
describe('issueState and issueThreads', () => {
const OWNER = 'did:plc:owner';
/** @param {Partial<Record<string, unknown>>} value */
const entry = (value) => ({
by: { did: 'did:plc:filer', handle: 'filer' },
...value,
});
it('reads the state from the owner and nobody else', () => {
const thread = [
entry({ uri: 'at://a', createdAt: '2026-08-01T00:00:00Z' }),
entry({
uri: 'at://b',
createdAt: '2026-08-02T00:00:00Z',
disposition: 'resolved',
}),
];
// The filer marking their own issue resolved is a comment, not a state.
expect(issueState(thread, OWNER).state).toBe('open');
thread.push(
entry({
by: { did: OWNER, handle: 'owner' },
uri: 'at://c',
createdAt: '2026-08-03T00:00:00Z',
disposition: 'resolved',
}),
);
expect(issueState(thread, OWNER)).toMatchObject({ state: 'resolved' });
});
it('reads the latest word, so reopened takes back a decline', () => {
const owner = { did: OWNER, handle: 'owner' };
const thread = [
entry({ uri: 'at://a', createdAt: '2026-08-01T00:00:00Z' }),
entry({
by: owner,
uri: 'at://b',
createdAt: '2026-08-02T00:00:00Z',
disposition: 'declined',
}),
];
expect(issueState(thread, OWNER)).toMatchObject({ state: 'declined' });
thread.push(
entry({
by: owner,
uri: 'at://c',
createdAt: '2026-08-03T00:00:00Z',
disposition: 'reopened',
}),
);
expect(issueState(thread, OWNER)).toMatchObject({
state: 'open',
said: { uri: 'at://c', disposition: 'reopened' },
});
});
it('leaves the state alone when anybody but the owner reopens', () => {
const thread = [
entry({ uri: 'at://a', createdAt: '2026-08-01T00:00:00Z' }),
entry({
by: { did: OWNER, handle: 'owner' },
uri: 'at://b',
createdAt: '2026-08-02T00:00:00Z',
disposition: 'declined',
}),
entry({
uri: 'at://c',
createdAt: '2026-08-03T00:00:00Z',
disposition: 'reopened',
}),
];
expect(issueState(thread, OWNER)).toMatchObject({ state: 'declined' });
});
it('reads a word off Object.prototype as no disposition at all', () => {
const thread = [
entry({
by: { did: OWNER, handle: 'owner' },
uri: 'at://a',
createdAt: '2026-08-01T00:00:00Z',
disposition: 'constructor',
}),
];
expect(issueState(thread, OWNER)).toEqual({ state: 'open' });
});
it('groups replies with their opener, newest activity first', () => {
const threads = issueThreads(
[
entry({
uri: 'at://old',
title: 'old',
createdAt: '2026-08-01T00:00:00Z',
}),
entry({
uri: 'at://young',
title: 'young',
createdAt: '2026-08-02T00:00:00Z',
}),
entry({
uri: 'at://answer',
createdAt: '2026-08-03T00:00:00Z',
replyTo: { uri: 'at://old' },
}),
],
OWNER,
);
expect(threads.map((t) => t.root.title)).toEqual(['old', 'young']);
expect(threads[0].thread).toHaveLength(2);
});
});