From 0f64887623a6f25c30f88dbae03e9720ac4e9e55 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 28 Apr 2026 04:53:16 +0100 Subject: [PATCH] test(gateway): preserve startup model allowlist --- .../server-startup-config.recovery.test.ts | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/src/gateway/server-startup-config.recovery.test.ts b/src/gateway/server-startup-config.recovery.test.ts index 62164a1f08b..21e1f48417d 100644 --- a/src/gateway/server-startup-config.recovery.test.ts +++ b/src/gateway/server-startup-config.recovery.test.ts @@ -226,6 +226,126 @@ describe("gateway startup config recovery", () => { expect(log.info).not.toHaveBeenCalled(); }); + it("preserves empty model allowlist entries through startup auto-enable writes", async () => { + const sourceConfig = { + agents: { + defaults: { + model: { primary: "dos-ai/dos-ai" }, + models: { + "dos-ai/dos-ai": {}, + "dos-ai/dos-auto": {}, + }, + }, + }, + gateway: { mode: "local" }, + models: { + mode: "replace", + providers: { + "dos-ai": { + baseUrl: "https://dos.example.test/v1", + apiKey: "test-key", + api: "openai-completions", + models: [ + { id: "dos-ai", name: "DOS AI" }, + { id: "dos-auto", name: "DOS Auto" }, + ], + }, + }, + }, + } as OpenClawConfig; + const autoEnabledConfig = { + ...sourceConfig, + channels: { + telegram: { enabled: true }, + }, + } as OpenClawConfig; + const initialSnapshot = { + ...buildTestConfigSnapshot({ + path: configPath, + exists: true, + raw: `${JSON.stringify(sourceConfig)}\n`, + parsed: sourceConfig, + valid: true, + config: sourceConfig, + issues: [], + legacyIssues: [], + }), + sourceConfig, + resolved: sourceConfig, + runtimeConfig: sourceConfig, + config: sourceConfig, + } satisfies ConfigFileSnapshot; + const postWriteSnapshot = { + ...buildTestConfigSnapshot({ + path: configPath, + exists: true, + raw: `${JSON.stringify(autoEnabledConfig)}\n`, + parsed: autoEnabledConfig, + valid: true, + config: autoEnabledConfig, + issues: [], + legacyIssues: [], + }), + sourceConfig: autoEnabledConfig, + resolved: autoEnabledConfig, + runtimeConfig: autoEnabledConfig, + config: autoEnabledConfig, + } satisfies ConfigFileSnapshot; + vi.mocked(configIo.readConfigFileSnapshotWithPluginMetadata) + .mockResolvedValueOnce({ + snapshot: initialSnapshot, + pluginMetadataSnapshot, + }) + .mockResolvedValueOnce({ + snapshot: postWriteSnapshot, + pluginMetadataSnapshot, + }); + applyPluginAutoEnable.mockReturnValueOnce({ + config: autoEnabledConfig, + changes: ["Telegram configured, enabled automatically."], + autoEnabledReasons: {}, + }); + const log = { info: vi.fn(), warn: vi.fn() }; + + await expect( + loadGatewayStartupConfigSnapshot({ + minimalTestGateway: false, + log, + }), + ).resolves.toEqual({ + snapshot: postWriteSnapshot, + wroteConfig: true, + pluginMetadataSnapshot, + }); + + expect(applyPluginAutoEnable).toHaveBeenCalledWith({ + config: sourceConfig, + env: process.env, + manifestRegistry: pluginManifestRegistry, + }); + expect(configMutate.replaceConfigFile).toHaveBeenCalledWith({ + nextConfig: expect.objectContaining({ + agents: expect.objectContaining({ + defaults: expect.objectContaining({ + models: { + "dos-ai/dos-ai": {}, + "dos-ai/dos-auto": {}, + }, + }), + }), + }), + afterWrite: { mode: "auto" }, + }); + expect(postWriteSnapshot.sourceConfig.agents?.defaults?.models).toEqual({ + "dos-ai/dos-ai": {}, + "dos-ai/dos-auto": {}, + }); + expect(postWriteSnapshot.config.agents?.defaults?.models).toEqual({ + "dos-ai/dos-ai": {}, + "dos-ai/dos-auto": {}, + }); + }); + it("restores last-known-good config before startup validation", async () => { const invalidSnapshot = buildSnapshot({ valid: false, raw: "{ invalid json" }); const recoveredSnapshot = buildSnapshot({