mirror of https://github.com/openclaw/openclaw.git
refactor(cli): dedupe command secret gateway env fixtures
This commit is contained in:
parent
c1a8f8150e
commit
4e8fcc1d3d
|
|
@ -10,6 +10,60 @@ vi.mock("../gateway/call.js", () => ({
|
|||
const { resolveCommandSecretRefsViaGateway } = await import("./command-secret-gateway.js");
|
||||
|
||||
describe("resolveCommandSecretRefsViaGateway", () => {
|
||||
function makeTalkApiKeySecretRefConfig(envKey: string): OpenClawConfig {
|
||||
return {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: envKey },
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
}
|
||||
|
||||
async function withEnvValue(
|
||||
envKey: string,
|
||||
value: string | undefined,
|
||||
fn: () => Promise<void>,
|
||||
): Promise<void> {
|
||||
const priorValue = process.env[envKey];
|
||||
if (value === undefined) {
|
||||
delete process.env[envKey];
|
||||
} else {
|
||||
process.env[envKey] = value;
|
||||
}
|
||||
try {
|
||||
await fn();
|
||||
} finally {
|
||||
if (priorValue === undefined) {
|
||||
delete process.env[envKey];
|
||||
} else {
|
||||
process.env[envKey] = priorValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveTalkApiKey(params: {
|
||||
envKey: string;
|
||||
commandName?: string;
|
||||
mode?: "strict" | "summary";
|
||||
}) {
|
||||
return resolveCommandSecretRefsViaGateway({
|
||||
config: makeTalkApiKeySecretRefConfig(params.envKey),
|
||||
commandName: params.commandName ?? "memory status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
mode: params.mode,
|
||||
});
|
||||
}
|
||||
|
||||
function expectTalkApiKeySecretRef(
|
||||
result: Awaited<ReturnType<typeof resolveTalkApiKey>>,
|
||||
envKey: string,
|
||||
) {
|
||||
expect(result.resolvedConfig.talk?.apiKey).toEqual({
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: envKey,
|
||||
});
|
||||
}
|
||||
|
||||
it("returns config unchanged when no target SecretRefs are configured", async () => {
|
||||
const config = {
|
||||
talk: {
|
||||
|
|
@ -153,58 +207,26 @@ describe("resolveCommandSecretRefsViaGateway", () => {
|
|||
|
||||
it("returns a version-skew hint when gateway does not support secrets.resolve", async () => {
|
||||
const envKey = "TALK_API_KEY_UNSUPPORTED";
|
||||
const priorValue = process.env[envKey];
|
||||
delete process.env[envKey];
|
||||
callGateway.mockRejectedValueOnce(new Error("unknown method: secrets.resolve"));
|
||||
try {
|
||||
await expect(
|
||||
resolveCommandSecretRefsViaGateway({
|
||||
config: {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: envKey },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
commandName: "memory status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
}),
|
||||
).rejects.toThrow(/does not support secrets\.resolve/i);
|
||||
} finally {
|
||||
if (priorValue === undefined) {
|
||||
delete process.env[envKey];
|
||||
} else {
|
||||
process.env[envKey] = priorValue;
|
||||
}
|
||||
}
|
||||
await withEnvValue(envKey, undefined, async () => {
|
||||
await expect(resolveTalkApiKey({ envKey })).rejects.toThrow(
|
||||
/does not support secrets\.resolve/i,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("returns a version-skew hint when required-method capability check fails", async () => {
|
||||
const envKey = "TALK_API_KEY_REQUIRED_METHOD";
|
||||
const priorValue = process.env[envKey];
|
||||
delete process.env[envKey];
|
||||
callGateway.mockRejectedValueOnce(
|
||||
new Error(
|
||||
'active gateway does not support required method "secrets.resolve" for "secrets.resolve".',
|
||||
),
|
||||
);
|
||||
try {
|
||||
await expect(
|
||||
resolveCommandSecretRefsViaGateway({
|
||||
config: {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: envKey },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
commandName: "memory status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
}),
|
||||
).rejects.toThrow(/does not support secrets\.resolve/i);
|
||||
} finally {
|
||||
if (priorValue === undefined) {
|
||||
delete process.env[envKey];
|
||||
} else {
|
||||
process.env[envKey] = priorValue;
|
||||
}
|
||||
}
|
||||
await withEnvValue(envKey, undefined, async () => {
|
||||
await expect(resolveTalkApiKey({ envKey })).rejects.toThrow(
|
||||
/does not support secrets\.resolve/i,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("fails when gateway returns an invalid secrets.resolve payload", async () => {
|
||||
|
|
@ -276,21 +298,9 @@ describe("resolveCommandSecretRefsViaGateway", () => {
|
|||
],
|
||||
});
|
||||
|
||||
const result = await resolveCommandSecretRefsViaGateway({
|
||||
config: {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: "TALK_API_KEY" },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
commandName: "memory status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
});
|
||||
const result = await resolveTalkApiKey({ envKey: "TALK_API_KEY" });
|
||||
|
||||
expect(result.resolvedConfig.talk?.apiKey).toEqual({
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "TALK_API_KEY",
|
||||
});
|
||||
expectTalkApiKeySecretRef(result, "TALK_API_KEY");
|
||||
expect(result.diagnostics).toEqual([
|
||||
"talk.apiKey: secret ref is configured on an inactive surface; skipping command-time assignment.",
|
||||
]);
|
||||
|
|
@ -303,21 +313,9 @@ describe("resolveCommandSecretRefsViaGateway", () => {
|
|||
inactiveRefPaths: ["talk.apiKey"],
|
||||
});
|
||||
|
||||
const result = await resolveCommandSecretRefsViaGateway({
|
||||
config: {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: "TALK_API_KEY" },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
commandName: "memory status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
});
|
||||
const result = await resolveTalkApiKey({ envKey: "TALK_API_KEY" });
|
||||
|
||||
expect(result.resolvedConfig.talk?.apiKey).toEqual({
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "TALK_API_KEY",
|
||||
});
|
||||
expectTalkApiKeySecretRef(result, "TALK_API_KEY");
|
||||
expect(result.diagnostics).toEqual(["talk api key inactive"]);
|
||||
});
|
||||
|
||||
|
|
@ -359,25 +357,16 @@ describe("resolveCommandSecretRefsViaGateway", () => {
|
|||
|
||||
it("degrades unresolved refs in summary mode instead of throwing", async () => {
|
||||
const envKey = "TALK_API_KEY_SUMMARY_MISSING";
|
||||
const priorValue = process.env[envKey];
|
||||
delete process.env[envKey];
|
||||
callGateway.mockResolvedValueOnce({
|
||||
assignments: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await resolveCommandSecretRefsViaGateway({
|
||||
config: {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: envKey },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
await withEnvValue(envKey, undefined, async () => {
|
||||
const result = await resolveTalkApiKey({
|
||||
envKey,
|
||||
commandName: "status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
mode: "summary",
|
||||
});
|
||||
|
||||
expect(result.resolvedConfig.talk?.apiKey).toBeUndefined();
|
||||
expect(result.hadUnresolvedTargets).toBe(true);
|
||||
expect(result.targetStatesByPath["talk.apiKey"]).toBe("unresolved");
|
||||
|
|
@ -386,36 +375,21 @@ describe("resolveCommandSecretRefsViaGateway", () => {
|
|||
entry.includes("talk.apiKey is unavailable in this command path"),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
if (priorValue === undefined) {
|
||||
delete process.env[envKey];
|
||||
} else {
|
||||
process.env[envKey] = priorValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("uses targeted local fallback after an incomplete gateway snapshot", async () => {
|
||||
const envKey = "TALK_API_KEY_PARTIAL_GATEWAY";
|
||||
const priorValue = process.env[envKey];
|
||||
process.env[envKey] = "recovered-locally";
|
||||
callGateway.mockResolvedValueOnce({
|
||||
assignments: [],
|
||||
diagnostics: [],
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await resolveCommandSecretRefsViaGateway({
|
||||
config: {
|
||||
talk: {
|
||||
apiKey: { source: "env", provider: "default", id: envKey },
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
await withEnvValue(envKey, "recovered-locally", async () => {
|
||||
const result = await resolveTalkApiKey({
|
||||
envKey,
|
||||
commandName: "status",
|
||||
targetIds: new Set(["talk.apiKey"]),
|
||||
mode: "summary",
|
||||
});
|
||||
|
||||
expect(result.resolvedConfig.talk?.apiKey).toBe("recovered-locally");
|
||||
expect(result.hadUnresolvedTargets).toBe(false);
|
||||
expect(result.targetStatesByPath["talk.apiKey"]).toBe("resolved_local");
|
||||
|
|
@ -426,13 +400,7 @@ describe("resolveCommandSecretRefsViaGateway", () => {
|
|||
),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
if (priorValue === undefined) {
|
||||
delete process.env[envKey];
|
||||
} else {
|
||||
process.env[envKey] = priorValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("limits strict local fallback analysis to unresolved gateway paths", async () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue