惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Martin Fowler
Martin Fowler
云风的 BLOG
云风的 BLOG
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
The Blog of Author Tim Ferriss
大猫的无限游戏
大猫的无限游戏
A
About on SuperTechFans
小众软件
小众软件
博客园_首页
博客园 - 聂微东
罗磊的独立博客
Recent Announcements
Recent Announcements
U
Unit 42
N
Netflix TechBlog - Medium
Blog — PlanetScale
Blog — PlanetScale
阮一峰的网络日志
阮一峰的网络日志
博客园 - 叶小钗
V
V2EX
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
IT之家
IT之家
Stack Overflow Blog
Stack Overflow Blog
博客园 - Franky
D
DataBreaches.Net
Last Week in AI
Last Week in AI

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(telegram): stabilize topic dispatch runtime · opencla...
steipete · 2026-05-04 · via Recent Commits to openclaw:main

@@ -1,6 +1,7 @@

11

import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";

22

import type { OpenClawConfig } from "../../config/config.js";

334+

const getLoadedChannelPluginMock = vi.hoisted(() => vi.fn());

45

const getChannelPluginMock = vi.hoisted(() => vi.fn());

56

const getActivePluginChannelRegistryVersionMock = vi.hoisted(() => vi.fn());

67

@@ -15,7 +16,11 @@ let normalizeTargetForProvider: TargetNormalizationModule["normalizeTargetForPro

1516

let resetTargetNormalizerCacheForTests: TargetNormalizationModule["__testing"]["resetTargetNormalizerCacheForTests"];

16171718

vi.mock("../../channels/plugins/registry-loaded-read.js", () => ({

18-

getLoadedChannelPluginForRead: (...args: unknown[]) => getChannelPluginMock(...args),

19+

getLoadedChannelPluginForRead: (...args: unknown[]) => getLoadedChannelPluginMock(...args),

20+

}));

21+22+

vi.mock("../../channels/plugins/index.js", () => ({

23+

getChannelPlugin: (...args: unknown[]) => getChannelPluginMock(...args),

1924

}));

20252126

vi.mock("../../plugins/runtime.js", () => ({

@@ -38,6 +43,7 @@ beforeAll(async () => {

3843

});

39444045

beforeEach(() => {

46+

getLoadedChannelPluginMock.mockReset();

4147

getChannelPluginMock.mockReset();

4248

getActivePluginChannelRegistryVersionMock.mockReset();

4349

resetTargetNormalizerCacheForTests();

@@ -58,6 +64,7 @@ describe("normalizeTargetForProvider", () => {

5864

{

5965

provider: "unknown",

6066

setup: () => {

67+

getLoadedChannelPluginMock.mockReturnValueOnce(undefined);

6168

getChannelPluginMock.mockReturnValueOnce(undefined);

6269

},

6370

expected: "raw-id",

@@ -66,6 +73,7 @@ describe("normalizeTargetForProvider", () => {

6673

provider: "alpha",

6774

setup: () => {

6875

getActivePluginChannelRegistryVersionMock.mockReturnValueOnce(1);

76+

getLoadedChannelPluginMock.mockReturnValueOnce(undefined);

6977

getChannelPluginMock.mockReturnValueOnce(undefined);

7078

},

7179

expected: "raw-id",

@@ -85,7 +93,7 @@ describe("normalizeTargetForProvider", () => {

8593

.mockReturnValueOnce(10)

8694

.mockReturnValueOnce(10)

8795

.mockReturnValueOnce(11);

88-

getChannelPluginMock

96+

getLoadedChannelPluginMock

8997

.mockReturnValueOnce({

9098

messaging: { normalizeTarget: firstNormalizer },

9199

})

@@ -97,14 +105,30 @@ describe("normalizeTargetForProvider", () => {

97105

expect(normalizeTargetForProvider("alpha", " def ")).toBe("DEF");

98106

expect(normalizeTargetForProvider("alpha", " ghi ")).toBe("next:ghi");

99107100-

expect(getChannelPluginMock).toHaveBeenCalledTimes(2);

108+

expect(getLoadedChannelPluginMock).toHaveBeenCalledTimes(2);

109+

expect(getChannelPluginMock).not.toHaveBeenCalled();

101110

expect(firstNormalizer).toHaveBeenCalledTimes(2);

102111

expect(secondNormalizer).toHaveBeenCalledTimes(1);

103112

});

104113114+

it("uses bundled/catalog target normalization when the channel is not loaded", () => {

115+

getActivePluginChannelRegistryVersionMock.mockReturnValueOnce(30);

116+

getLoadedChannelPluginMock.mockReturnValueOnce(undefined);

117+

getChannelPluginMock.mockReturnValueOnce({

118+

messaging: {

119+

normalizeTarget: (raw: string) =>

120+

raw.trim() === "-1001234567890:topic:42" ? "telegram:-1001234567890:topic:42" : undefined,

121+

},

122+

});

123+124+

expect(normalizeTargetForProvider("telegram", " -1001234567890:topic:42 ")).toBe(

125+

"telegram:-1001234567890:topic:42",

126+

);

127+

});

128+105129

it("returns undefined when the provider normalizer resolves to an empty value", () => {

106130

getActivePluginChannelRegistryVersionMock.mockReturnValueOnce(20);

107-

getChannelPluginMock.mockReturnValueOnce({

131+

getLoadedChannelPluginMock.mockReturnValueOnce({

108132

messaging: {

109133

normalizeTarget: () => "",

110134

},

@@ -121,7 +145,7 @@ describe("resolveNormalizedTargetInput", () => {

121145122146

it("returns raw and normalized values", () => {

123147

getActivePluginChannelRegistryVersionMock.mockReturnValueOnce(1);

124-

getChannelPluginMock.mockReturnValueOnce({

148+

getLoadedChannelPluginMock.mockReturnValueOnce({

125149

messaging: {

126150

normalizeTarget: (raw: string) => raw.trim().toUpperCase(),

127151

},

@@ -137,7 +161,7 @@ describe("resolveNormalizedTargetInput", () => {

137161

describe("looksLikeTargetId", () => {

138162

it("uses plugin looksLikeId when available", () => {

139163

const pluginLooksLikeId = vi.fn((raw: string, normalized: string) => raw !== normalized);

140-

getChannelPluginMock.mockReturnValueOnce({

164+

getLoadedChannelPluginMock.mockReturnValueOnce({

141165

messaging: {

142166

targetResolver: {

143167

looksLikeId: pluginLooksLikeId,

@@ -158,17 +182,38 @@ describe("looksLikeTargetId", () => {

158182

it.each(["channel:C123", "@alice", "#general", "+15551234567", "conversation:abc", "foo@thread"])(

159183

"falls back to built-in id-like heuristics for %s",

160184

(raw) => {

185+

getLoadedChannelPluginMock.mockReturnValueOnce(undefined);

161186

getChannelPluginMock.mockReturnValueOnce(undefined);

162187

expect(looksLikeTargetId({ channel: "workspace", raw })).toBe(true);

163188

},

164189

);

190+191+

it("uses bundled/catalog target id detection when the channel is not loaded", () => {

192+

getLoadedChannelPluginMock.mockReturnValueOnce(undefined);

193+

getChannelPluginMock.mockReturnValueOnce({

194+

messaging: {

195+

targetResolver: {

196+

looksLikeId: (raw: string, normalized?: string) =>

197+

raw === "-1001234567890:topic:42" && normalized === "telegram:-1001234567890:topic:42",

198+

},

199+

},

200+

});

201+202+

expect(

203+

looksLikeTargetId({

204+

channel: "telegram",

205+

raw: "-1001234567890:topic:42",

206+

normalized: "telegram:-1001234567890:topic:42",

207+

}),

208+

).toBe(true);

209+

});

165210

});

166211167212

describe("maybeResolvePluginMessagingTarget", () => {

168213

const cfg = {} as OpenClawConfig;

169214170215

it("returns undefined when requireIdLike is set and the target is not id-like", async () => {

171-

getChannelPluginMock.mockReturnValueOnce({

216+

getLoadedChannelPluginMock.mockReturnValueOnce({

172217

messaging: {

173218

targetResolver: {

174219

looksLikeId: () => false,

@@ -194,7 +239,7 @@ describe("maybeResolvePluginMessagingTarget", () => {

194239

kind: "group",

195240

display: "general",

196241

});

197-

getChannelPluginMock

242+

getLoadedChannelPluginMock

198243

.mockReturnValueOnce({

199244

messaging: {

200245

normalizeTarget: (raw: string) => raw.trim().toUpperCase(),

@@ -234,7 +279,7 @@ describe("maybeResolvePluginMessagingTarget", () => {

234279

describe("buildTargetResolverSignature", () => {

235280

it("builds stable signatures from resolver hint and looksLikeId source", () => {

236281

const looksLikeId = (value: string) => value.startsWith("C");

237-

getChannelPluginMock.mockReturnValueOnce({

282+

getLoadedChannelPluginMock.mockReturnValueOnce({

238283

messaging: {

239284

targetResolver: {

240285

hint: "Use channel id",

@@ -244,7 +289,7 @@ describe("buildTargetResolverSignature", () => {

244289

});

245290246291

const first = buildTargetResolverSignature("workspace");

247-

getChannelPluginMock.mockReturnValueOnce({

292+

getLoadedChannelPluginMock.mockReturnValueOnce({

248293

messaging: {

249294

targetResolver: {

250295

hint: "Use channel id",

@@ -258,7 +303,7 @@ describe("buildTargetResolverSignature", () => {

258303

});

259304260305

it("changes when resolver metadata changes", () => {

261-

getChannelPluginMock.mockReturnValueOnce({

306+

getLoadedChannelPluginMock.mockReturnValueOnce({

262307

messaging: {

263308

targetResolver: {

264309

hint: "Use channel id",

@@ -268,7 +313,7 @@ describe("buildTargetResolverSignature", () => {

268313

});

269314

const first = buildTargetResolverSignature("workspace");

270315271-

getChannelPluginMock.mockReturnValueOnce({

316+

getLoadedChannelPluginMock.mockReturnValueOnce({

272317

messaging: {

273318

targetResolver: {

274319

hint: "Use user id",