jevstrudel.git / worker / test / tab-hubs.ts

The TAB_HUB Durable Object namespace for tests: one hub per name (a user's id, as the Worker names them), each holding the tabs a test opens in it. A tab is a function answering commands as the page would (website/src/jev/myTabs.mjs); what each hub was asked is recorded, so a test can show a command reached only the hub it should.

6import type { TabCommand, TabReply } from '../src/tab-hub-core';
8type Tab = (command: TabCommand) => TabReply | Promise<TabReply>;
9
10export function memoryTabHubs() {
11  const hubs = new Map<string, Map<string, Tab>>();
12  const asked: { hub: string; session: string; command: TabCommand }[] = [];
13  const hub = (name: string) => {
14    let tabs = hubs.get(name);
15    if (!tabs) hubs.set(name, (tabs = new Map()));
16    return tabs;
17  };
18  return {
19    asked,
20    // a tab open in `user`'s hub
21    open(user: string, session: string, tab: Tab) {
22      hub(user).set(session, tab);
23    },
24    close(user: string, session: string) {
25      hub(user).delete(session);
26    },
27    idFromName: (name: string) => ({ name }),
28    get: ({ name }: { name: string }) => ({
29      sessions: async () => [...hub(name).keys()],
30      command: async (session: string, command: TabCommand) => {
31        const tab = hub(name).get(session);
32        if (!tab) throw new Error(`you have no open tab ${session}`);
33        asked.push({ hub: name, session, command });
34        return tab(command);
35      },
36      fetch: async (request: Request) => {
37        const session = new URL(request.url).searchParams.get('session_id') ?? '';
38        return Response.json({ joined: name, session });
39      },
40    }),
41  };
42}