mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
refactor: share startup config recovery test helpers
This commit is contained in:
@@ -86,6 +86,8 @@ let configIo: typeof import("../config/io.js");
|
||||
let configMutate: typeof import("../config/mutate.js");
|
||||
|
||||
const configPath = "/tmp/openclaw-startup-recovery.json";
|
||||
const telegramAutoEnableChange = "Telegram configured, enabled automatically.";
|
||||
const runtimeOnlyAutoEnableLog = `gateway: auto-enabled plugins for this runtime without writing config:\n- ${telegramAutoEnableChange}`;
|
||||
const validConfig = {
|
||||
gateway: {
|
||||
mode: "local",
|
||||
@@ -163,6 +165,58 @@ function mockStartupSnapshot(snapshot: ConfigFileSnapshot) {
|
||||
});
|
||||
}
|
||||
|
||||
async function expectStartupResult(params: {
|
||||
snapshot: ConfigFileSnapshot;
|
||||
log?: ReturnType<typeof testStartupLog>;
|
||||
initialSnapshotRead?: Parameters<
|
||||
typeof loadGatewayStartupConfigSnapshot
|
||||
>[0]["initialSnapshotRead"];
|
||||
}) {
|
||||
await expect(
|
||||
loadTestStartup({
|
||||
minimalTestGateway: false,
|
||||
log: params.log,
|
||||
initialSnapshotRead: params.initialSnapshotRead,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
snapshot: params.snapshot,
|
||||
wroteConfig: false,
|
||||
pluginMetadataSnapshot,
|
||||
});
|
||||
}
|
||||
|
||||
function expectPluginAutoEnableFor(config: OpenClawConfig) {
|
||||
expect(applyPluginAutoEnable).toHaveBeenCalledWith({
|
||||
config,
|
||||
env: process.env,
|
||||
manifestRegistry: pluginManifestRegistry,
|
||||
});
|
||||
}
|
||||
|
||||
function mockRuntimeAutoEnable(config: OpenClawConfig) {
|
||||
applyPluginAutoEnable.mockReturnValueOnce({
|
||||
config,
|
||||
changes: [telegramAutoEnableChange],
|
||||
autoEnabledReasons: {},
|
||||
});
|
||||
}
|
||||
|
||||
function expectRuntimeOnlyAutoEnableLogged(log: ReturnType<typeof testStartupLog>) {
|
||||
expect(log.info).toHaveBeenCalledWith(runtimeOnlyAutoEnableLog);
|
||||
expect(log.warn).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
function withRuntimeConfig(
|
||||
snapshot: ConfigFileSnapshot,
|
||||
runtimeConfig: OpenClawConfig,
|
||||
): ConfigFileSnapshot {
|
||||
return {
|
||||
...snapshot,
|
||||
runtimeConfig,
|
||||
config: runtimeConfig,
|
||||
};
|
||||
}
|
||||
|
||||
function buildInvalidConfigSnapshot(params: {
|
||||
rawConfig: unknown;
|
||||
config?: OpenClawConfig;
|
||||
@@ -183,6 +237,24 @@ function buildInvalidConfigSnapshot(params: {
|
||||
});
|
||||
}
|
||||
|
||||
function pluginSlotRawConfig(gatewayMode: string) {
|
||||
return {
|
||||
gateway: { mode: gatewayMode },
|
||||
plugins: { slots: { memory: "source-only-pack" } },
|
||||
};
|
||||
}
|
||||
|
||||
function enabledPluginRawConfig(gatewayMode: string) {
|
||||
return {
|
||||
gateway: { mode: gatewayMode },
|
||||
plugins: {
|
||||
entries: {
|
||||
feishu: { enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function testStartupLog() {
|
||||
return { info: vi.fn(), warn: vi.fn() };
|
||||
}
|
||||
@@ -278,23 +350,10 @@ describe("gateway startup config validation", () => {
|
||||
mockStartupSnapshot(snapshot);
|
||||
const log = testStartupLog();
|
||||
|
||||
await expect(
|
||||
loadTestStartup({
|
||||
minimalTestGateway: false,
|
||||
log,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
snapshot,
|
||||
wroteConfig: false,
|
||||
pluginMetadataSnapshot,
|
||||
});
|
||||
await expectStartupResult({ snapshot, log });
|
||||
|
||||
expect(configIo.readConfigFileSnapshotWithPluginMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(applyPluginAutoEnable).toHaveBeenCalledWith({
|
||||
config: sourceConfig,
|
||||
env: process.env,
|
||||
manifestRegistry: pluginManifestRegistry,
|
||||
});
|
||||
expectPluginAutoEnableFor(sourceConfig);
|
||||
expect(configMutate.replaceConfigFile).not.toHaveBeenCalled();
|
||||
expect(log.info).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -312,27 +371,17 @@ describe("gateway startup config validation", () => {
|
||||
});
|
||||
const log = testStartupLog();
|
||||
|
||||
await expect(
|
||||
loadTestStartup({
|
||||
minimalTestGateway: false,
|
||||
log,
|
||||
initialSnapshotRead: {
|
||||
snapshot,
|
||||
pluginMetadataSnapshot,
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
await expectStartupResult({
|
||||
snapshot,
|
||||
wroteConfig: false,
|
||||
pluginMetadataSnapshot,
|
||||
log,
|
||||
initialSnapshotRead: {
|
||||
snapshot,
|
||||
pluginMetadataSnapshot,
|
||||
},
|
||||
});
|
||||
|
||||
expect(configIo.readConfigFileSnapshotWithPluginMetadata).not.toHaveBeenCalled();
|
||||
expect(applyPluginAutoEnable).toHaveBeenCalledWith({
|
||||
config: validConfig,
|
||||
env: process.env,
|
||||
manifestRegistry: pluginManifestRegistry,
|
||||
});
|
||||
expectPluginAutoEnableFor(validConfig);
|
||||
});
|
||||
|
||||
it("preserves empty model allowlist entries through runtime-only startup auto-enable", async () => {
|
||||
@@ -367,33 +416,15 @@ describe("gateway startup config validation", () => {
|
||||
} as unknown as OpenClawConfig;
|
||||
const initialSnapshot = buildRuntimeSnapshot(sourceConfig);
|
||||
mockStartupSnapshot(initialSnapshot);
|
||||
applyPluginAutoEnable.mockReturnValueOnce({
|
||||
config: autoEnabledConfig,
|
||||
changes: ["Telegram configured, enabled automatically."],
|
||||
autoEnabledReasons: {},
|
||||
});
|
||||
mockRuntimeAutoEnable(autoEnabledConfig);
|
||||
const log = testStartupLog();
|
||||
|
||||
await expect(
|
||||
loadTestStartup({
|
||||
minimalTestGateway: false,
|
||||
log,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
snapshot: {
|
||||
...initialSnapshot,
|
||||
runtimeConfig: autoEnabledConfig,
|
||||
config: autoEnabledConfig,
|
||||
},
|
||||
wroteConfig: false,
|
||||
pluginMetadataSnapshot,
|
||||
await expectStartupResult({
|
||||
snapshot: withRuntimeConfig(initialSnapshot, autoEnabledConfig),
|
||||
log,
|
||||
});
|
||||
|
||||
expect(applyPluginAutoEnable).toHaveBeenCalledWith({
|
||||
config: sourceConfig,
|
||||
env: process.env,
|
||||
manifestRegistry: pluginManifestRegistry,
|
||||
});
|
||||
expectPluginAutoEnableFor(sourceConfig);
|
||||
expect(configMutate.replaceConfigFile).not.toHaveBeenCalled();
|
||||
expect(configIo.readConfigFileSnapshotWithPluginMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(initialSnapshot.sourceConfig.agents?.defaults?.models).toEqual({
|
||||
@@ -408,10 +439,7 @@ describe("gateway startup config validation", () => {
|
||||
expect(autoEnabledConfig.channels?.telegram).toEqual({
|
||||
enabled: true,
|
||||
});
|
||||
expect(log.info).toHaveBeenCalledWith(
|
||||
"gateway: auto-enabled plugins for this runtime without writing config:\n- Telegram configured, enabled automatically.",
|
||||
);
|
||||
expect(log.warn).not.toHaveBeenCalled();
|
||||
expectRuntimeOnlyAutoEnableLogged(log);
|
||||
});
|
||||
|
||||
it("keeps plugin auto-enable runtime-only in Nix mode", async () => {
|
||||
@@ -431,35 +459,18 @@ describe("gateway startup config validation", () => {
|
||||
} as unknown as OpenClawConfig;
|
||||
const snapshot = buildRuntimeSnapshot(sourceConfig);
|
||||
mockStartupSnapshot(snapshot);
|
||||
applyPluginAutoEnable.mockReturnValueOnce({
|
||||
config: autoEnabledConfig,
|
||||
changes: ["Telegram configured, enabled automatically."],
|
||||
autoEnabledReasons: {},
|
||||
});
|
||||
mockRuntimeAutoEnable(autoEnabledConfig);
|
||||
configMocks.isNixMode.value = true;
|
||||
const log = testStartupLog();
|
||||
|
||||
await expect(
|
||||
loadTestStartup({
|
||||
minimalTestGateway: false,
|
||||
log,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
snapshot: {
|
||||
...snapshot,
|
||||
runtimeConfig: autoEnabledConfig,
|
||||
config: autoEnabledConfig,
|
||||
},
|
||||
wroteConfig: false,
|
||||
pluginMetadataSnapshot,
|
||||
await expectStartupResult({
|
||||
snapshot: withRuntimeConfig(snapshot, autoEnabledConfig),
|
||||
log,
|
||||
});
|
||||
|
||||
expect(configMutate.replaceConfigFile).not.toHaveBeenCalled();
|
||||
expect(configIo.readConfigFileSnapshotWithPluginMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(log.info).toHaveBeenCalledWith(
|
||||
"gateway: auto-enabled plugins for this runtime without writing config:\n- Telegram configured, enabled automatically.",
|
||||
);
|
||||
expect(log.warn).not.toHaveBeenCalled();
|
||||
expectRuntimeOnlyAutoEnableLogged(log);
|
||||
});
|
||||
|
||||
it("rejects invalid config before startup without automatic recovery", async () => {
|
||||
@@ -472,10 +483,7 @@ describe("gateway startup config validation", () => {
|
||||
});
|
||||
|
||||
it("does not suggest doctor repair for plugin packaging compiled-output failures", async () => {
|
||||
const rawConfig = {
|
||||
gateway: { mode: "local" },
|
||||
plugins: { slots: { memory: "source-only-pack" } },
|
||||
};
|
||||
const rawConfig = pluginSlotRawConfig("local");
|
||||
const invalidSnapshot = buildInvalidConfigSnapshot({
|
||||
rawConfig,
|
||||
config: rawConfig as OpenClawConfig,
|
||||
@@ -505,10 +513,7 @@ describe("gateway startup config validation", () => {
|
||||
});
|
||||
|
||||
it("keeps doctor repair guidance for mixed plugin packaging and core invalidity", async () => {
|
||||
const rawConfig = {
|
||||
gateway: { mode: "invalid" },
|
||||
plugins: { slots: { memory: "source-only-pack" } },
|
||||
};
|
||||
const rawConfig = pluginSlotRawConfig("invalid");
|
||||
const invalidSnapshot = buildInvalidConfigSnapshot({
|
||||
rawConfig,
|
||||
config: rawConfig as unknown as OpenClawConfig,
|
||||
@@ -565,14 +570,7 @@ describe("gateway startup config validation", () => {
|
||||
});
|
||||
|
||||
it("rejects plugin-local startup invalidity without degraded startup", async () => {
|
||||
const rawConfig = {
|
||||
gateway: { mode: "local" },
|
||||
plugins: {
|
||||
entries: {
|
||||
feishu: { enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
const rawConfig = enabledPluginRawConfig("local");
|
||||
const invalidSnapshot = buildInvalidConfigSnapshot({
|
||||
rawConfig,
|
||||
config: rawConfig as OpenClawConfig,
|
||||
@@ -589,14 +587,7 @@ describe("gateway startup config validation", () => {
|
||||
});
|
||||
|
||||
it("keeps mixed plugin and core startup invalidity fatal", async () => {
|
||||
const rawConfig = {
|
||||
gateway: { mode: "invalid" },
|
||||
plugins: {
|
||||
entries: {
|
||||
feishu: { enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
const rawConfig = enabledPluginRawConfig("invalid");
|
||||
const invalidSnapshot = buildInvalidConfigSnapshot({
|
||||
rawConfig,
|
||||
config: rawConfig as unknown as OpenClawConfig,
|
||||
|
||||
Reference in New Issue
Block a user