Skip to content

Commit 89cf341

Browse files
test: watch the signal listeners of the MCP waiters that give up, not the global Set
1 parent 662947c commit 89cf341

2 files changed

Lines changed: 26 additions & 37 deletions

File tree

‎src/core/webview/__tests__/mcpEnabledChanges.spec.ts‎

Lines changed: 25 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -253,45 +253,33 @@ describe("changeMcpEnabled", () => {
253253
expect(hub.handleMcpEnabledChange.mock.calls).toEqual([[true], [false]])
254254
})
255255

256-
it("takes every save that gives up off the change again, while the shared change stays stalled", async () => {
256+
it("lets go of the signal of every save that gives up, while the shared change stays stalled", async () => {
257257
const { hub, releases } = createHub()
258-
// The registrations of the saves are kept in sets: what is added to them, and taken out, is watched.
259-
const isCaller = (value: unknown) =>
260-
typeof value === "object" &&
261-
value !== null &&
262-
"resolve" in value &&
263-
"reject" in value &&
264-
typeof value.resolve === "function"
265-
const added = vi.spyOn(Set.prototype, "add")
266-
const deleted = vi.spyOn(Set.prototype, "delete")
267-
try {
268-
void changeMcpEnabled(hub, true) // stalled
269-
const staying = changeMcpEnabled(hub, false) // a save that stays: it waits for the change behind it
270-
271-
for (let i = 0; i < 200; i++) {
272-
const giveUp = new AbortController()
273-
const waiting = changeMcpEnabled(hub, i % 2 === 0, giveUp.signal) // joins the running or the waiting one
274-
const rejected = expect(waiting).rejects.toThrow("gave up")
275-
giveUp.abort(new Error("gave up"))
276-
await rejected
277-
}
278-
279-
const registered = added.mock.calls.map(([value]) => value).filter(isCaller)
280-
const taken = new Set(deleted.mock.calls.map(([value]) => value).filter(isCaller))
281-
// The 200 that gave up are all taken off; only the save that stayed (and the first call) are still registered.
282-
expect(registered.length).toBeGreaterThanOrEqual(200)
283-
expect(registered.filter((caller) => !taken.has(caller)).length).toBeLessThanOrEqual(2)
284-
285-
// And the change they gave up on goes on for the save that stayed.
286-
releases[0]()
287-
await vi.waitFor(() => expect(releases).toHaveLength(2))
288-
releases[1]()
289-
await expect(staying).resolves.toBeUndefined()
290-
expect(hub.handleMcpEnabledChange.mock.calls).toEqual([[true], [false]])
291-
} finally {
292-
added.mockRestore()
293-
deleted.mockRestore()
258+
void changeMcpEnabled(hub, true) // stalled
259+
const staying = changeMcpEnabled(hub, false) // a save that stays: it waits for the change behind it
260+
261+
for (let i = 0; i < 200; i++) {
262+
const giveUp = new AbortController()
263+
const add = vi.spyOn(giveUp.signal, "addEventListener")
264+
const remove = vi.spyOn(giveUp.signal, "removeEventListener")
265+
const waiting = changeMcpEnabled(hub, i % 2 === 0, giveUp.signal) // joins the running or the waiting one
266+
const rejected = expect(waiting).rejects.toThrow("gave up")
267+
giveUp.abort(new Error("gave up"))
268+
await rejected
269+
270+
// The listener that the save put on its signal is the one that was taken off again.
271+
const added = add.mock.calls.filter(([type]) => type === "abort").map(([, listener]) => listener)
272+
const removed = remove.mock.calls.filter(([type]) => type === "abort").map(([, listener]) => listener)
273+
expect(added).toHaveLength(1)
274+
expect(removed).toEqual(added)
294275
}
276+
277+
// And the change they gave up on goes on for the save that stayed.
278+
releases[0]()
279+
await vi.waitFor(() => expect(releases).toHaveLength(2))
280+
releases[1]()
281+
await expect(staying).resolves.toBeUndefined()
282+
expect(hub.handleMcpEnabledChange.mock.calls).toEqual([[true], [false]])
295283
})
296284

297285
it("answers a save that gives up at once, while another save still waits for the same change, which goes on", async () => {

‎src/core/webview/mcpEnabledChanges.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function register(change: Waited, signal?: AbortSignal, onGiveUp?: () => void):
7979
const caller: Caller = { resolve, reject }
8080
if (signal) {
8181
const giveUp = () => {
82+
signal.removeEventListener("abort", giveUp) // the signal keeps no reference to this save any more
8283
change.callers.delete(caller)
8384
onGiveUp?.()
8485
reject(signal.reason)

0 commit comments

Comments
 (0)