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 38c3fadc91.
- Required merge gates passed before the squash merge.

Prepared head SHA: 38c3fadc91
Review: https://github.com/openclaw/openclaw/pull/84309#issuecomment-4491930986

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>
This commit is contained in:
clawsweeper[bot]
2026-05-20 00:26:21 +00:00
committed by GitHub
parent 00da318350
commit ff5354ee4f
3 changed files with 59 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
- CLI/message: include a stable top-level `messageId` in `openclaw message --json` output when channel sends return one. (#84191) Thanks @100menotu001.
- 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)
- Discord: preserve disabled presentation buttons when adapting and rendering Discord message controls. (#84188) Thanks @100menotu001.
- 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.
- 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.
- harden update restart script creation [AI]. (#84088) Thanks @pgondhi987.
- 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.

View File

@@ -0,0 +1,36 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import {
clearRegistryForTest,
getClientManager,
getOrCreateClientManager,
} from "./client-manager-registry.js";
import type { ChannelLogSink } from "./types.js";
function makeLogger(): ChannelLogSink {
return {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
debug: vi.fn(),
};
}
describe("client manager registry", () => {
afterEach(async () => {
await clearRegistryForTest();
});
it("clears cached managers for hot module test isolation", async () => {
const firstManager = getOrCreateClientManager("default", makeLogger());
const disconnectAll = vi.spyOn(firstManager, "disconnectAll");
expect(getClientManager("default")).toBe(firstManager);
expect(getOrCreateClientManager("default", makeLogger())).toBe(firstManager);
await clearRegistryForTest();
expect(disconnectAll).toHaveBeenCalledOnce();
expect(getClientManager("default")).toBeUndefined();
expect(getOrCreateClientManager("default", makeLogger())).not.toBe(firstManager);
});
});

View File

@@ -85,3 +85,25 @@ export async function removeClientManager(accountId: string): Promise<void> {
registry.delete(accountId);
entry.logger.info(`Unregistered client manager for account: ${accountId}`);
}
/**
* Test-only: clear the module-level registry of all client manager entries.
*
* Mirrors the `clearForTest` escape hatch on `TwitchClientManager`. Without
* this, the module-level `registry` Map survives across tests when vitest
* is run with `--isolate=false` (or any harness that does not tear the
* module graph down between cases), and a stale entry from one test will
* shadow `getOrCreateClientManager` calls in subsequent tests, silently
* handing back another test's mocked logger/manager. See #83887.
*
* Production code MUST NOT call this. It disconnects cached managers before
* clearing the registry so tests do not leave handlers or clients behind.
*/
export async function clearRegistryForTest(): Promise<void> {
const entries = [...registry.values()];
try {
await Promise.all(entries.map((entry) => entry.manager.disconnectAll()));
} finally {
registry.clear();
}
}