jevstrudel.git / worker / src / party-directory.test.ts
1import { beforeEach, describe, expect, it } from 'vitest';
2import { live, note, STALE_MS, type Storage } from './party-directory';
3
4let store: Map<string, unknown>;
5const storage: Storage = {
6  get: async <T,>(k: string) => store.get(k) as T | undefined,
7  put: async (k: string, v: unknown) => void store.set(k, v),
8  delete: async (k: string) => store.delete(k),
9  list: async <T,>({ prefix, limit }: { prefix: string; limit?: number }) =>
10    new Map([...store].filter(([k]) => k.startsWith(prefix)).slice(0, limit ?? Infinity) as [string, T][]),
11};

a Durable Object id: 64 hex characters

13const room = (c: string) => c.repeat(64);
15beforeEach(() => {
16  store = new Map();
17});
18
19describe('the party directory', () => {
20  it('keeps a live room, since it started, and forgets it once empty', async () => {
21    await note(storage, room('a'), { people: 1, host: true, song: null }, 1000);
22    await note(storage, room('a'), { people: 3, host: true, song: 'jev/dial-up' }, 5000);
23    expect(await live(storage, 6000)).toEqual([{ people: 3, host: true, song: 'jev/dial-up', since: 1000, listed: false }]);
24    await note(storage, room('a'), null, 7000);
25    expect(await live(storage, 8000)).toEqual([]);
26  });
27
28  it('lists the longest-running first, and drops one that stopped reporting', async () => {
29    await note(storage, room('b'), { people: 2, host: false, song: null }, 2000);
30    await note(storage, room('c'), { people: 1, host: true, song: null }, 1000);
31    expect((await live(storage, 3000)).map((p) => p.since)).toEqual([1000, 2000]);
32    expect(await live(storage, 2000 + STALE_MS + 1)).toEqual([]);
33    expect(store.size).toBe(0);
34  });
35
36  it("gives a listed room's object id, to join it by, and never an unlisted one's", async () => {
37    await note(storage, room('e'), { people: 2, host: true, song: 'jev/dial-up', listed: true }, 1);
38    await note(storage, room('f'), { people: 1, host: true, song: null, listed: false }, 2);
39    await note(storage, room('0'), { people: 1, host: true, song: null }, 3);
40    const parties = await live(storage, 4);
41    expect(parties).toEqual([
42      { people: 2, host: true, song: 'jev/dial-up', since: 1, listed: true, id: room('e') },
43      { people: 1, host: true, song: null, since: 2, listed: false },
44      { people: 1, host: true, song: null, since: 3, listed: false },
45    ]);
46    // a host who stops being listed takes the id back at the next report
47    await note(storage, room('e'), { people: 2, host: true, song: 'jev/dial-up', listed: false }, 5);
48    expect((await live(storage, 6)).some((p) => 'id' in p)).toBe(false);
49  });
50
51  it('takes only a Durable Object id, and bounds what a room says', async () => {
52    await note(storage, 'my-secret-room-name', { people: 1, host: true, song: null }, 1);
53    expect(store.size).toBe(0);
54    await note(storage, room('d'), { people: 1e9, host: true, song: 'x'.repeat(500) }, 1);
55    const [p] = await live(storage, 2);
56    expect(p.people).toBe(1000);
57    expect(p.song).toHaveLength(129);
58  });
59});