




















@@ -86,6 +86,8 @@ let configIo: typeof import("../config/io.js");
8686let configMutate: typeof import("../config/mutate.js");
87878888const configPath = "/tmp/openclaw-startup-recovery.json";
89+const telegramAutoEnableChange = "Telegram configured, enabled automatically.";
90+const runtimeOnlyAutoEnableLog = `gateway: auto-enabled plugins for this runtime without writing config:\n- ${telegramAutoEnableChange}`;
8991const validConfig = {
9092gateway: {
9193mode: "local",
@@ -163,6 +165,58 @@ function mockStartupSnapshot(snapshot: ConfigFileSnapshot) {
163165});
164166}
165167168+async function expectStartupResult(params: {
169+snapshot: ConfigFileSnapshot;
170+log?: ReturnType<typeof testStartupLog>;
171+initialSnapshotRead?: Parameters<
172+typeof loadGatewayStartupConfigSnapshot
173+>[0]["initialSnapshotRead"];
174+}) {
175+await expect(
176+loadTestStartup({
177+minimalTestGateway: false,
178+log: params.log,
179+initialSnapshotRead: params.initialSnapshotRead,
180+}),
181+).resolves.toEqual({
182+snapshot: params.snapshot,
183+wroteConfig: false,
184+ pluginMetadataSnapshot,
185+});
186+}
187+188+function expectPluginAutoEnableFor(config: OpenClawConfig) {
189+expect(applyPluginAutoEnable).toHaveBeenCalledWith({
190+ config,
191+env: process.env,
192+manifestRegistry: pluginManifestRegistry,
193+});
194+}
195+196+function mockRuntimeAutoEnable(config: OpenClawConfig) {
197+applyPluginAutoEnable.mockReturnValueOnce({
198+ config,
199+changes: [telegramAutoEnableChange],
200+autoEnabledReasons: {},
201+});
202+}
203+204+function expectRuntimeOnlyAutoEnableLogged(log: ReturnType<typeof testStartupLog>) {
205+expect(log.info).toHaveBeenCalledWith(runtimeOnlyAutoEnableLog);
206+expect(log.warn).not.toHaveBeenCalled();
207+}
208+209+function withRuntimeConfig(
210+snapshot: ConfigFileSnapshot,
211+runtimeConfig: OpenClawConfig,
212+): ConfigFileSnapshot {
213+return {
214+ ...snapshot,
215+ runtimeConfig,
216+config: runtimeConfig,
217+};
218+}
219+166220function buildInvalidConfigSnapshot(params: {
167221rawConfig: unknown;
168222config?: OpenClawConfig;
@@ -183,6 +237,24 @@ function buildInvalidConfigSnapshot(params: {
183237});
184238}
185239240+function pluginSlotRawConfig(gatewayMode: string) {
241+return {
242+gateway: { mode: gatewayMode },
243+plugins: { slots: { memory: "source-only-pack" } },
244+};
245+}
246+247+function enabledPluginRawConfig(gatewayMode: string) {
248+return {
249+gateway: { mode: gatewayMode },
250+plugins: {
251+entries: {
252+feishu: { enabled: true },
253+},
254+},
255+};
256+}
257+186258function testStartupLog() {
187259return { info: vi.fn(), warn: vi.fn() };
188260}
@@ -278,23 +350,10 @@ describe("gateway startup config validation", () => {
278350mockStartupSnapshot(snapshot);
279351const log = testStartupLog();
280352281-await expect(
282-loadTestStartup({
283-minimalTestGateway: false,
284- log,
285-}),
286-).resolves.toEqual({
287- snapshot,
288-wroteConfig: false,
289- pluginMetadataSnapshot,
290-});
353+await expectStartupResult({ snapshot, log });
291354292355expect(configIo.readConfigFileSnapshotWithPluginMetadata).toHaveBeenCalledTimes(1);
293-expect(applyPluginAutoEnable).toHaveBeenCalledWith({
294-config: sourceConfig,
295-env: process.env,
296-manifestRegistry: pluginManifestRegistry,
297-});
356+expectPluginAutoEnableFor(sourceConfig);
298357expect(configMutate.replaceConfigFile).not.toHaveBeenCalled();
299358expect(log.info).not.toHaveBeenCalled();
300359});
@@ -312,27 +371,17 @@ describe("gateway startup config validation", () => {
312371});
313372const log = testStartupLog();
314373315-await expect(
316-loadTestStartup({
317-minimalTestGateway: false,
318- log,
319-initialSnapshotRead: {
320- snapshot,
321- pluginMetadataSnapshot,
322-},
323-}),
324-).resolves.toEqual({
374+await expectStartupResult({
325375 snapshot,
326-wroteConfig: false,
327- pluginMetadataSnapshot,
376+ log,
377+initialSnapshotRead: {
378+ snapshot,
379+ pluginMetadataSnapshot,
380+},
328381});
329382330383expect(configIo.readConfigFileSnapshotWithPluginMetadata).not.toHaveBeenCalled();
331-expect(applyPluginAutoEnable).toHaveBeenCalledWith({
332-config: validConfig,
333-env: process.env,
334-manifestRegistry: pluginManifestRegistry,
335-});
384+expectPluginAutoEnableFor(validConfig);
336385});
337386338387it("preserves empty model allowlist entries through runtime-only startup auto-enable", async () => {
@@ -367,33 +416,15 @@ describe("gateway startup config validation", () => {
367416} as unknown as OpenClawConfig;
368417const initialSnapshot = buildRuntimeSnapshot(sourceConfig);
369418mockStartupSnapshot(initialSnapshot);
370-applyPluginAutoEnable.mockReturnValueOnce({
371-config: autoEnabledConfig,
372-changes: ["Telegram configured, enabled automatically."],
373-autoEnabledReasons: {},
374-});
419+mockRuntimeAutoEnable(autoEnabledConfig);
375420const log = testStartupLog();
376421377-await expect(
378-loadTestStartup({
379-minimalTestGateway: false,
380- log,
381-}),
382-).resolves.toEqual({
383-snapshot: {
384- ...initialSnapshot,
385-runtimeConfig: autoEnabledConfig,
386-config: autoEnabledConfig,
387-},
388-wroteConfig: false,
389- pluginMetadataSnapshot,
422+await expectStartupResult({
423+snapshot: withRuntimeConfig(initialSnapshot, autoEnabledConfig),
424+ log,
390425});
391426392-expect(applyPluginAutoEnable).toHaveBeenCalledWith({
393-config: sourceConfig,
394-env: process.env,
395-manifestRegistry: pluginManifestRegistry,
396-});
427+expectPluginAutoEnableFor(sourceConfig);
397428expect(configMutate.replaceConfigFile).not.toHaveBeenCalled();
398429expect(configIo.readConfigFileSnapshotWithPluginMetadata).toHaveBeenCalledTimes(1);
399430expect(initialSnapshot.sourceConfig.agents?.defaults?.models).toEqual({
@@ -408,10 +439,7 @@ describe("gateway startup config validation", () => {
408439expect(autoEnabledConfig.channels?.telegram).toEqual({
409440enabled: true,
410441});
411-expect(log.info).toHaveBeenCalledWith(
412-"gateway: auto-enabled plugins for this runtime without writing config:\n- Telegram configured, enabled automatically.",
413-);
414-expect(log.warn).not.toHaveBeenCalled();
442+expectRuntimeOnlyAutoEnableLogged(log);
415443});
416444417445it("keeps plugin auto-enable runtime-only in Nix mode", async () => {
@@ -431,35 +459,18 @@ describe("gateway startup config validation", () => {
431459} as unknown as OpenClawConfig;
432460const snapshot = buildRuntimeSnapshot(sourceConfig);
433461mockStartupSnapshot(snapshot);
434-applyPluginAutoEnable.mockReturnValueOnce({
435-config: autoEnabledConfig,
436-changes: ["Telegram configured, enabled automatically."],
437-autoEnabledReasons: {},
438-});
462+mockRuntimeAutoEnable(autoEnabledConfig);
439463configMocks.isNixMode.value = true;
440464const log = testStartupLog();
441465442-await expect(
443-loadTestStartup({
444-minimalTestGateway: false,
445- log,
446-}),
447-).resolves.toEqual({
448-snapshot: {
449- ...snapshot,
450-runtimeConfig: autoEnabledConfig,
451-config: autoEnabledConfig,
452-},
453-wroteConfig: false,
454- pluginMetadataSnapshot,
466+await expectStartupResult({
467+snapshot: withRuntimeConfig(snapshot, autoEnabledConfig),
468+ log,
455469});
456470457471expect(configMutate.replaceConfigFile).not.toHaveBeenCalled();
458472expect(configIo.readConfigFileSnapshotWithPluginMetadata).toHaveBeenCalledTimes(1);
459-expect(log.info).toHaveBeenCalledWith(
460-"gateway: auto-enabled plugins for this runtime without writing config:\n- Telegram configured, enabled automatically.",
461-);
462-expect(log.warn).not.toHaveBeenCalled();
473+expectRuntimeOnlyAutoEnableLogged(log);
463474});
464475465476it("rejects invalid config before startup without automatic recovery", async () => {
@@ -472,10 +483,7 @@ describe("gateway startup config validation", () => {
472483});
473484474485it("does not suggest doctor repair for plugin packaging compiled-output failures", async () => {
475-const rawConfig = {
476-gateway: { mode: "local" },
477-plugins: { slots: { memory: "source-only-pack" } },
478-};
486+const rawConfig = pluginSlotRawConfig("local");
479487const invalidSnapshot = buildInvalidConfigSnapshot({
480488 rawConfig,
481489config: rawConfig as OpenClawConfig,
@@ -505,10 +513,7 @@ describe("gateway startup config validation", () => {
505513});
506514507515it("keeps doctor repair guidance for mixed plugin packaging and core invalidity", async () => {
508-const rawConfig = {
509-gateway: { mode: "invalid" },
510-plugins: { slots: { memory: "source-only-pack" } },
511-};
516+const rawConfig = pluginSlotRawConfig("invalid");
512517const invalidSnapshot = buildInvalidConfigSnapshot({
513518 rawConfig,
514519config: rawConfig as unknown as OpenClawConfig,
@@ -565,14 +570,7 @@ describe("gateway startup config validation", () => {
565570});
566571567572it("rejects plugin-local startup invalidity without degraded startup", async () => {
568-const rawConfig = {
569-gateway: { mode: "local" },
570-plugins: {
571-entries: {
572-feishu: { enabled: true },
573-},
574-},
575-};
573+const rawConfig = enabledPluginRawConfig("local");
576574const invalidSnapshot = buildInvalidConfigSnapshot({
577575 rawConfig,
578576config: rawConfig as OpenClawConfig,
@@ -589,14 +587,7 @@ describe("gateway startup config validation", () => {
589587});
590588591589it("keeps mixed plugin and core startup invalidity fatal", async () => {
592-const rawConfig = {
593-gateway: { mode: "invalid" },
594-plugins: {
595-entries: {
596-feishu: { enabled: true },
597-},
598-},
599-};
590+const rawConfig = enabledPluginRawConfig("invalid");
600591const invalidSnapshot = buildInvalidConfigSnapshot({
601592 rawConfig,
602593config: rawConfig as unknown as OpenClawConfig,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。