Skip to content

Commit ff5354e

Browse files
clawsweeper[bot]HCLTakhoffman
authored
fix(twitch): export clearRegistryForTest for cross-test isolation (#83887) (#84309)
Summary: - The PR adds an async test-only Twitch client-manager registry reset helper, a focused registry isolation test, and an Unreleased changelog entry. - Reproducibility: yes. Source inspection shows getOrCreateClientManager() returns the cached module-level manager for the same account id, and the repo’s Vitest configuration is explicitly non-isolated. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(twitch): export clearRegistryForTest for cross-test isolation (#8… Validation: - ClawSweeper review passed for head 38c3fad. - Required merge gates passed before the squash merge. Prepared head SHA: 38c3fad Review: #84309 (comment) Co-authored-by: HCL <chenglunhu@gmail.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
1 parent 00da318 commit ff5354e

3 files changed

Lines changed: 59 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
1414
- CLI/message: include a stable top-level `messageId` in `openclaw message --json` output when channel sends return one. (#84191) Thanks @100menotu001.
1515
- Plugins/hooks: apply a default 30-second timeout to `before_compaction` and `after_compaction` hooks so a hung plugin handler no longer blocks compaction completion. (#84153)
1616
- Discord: preserve disabled presentation buttons when adapting and rendering Discord message controls. (#84188) Thanks @100menotu001.
17+
- Twitch: add a test-only client-manager registry reset helper so non-isolated Twitch tests can clear cached managers between cases. Fixes #83887. (#84244) Thanks @hclsys.
1718
- Plugins/perf: thread explicit plugin discovery results through `loadBundledCapabilityRuntimeRegistry`, `resolveBundledPluginSources`, and `listChannelCatalogEntries` so callers that already hold a discovery result skip redundant filesystem walks. Thanks @SebTardif.
1819
- harden update restart script creation [AI]. (#84088) Thanks @pgondhi987.
1920
- Docker: keep the bundled Codex plugin in official release image keep lists so the default OpenAI agent harness remains available after Docker pruning. Fixes #83613. (#83626) Thanks @YuanHanzhong.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { afterEach, describe, expect, it, vi } from "vitest";
2+
import {
3+
clearRegistryForTest,
4+
getClientManager,
5+
getOrCreateClientManager,
6+
} from "./client-manager-registry.js";
7+
import type { ChannelLogSink } from "./types.js";
8+
9+
function makeLogger(): ChannelLogSink {
10+
return {
11+
info: vi.fn(),
12+
warn: vi.fn(),
13+
error: vi.fn(),
14+
debug: vi.fn(),
15+
};
16+
}
17+
18+
describe("client manager registry", () => {
19+
afterEach(async () => {
20+
await clearRegistryForTest();
21+
});
22+
23+
it("clears cached managers for hot module test isolation", async () => {
24+
const firstManager = getOrCreateClientManager("default", makeLogger());
25+
const disconnectAll = vi.spyOn(firstManager, "disconnectAll");
26+
27+
expect(getClientManager("default")).toBe(firstManager);
28+
expect(getOrCreateClientManager("default", makeLogger())).toBe(firstManager);
29+
30+
await clearRegistryForTest();
31+
32+
expect(disconnectAll).toHaveBeenCalledOnce();
33+
expect(getClientManager("default")).toBeUndefined();
34+
expect(getOrCreateClientManager("default", makeLogger())).not.toBe(firstManager);
35+
});
36+
});

extensions/twitch/src/client-manager-registry.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,25 @@ export async function removeClientManager(accountId: string): Promise<void> {
8585
registry.delete(accountId);
8686
entry.logger.info(`Unregistered client manager for account: ${accountId}`);
8787
}
88+
89+
/**
90+
* Test-only: clear the module-level registry of all client manager entries.
91+
*
92+
* Mirrors the `clearForTest` escape hatch on `TwitchClientManager`. Without
93+
* this, the module-level `registry` Map survives across tests when vitest
94+
* is run with `--isolate=false` (or any harness that does not tear the
95+
* module graph down between cases), and a stale entry from one test will
96+
* shadow `getOrCreateClientManager` calls in subsequent tests, silently
97+
* handing back another test's mocked logger/manager. See #83887.
98+
*
99+
* Production code MUST NOT call this. It disconnects cached managers before
100+
* clearing the registry so tests do not leave handlers or clients behind.
101+
*/
102+
export async function clearRegistryForTest(): Promise<void> {
103+
const entries = [...registry.values()];
104+
try {
105+
await Promise.all(entries.map((entry) => entry.manager.disconnectAll()));
106+
} finally {
107+
registry.clear();
108+
}
109+
}

0 commit comments

Comments
 (0)