aboutsummaryrefslogtreecommitdiff
path: root/test/daemon/bridge-routing-test.ts
blob: 7c96badea8e6419c911ed49af210af61b55a3b9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { test, describe } from "node:test";
import assert from "node:assert/strict";
import { createEventRouter, createIdMultiplexer, type InFlight } from "../../src/daemon/bridge";
import type { SendToClient } from "../../src/daemon/bridge";
import type { WireEvent } from "../../src/daemon/protocol";

const noSend: SendToClient = () => {};
const entry = (clientId: string, localId: number): Omit<InFlight, "timer"> => ({
  clientId,
  localId,
  send: noSend,
  isAttach: false,
});

const inertTimer = (): ReturnType<typeof setTimeout> => setTimeout(() => {}, 60_000);

describe("daemon id multiplexer", () => {
  test("two clients using the same local id get distinct daemon ids", () => {
    const mux = createIdMultiplexer();
    const a = mux.allocate(entry("client-a", 1), inertTimer);
    const b = mux.allocate(entry("client-b", 1), inertTimer);
    assert.notEqual(a, b);
  });

  test("a response is routed back to the client that asked, with its own id", () => {
    const mux = createIdMultiplexer();
    const a = mux.allocate(entry("client-a", 7), inertTimer);
    const b = mux.allocate(entry("client-b", 7), inertTimer);
    assert.equal(mux.take(b)?.clientId, "client-b");
    const takenA = mux.take(a);
    assert.equal(takenA?.clientId, "client-a");
    assert.equal(takenA?.localId, 7);
  });

  test("taking the same daemon id twice yields nothing the second time", () => {
    const mux = createIdMultiplexer();
    const id = mux.allocate(entry("client-a", 1), inertTimer);
    assert.ok(mux.take(id));
    assert.equal(mux.take(id), undefined);
  });

  test("an unknown daemon id is not mistaken for an in-flight request", () => {
    const mux = createIdMultiplexer();
    assert.equal(mux.take(999), undefined);
  });

  test("clearClient drops only that client's in-flight requests", () => {
    const mux = createIdMultiplexer();
    const a = mux.allocate(entry("client-a", 1), inertTimer);
    const b = mux.allocate(entry("client-b", 1), inertTimer);
    mux.clearClient("client-a");
    assert.equal(mux.take(a), undefined);
    assert.equal(mux.take(b)?.clientId, "client-b");
  });

  test("clearClient cancels the pending timeout so a gone client is never answered", () => {
    const mux = createIdMultiplexer();
    let fired = false;
    mux.allocate(entry("client-a", 1), () => setTimeout(() => { fired = true; }, 1));
    mux.clearClient("client-a");
    return new Promise<void>((resolve) => setTimeout(() => {
      assert.equal(fired, false);
      resolve();
    }, 30));
  });

  test("takeAll drains every in-flight request exactly once", () => {
    const mux = createIdMultiplexer();
    mux.allocate(entry("client-a", 1), inertTimer);
    mux.allocate(entry("client-b", 2), inertTimer);
    assert.equal(mux.takeAll().length, 2);
    assert.equal(mux.takeAll().length, 0);
  });
});

const ev = (sessionId?: string): WireEvent => ({
  type: "event",
  method: "Page.loadEventFired",
  ...(sessionId !== undefined ? { sessionId } : {}),
});

describe("daemon event router", () => {
  test("a session-scoped event goes only to the client owning that session", () => {
    const router = createEventRouter();
    router.record("client-a", "S1");
    router.record("client-b", "S2");
    assert.deepEqual(router.route(ev("S1")), ["client-a"]);
    assert.deepEqual(router.route(ev("S2")), ["client-b"]);
  });

  test("an event for an unknown session goes nowhere rather than to everyone", () => {
    const router = createEventRouter();
    router.record("client-a", "S1");
    assert.deepEqual(router.route(ev("S9")), []);
  });

  test("a session-less event goes to every client that holds a session", () => {
    const router = createEventRouter();
    router.record("client-a", "S1");
    router.record("client-b", "S2");
    assert.deepEqual(router.route(ev()).sort(), ["client-a", "client-b"]);
  });

  test("re-recording a session moves ownership and does not leave it with the old client", () => {
    const router = createEventRouter();
    router.record("client-a", "S1");
    router.record("client-b", "S1");
    assert.equal(router.getOwner("S1"), "client-b");
    assert.deepEqual(router.route(ev("S1")), ["client-b"]);
  });

  test("releasing a session leaves its events unrouted", () => {
    const router = createEventRouter();
    router.record("client-a", "S1");
    router.release("S1");
    assert.equal(router.getOwner("S1"), undefined);
    assert.deepEqual(router.route(ev("S1")), []);
  });

  test("removing a client releases every session it owned", () => {
    const router = createEventRouter();
    router.record("client-a", "S1");
    router.record("client-a", "S2");
    router.record("client-b", "S3");
    router.removeClient("client-a");
    assert.equal(router.getOwner("S1"), undefined);
    assert.equal(router.getOwner("S2"), undefined);
    assert.equal(router.getOwner("S3"), "client-b");
    assert.deepEqual(router.route(ev()), ["client-b"]);
  });
});