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

推荐订阅源

IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
A
About on SuperTechFans
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog RSS Feed
U
Unit 42
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
雷峰网
雷峰网
罗磊的独立博客
Microsoft Security Blog
Microsoft Security Blog
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
P
Proofpoint News Feed
The Cloudflare Blog
D
Docker
大猫的无限游戏
大猫的无限游戏

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
test: clear manifest registry broad matchers · openclaw/o...
steipete · 2026-05-10 · via Recent Commits to openclaw:main

@@ -119,9 +119,63 @@ function expectRegistryDiagnosticContains(

119119

registry: ReturnType<typeof loadPluginManifestRegistry>,

120120

fragment: string,

121121

) {

122-

expect(registry.diagnostics.map((diag) => diag.message)).toEqual(

123-

expect.arrayContaining([expect.stringContaining(fragment)]),

124-

);

122+

expect(registry.diagnostics.some((diag) => diag.message.includes(fragment))).toBe(true);

123+

}

124+125+

function expectNoRegistryDiagnosticContains(

126+

registry: ReturnType<typeof loadPluginManifestRegistry>,

127+

fragment: string,

128+

) {

129+

expect(registry.diagnostics.some((diag) => diag.message.includes(fragment))).toBe(false);

130+

}

131+132+

function requireRecord(value: unknown, label: string): Record<string, unknown> {

133+

expect(

134+

typeof value === "object" && value !== null && !Array.isArray(value),

135+

`${label} object`,

136+

).toBe(true);

137+

return value as Record<string, unknown>;

138+

}

139+140+

function expectRecordFields(

141+

value: unknown,

142+

label: string,

143+

expected: Record<string, unknown>,

144+

): Record<string, unknown> {

145+

const record = requireRecord(value, label);

146+

for (const [key, expectedValue] of Object.entries(expected)) {

147+

expect(record[key], `${label}.${key}`).toEqual(expectedValue);

148+

}

149+

return record;

150+

}

151+152+

function expectArrayIncludesAll(value: unknown, expected: readonly unknown[], label: string) {

153+

expect(Array.isArray(value), `${label} array`).toBe(true);

154+

for (const item of expected) {

155+

expect(value as unknown[], `${label} item ${String(item)}`).toContain(item);

156+

}

157+

}

158+159+

function expectDiagnosticFields(

160+

registry: ReturnType<typeof loadPluginManifestRegistry>,

161+

expected: { level?: string; pluginId?: string; source?: string; messageIncludes?: string },

162+

) {

163+

const diagnostic = registry.diagnostics.find((entry) => {

164+

if (expected.level && entry.level !== expected.level) {

165+

return false;

166+

}

167+

if (expected.pluginId && entry.pluginId !== expected.pluginId) {

168+

return false;

169+

}

170+

if (expected.source && entry.source !== expected.source) {

171+

return false;

172+

}

173+

if (expected.messageIncludes && !entry.message.includes(expected.messageIncludes)) {

174+

return false;

175+

}

176+

return true;

177+

});

178+

expect(diagnostic, `diagnostic ${expected.messageIncludes ?? ""}`).toBeDefined();

125179

}

126180127181

function prepareLinkedManifestFixture(params: { id: string; mode: "symlink" | "hardlink" }): {

@@ -455,17 +509,13 @@ describe("loadPluginManifestRegistry", () => {

455509

config: { plugins: { entries: { "external-chat": { enabled: false } } } },

456510

candidates: [candidate],

457511

});

458-

expect(disabledRegistry.diagnostics.map((diagnostic) => diagnostic.message)).not.toEqual(

459-

expect.arrayContaining([expect.stringContaining("without channelConfigs metadata")]),

460-

);

512+

expectNoRegistryDiagnosticContains(disabledRegistry, "without channelConfigs metadata");

461513462514

const allowlistRegistry = loadPluginManifestRegistry({

463515

config: { plugins: { allow: ["other-plugin"] } },

464516

candidates: [candidate],

465517

});

466-

expect(allowlistRegistry.diagnostics.map((diagnostic) => diagnostic.message)).not.toEqual(

467-

expect.arrayContaining([expect.stringContaining("without channelConfigs metadata")]),

468-

);

518+

expectNoRegistryDiagnosticContains(allowlistRegistry, "without channelConfigs metadata");

469519

});

470520471521

it("suppresses duplicate warnings for explicit installed globals overriding bundled plugins", () => {

@@ -590,12 +640,10 @@ describe("loadPluginManifestRegistry", () => {

590640

});

591641592642

expect(registry.plugins).toHaveLength(1);

593-

expect(registry.plugins[0]).toEqual(

594-

expect.objectContaining({

595-

origin: "config",

596-

trustedOfficialInstall: true,

597-

}),

598-

);

643+

expectRecordFields(registry.plugins[0], "plugin", {

644+

origin: "config",

645+

trustedOfficialInstall: true,

646+

});

599647

});

600648601649

it("does not trust unrecorded globals that spoof official ids", () => {

@@ -1064,16 +1112,12 @@ describe("loadPluginManifestRegistry", () => {

10641112

expect(registry.plugins[0]?.providerAuthEnvVars).toEqual({

10651113

openai: ["OPENAI_API_KEY"],

10661114

});

1067-

expect(registry.diagnostics).toContainEqual(

1068-

expect.objectContaining({

1069-

level: "warn",

1070-

pluginId: "external-openai",

1071-

source: path.join(dir, "openclaw.plugin.json"),

1072-

message: expect.stringContaining(

1073-

"providerAuthEnvVars is deprecated compatibility metadata",

1074-

),

1075-

}),

1076-

);

1115+

expectDiagnosticFields(registry, {

1116+

level: "warn",

1117+

pluginId: "external-openai",

1118+

source: path.join(dir, "openclaw.plugin.json"),

1119+

messageIncludes: "providerAuthEnvVars is deprecated compatibility metadata",

1120+

});

10771121

});

1078112210791123

it("does not report deprecated providerAuthEnvVars when setup providers mirror env vars", () => {

@@ -1096,12 +1140,9 @@ describe("loadPluginManifestRegistry", () => {

10961140

origin: "global",

10971141

});

109811421099-

expect(registry.diagnostics).not.toContainEqual(

1100-

expect.objectContaining({

1101-

message: expect.stringContaining(

1102-

"providerAuthEnvVars is deprecated compatibility metadata",

1103-

),

1104-

}),

1143+

expectNoRegistryDiagnosticContains(

1144+

registry,

1145+

"providerAuthEnvVars is deprecated compatibility metadata",

11051146

);

11061147

});

11071148

@@ -1148,14 +1189,12 @@ describe("loadPluginManifestRegistry", () => {

11481189

});

1149119011501191

expect(registry.plugins[0]?.channels).toEqual(["external-chat"]);

1151-

expect(registry.diagnostics).toContainEqual(

1152-

expect.objectContaining({

1153-

level: "warn",

1154-

pluginId: "external-chat",

1155-

source: path.join(dir, "openclaw.plugin.json"),

1156-

message: expect.stringContaining("without channelConfigs metadata"),

1157-

}),

1158-

);

1192+

expectDiagnosticFields(registry, {

1193+

level: "warn",

1194+

pluginId: "external-chat",

1195+

source: path.join(dir, "openclaw.plugin.json"),

1196+

messageIncludes: "without channelConfigs metadata",

1197+

});

11591198

});

1160119911611200

it("sanitizes manifest-controlled fields in channel config descriptor diagnostics", () => {

@@ -1208,13 +1247,11 @@ describe("loadPluginManifestRegistry", () => {

12081247

origin: "global",

12091248

});

121012491211-

expect(registry.plugins[0]?.channelConfigs?.["external-chat"]?.schema).toMatchObject({

1250+

expectRecordFields(registry.plugins[0]?.channelConfigs?.["external-chat"]?.schema, "schema", {

12121251

type: "object",

12131252

additionalProperties: false,

12141253

});

1215-

expect(registry.diagnostics.map((diagnostic) => diagnostic.message)).not.toEqual(

1216-

expect.arrayContaining([expect.stringContaining("without channelConfigs metadata")]),

1217-

);

1254+

expectNoRegistryDiagnosticContains(registry, "without channelConfigs metadata");

12181255

});

1219125612201257

it("hydrates supplemental official external catalog contracts for lagging npm manifests", () => {

@@ -1235,17 +1272,15 @@ describe("loadPluginManifestRegistry", () => {

12351272

]);

1236127312371274

expect(registry.plugins[0]?.contracts?.tools).toEqual(["wecom_mcp"]);

1238-

expect(registry.plugins[0]?.channelConfigs?.wecom).toEqual(

1239-

expect.objectContaining({

1275+

const wecomConfig = expectRecordFields(

1276+

registry.plugins[0]?.channelConfigs?.wecom,

1277+

"wecom config",

1278+

{

12401279

label: "WeCom",

1241-

schema: expect.objectContaining({

1242-

type: "object",

1243-

}),

1244-

}),

1245-

);

1246-

expect(registry.diagnostics.map((diagnostic) => diagnostic.message)).not.toEqual(

1247-

expect.arrayContaining([expect.stringContaining("without channelConfigs metadata")]),

1280+

},

12481281

);

1282+

expectRecordFields(wecomConfig.schema, "wecom schema", { type: "object" });

1283+

expectNoRegistryDiagnosticContains(registry, "without channelConfigs metadata");

12491284

});

1250128512511286

it("fills missing official external catalog descriptors for partial npm channel configs", () => {

@@ -1276,18 +1311,20 @@ describe("loadPluginManifestRegistry", () => {

12761311

}),

12771312

]);

127813131279-

expect(registry.plugins[0]?.channelConfigs?.wecom).toEqual(

1280-

expect.objectContaining({

1314+

const wecomConfig = expectRecordFields(

1315+

registry.plugins[0]?.channelConfigs?.wecom,

1316+

"wecom config",

1317+

{

12811318

label: "WeCom",

12821319

description: "Enterprise WeChat conversation channel.",

1283-

schema: expect.objectContaining({

1284-

additionalProperties: false,

1285-

properties: {

1286-

corpId: { type: "string" },

1287-

},

1288-

}),

1289-

}),

1320+

},

12901321

);

1322+

expectRecordFields(wecomConfig.schema, "wecom schema", {

1323+

additionalProperties: false,

1324+

properties: {

1325+

corpId: { type: "string" },

1326+

},

1327+

});

12911328

});

1292132912931330

it("drops prototype-polluting channel config keys from plugin manifests", () => {

@@ -1338,7 +1375,7 @@ describe("loadPluginManifestRegistry", () => {

13381375

expect(Object.prototype.hasOwnProperty.call(channelConfigs, "__proto__")).toBe(false);

13391376

expect(Object.prototype.hasOwnProperty.call(channelConfigs, "constructor")).toBe(false);

13401377

expect(Object.prototype.hasOwnProperty.call(channelConfigs, "prototype")).toBe(false);

1341-

expect(channelConfigs["safe-chat"]?.schema).toMatchObject({

1378+

expectRecordFields(channelConfigs["safe-chat"]?.schema, "safe-chat schema", {

13421379

type: "object",

13431380

additionalProperties: false,

13441381

});

@@ -1754,13 +1791,11 @@ describe("loadPluginManifestRegistry", () => {

17541791

}),

17551792

]);

175617931757-

expect(registry.plugins[0]?.channelConfigs?.telegram).toEqual(

1758-

expect.objectContaining({

1759-

schema: expect.objectContaining({

1760-

type: "object",

1761-

}),

1762-

}),

1794+

const telegramConfig = requireRecord(

1795+

registry.plugins[0]?.channelConfigs?.telegram,

1796+

"telegram config",

17631797

);

1798+

expectRecordFields(telegramConfig.schema, "telegram schema", { type: "object" });

17641799

});

1765180017661801

it("preserves manifest-owned config contracts from plugin manifests", () => {

@@ -1935,9 +1970,7 @@ describe("loadPluginManifestRegistry", () => {

19351970

});

1936197119371972

expect(registry.plugins.map((plugin) => plugin.id)).toEqual(["codex"]);

1938-

expect(registry.diagnostics.map((diag) => diag.message)).not.toEqual(

1939-

expect.arrayContaining([expect.stringContaining("openclaw.install.minHostVersion must use")]),

1940-

);

1973+

expectNoRegistryDiagnosticContains(registry, "openclaw.install.minHostVersion must use");

19411974

});

1942197519431976

it("does not runtime-gate bundled source plugins by install minHostVersion", () => {

@@ -1963,9 +1996,7 @@ describe("loadPluginManifestRegistry", () => {

19631996

});

1964199719651998

expect(registry.plugins.map((plugin) => plugin.id)).toContain("codex");

1966-

expect(registry.diagnostics.map((diag) => diag.message)).not.toEqual(

1967-

expect.arrayContaining([expect.stringContaining("requires OpenClaw")]),

1968-

);

1999+

expectNoRegistryDiagnosticContains(registry, "requires OpenClaw");

19692000

});

1970200119712002

it.each([

@@ -2115,8 +2146,8 @@ describe("loadPluginManifestRegistry", () => {

21152146

bundleFormat: "codex",

21162147

hooks: ["hooks"],

21172148

skills: ["skills"],

2118-

bundleCapabilities: expect.arrayContaining(["hooks", "skills"]),

21192149

},

2150+

expectedCapabilities: ["hooks", "skills"],

21202151

},

21212152

{

21222153

name: "loads Claude bundle manifests with command roots and settings files",

@@ -2143,8 +2174,8 @@ describe("loadPluginManifestRegistry", () => {

21432174

bundleFormat: "claude",

21442175

skills: ["skill-packs/starter", "commands-pack"],

21452176

settingsFiles: ["settings.json"],

2146-

bundleCapabilities: expect.arrayContaining(["skills", "commands", "settings"]),

21472177

},

2178+

expectedCapabilities: ["skills", "commands", "settings"],

21482179

},

21492180

{

21502181

name: "loads manifestless Claude bundles into the registry",

@@ -2164,8 +2195,8 @@ describe("loadPluginManifestRegistry", () => {

21642195

bundleFormat: "claude",

21652196

skills: ["commands"],

21662197

settingsFiles: ["settings.json"],

2167-

bundleCapabilities: expect.arrayContaining(["skills", "commands", "settings"]),

21682198

},

2199+

expectedCapabilities: ["skills", "commands", "settings"],

21692200

},

21702201

{

21712202

name: "loads Cursor bundle manifests into the registry",

@@ -2191,24 +2222,23 @@ describe("loadPluginManifestRegistry", () => {

21912222

format: "bundle",

21922223

bundleFormat: "cursor",

21932224

skills: ["skills", ".cursor/commands"],

2194-

bundleCapabilities: expect.arrayContaining([

2195-

"skills",

2196-

"commands",

2197-

"rules",

2198-

"hooks",

2199-

"mcpServers",

2200-

]),

22012225

},

2226+

expectedCapabilities: ["skills", "commands", "rules", "hooks", "mcpServers"],

22022227

},

2203-

] as const)("$name", ({ idHint, bundleFormat, setup, expected }) => {

2228+

] as const)("$name", ({ idHint, bundleFormat, setup, expected, expectedCapabilities }) => {

22042229

const registry = loadBundleRegistry({

22052230

idHint,

22062231

bundleFormat,

22072232

setup,

22082233

});

2209223422102235

expect(registry.plugins).toHaveLength(1);

2211-

expect(registry.plugins[0]).toMatchObject(expected);

2236+

expectRecordFields(registry.plugins[0], "bundle plugin", expected);

2237+

expectArrayIncludesAll(

2238+

registry.plugins[0]?.bundleCapabilities,

2239+

expectedCapabilities,

2240+

"bundle capabilities",

2241+

);

22122242

});

2213224322142244

it("prefers higher-precedence origins for the same physical directory (config > workspace > global > bundled)", () => {

@@ -2376,12 +2406,8 @@ describe("loadPluginManifestRegistry", () => {

23762406

});

2377240723782408

expect(olderHost.plugins).toStrictEqual([]);

2379-

expect(olderHost.diagnostics.map((diag) => diag.message)).toEqual(

2380-

expect.arrayContaining([expect.stringContaining("this host is 2026.3.21")]),

2381-

);

2409+

expectRegistryDiagnosticContains(olderHost, "this host is 2026.3.21");

23822410

expect(newerHost.plugins.map((plugin) => plugin.id)).toContain("synology-chat");

2383-

expect(newerHost.diagnostics.map((diag) => diag.message)).not.toEqual(

2384-

expect.arrayContaining([expect.stringContaining("this host is 2026.3.21")]),

2385-

);

2411+

expectNoRegistryDiagnosticContains(newerHost, "this host is 2026.3.21");

23862412

});

23872413

});