Files
openclaw/src/commands/models/list.status.test.ts
Vincent Koc 42e3d8d693 Secrets: add inline allowlist review set (#38314)
* Secrets: add inline allowlist review set

* Secrets: narrow detect-secrets file exclusions

* Secrets: exclude Docker fingerprint false positive

* Secrets: allowlist test and docs false positives

* Secrets: refresh baseline after allowlist updates

* Secrets: fix gateway chat fixture pragma

* Secrets: format pre-commit config

* Android: keep talk mode fixture JSON valid

* Feishu: rely on client timeout injection

* Secrets: allowlist provider auth test fixtures

* Secrets: allowlist onboard search fixtures

* Secrets: allowlist onboard mode fixture

* Secrets: allowlist gateway auth mode fixture

* Secrets: allowlist APNS wake test key

* Secrets: allowlist gateway reload fixtures

* Secrets: allowlist moonshot video fixture

* Secrets: allowlist auto audio fixture

* Secrets: allowlist tiny audio fixture

* Secrets: allowlist embeddings fixtures

* Secrets: allowlist resolve fixtures

* Secrets: allowlist target registry pattern fixtures

* Secrets: allowlist gateway chat env fixture

* Secrets: refresh baseline after fixture allowlists

* Secrets: reapply gateway chat env allowlist

* Secrets: reapply gateway chat env allowlist

* Secrets: stabilize gateway chat env allowlist

* Secrets: allowlist runtime snapshot save fixture

* Secrets: allowlist oauth profile fixtures

* Secrets: allowlist compaction identifier fixture

* Secrets: allowlist model auth fixture

* Secrets: allowlist model status fixtures

* Secrets: allowlist custom onboarding fixture

* Secrets: allowlist mattermost token summary fixtures

* Secrets: allowlist gateway auth suite fixtures

* Secrets: allowlist channel summary fixture

* Secrets: allowlist provider usage auth fixtures

* Secrets: allowlist media proxy fixture

* Secrets: allowlist secrets audit fixtures

* Secrets: refresh baseline after final fixture allowlists

* Feishu: prefer explicit client timeout

* Feishu: test direct timeout precedence
2026-03-06 19:35:26 -05:00

349 lines
13 KiB
TypeScript

import { describe, expect, it, type Mock, vi } from "vitest";
const mocks = vi.hoisted(() => {
type MockAuthProfile = { provider: string; [key: string]: unknown };
const store = {
version: 1,
profiles: {
"anthropic:default": {
type: "oauth",
provider: "anthropic",
access: "sk-ant-oat01-ACCESS-TOKEN-1234567890",
refresh: "sk-ant-ort01-REFRESH-TOKEN-1234567890", // pragma: allowlist secret
expires: Date.now() + 60_000,
email: "peter@example.com",
},
"anthropic:work": {
type: "api_key",
provider: "anthropic",
key: "sk-ant-api-0123456789abcdefghijklmnopqrstuvwxyz", // pragma: allowlist secret
},
"openai-codex:default": {
type: "oauth",
provider: "openai-codex",
access: "eyJhbGciOi-ACCESS",
refresh: "oai-refresh-1234567890",
expires: Date.now() + 60_000,
},
} as Record<string, MockAuthProfile>,
};
return {
store,
resolveOpenClawAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"),
resolveAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"),
resolveAgentExplicitModelPrimary: vi.fn().mockReturnValue(undefined),
resolveAgentEffectiveModelPrimary: vi.fn().mockReturnValue(undefined),
resolveAgentModelFallbacksOverride: vi.fn().mockReturnValue(undefined),
listAgentIds: vi.fn().mockReturnValue(["main", "jeremiah"]),
ensureAuthProfileStore: vi.fn().mockReturnValue(store),
listProfilesForProvider: vi.fn((s: typeof store, provider: string) => {
return Object.entries(s.profiles)
.filter(([, cred]) => cred.provider === provider)
.map(([id]) => id);
}),
resolveAuthProfileDisplayLabel: vi.fn(({ profileId }: { profileId: string }) => profileId),
resolveAuthStorePathForDisplay: vi
.fn()
.mockReturnValue("/tmp/openclaw-agent/auth-profiles.json"),
resolveEnvApiKey: vi.fn((provider: string) => {
if (provider === "openai") {
return {
apiKey: "sk-openai-0123456789abcdefghijklmnopqrstuvwxyz", // pragma: allowlist secret
source: "shell env: OPENAI_API_KEY",
};
}
if (provider === "anthropic") {
return {
apiKey: "sk-ant-oat01-ACCESS-TOKEN-1234567890", // pragma: allowlist secret
source: "env: ANTHROPIC_OAUTH_TOKEN",
};
}
return null;
}),
getCustomProviderApiKey: vi.fn().mockReturnValue(undefined),
getShellEnvAppliedKeys: vi.fn().mockReturnValue(["OPENAI_API_KEY", "ANTHROPIC_OAUTH_TOKEN"]),
shouldEnableShellEnvFallback: vi.fn().mockReturnValue(true),
loadConfig: vi.fn().mockReturnValue({
agents: {
defaults: {
model: { primary: "anthropic/claude-opus-4-5", fallbacks: [] },
models: { "anthropic/claude-opus-4-5": { alias: "Opus" } },
},
},
models: { providers: {} },
env: { shellEnv: { enabled: true } },
}),
loadProviderUsageSummary: vi.fn().mockResolvedValue(undefined),
};
});
vi.mock("../../agents/agent-paths.js", () => ({
resolveOpenClawAgentDir: mocks.resolveOpenClawAgentDir,
}));
vi.mock("../../agents/agent-scope.js", () => ({
resolveAgentDir: mocks.resolveAgentDir,
resolveAgentExplicitModelPrimary: mocks.resolveAgentExplicitModelPrimary,
resolveAgentEffectiveModelPrimary: mocks.resolveAgentEffectiveModelPrimary,
resolveAgentModelFallbacksOverride: mocks.resolveAgentModelFallbacksOverride,
listAgentIds: mocks.listAgentIds,
}));
vi.mock("../../agents/auth-profiles.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../agents/auth-profiles.js")>();
return {
...actual,
ensureAuthProfileStore: mocks.ensureAuthProfileStore,
listProfilesForProvider: mocks.listProfilesForProvider,
resolveAuthProfileDisplayLabel: mocks.resolveAuthProfileDisplayLabel,
resolveAuthStorePathForDisplay: mocks.resolveAuthStorePathForDisplay,
};
});
vi.mock("../../agents/model-auth.js", () => ({
resolveEnvApiKey: mocks.resolveEnvApiKey,
getCustomProviderApiKey: mocks.getCustomProviderApiKey,
}));
vi.mock("../../infra/shell-env.js", () => ({
getShellEnvAppliedKeys: mocks.getShellEnvAppliedKeys,
shouldEnableShellEnvFallback: mocks.shouldEnableShellEnvFallback,
}));
vi.mock("../../config/config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../config/config.js")>();
return {
...actual,
loadConfig: mocks.loadConfig,
};
});
vi.mock("../../infra/provider-usage.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/provider-usage.js")>();
return {
...actual,
loadProviderUsageSummary: mocks.loadProviderUsageSummary,
};
});
import { modelsStatusCommand } from "./list.status-command.js";
const defaultResolveEnvApiKeyImpl:
| ((provider: string) => { apiKey: string; source: string } | null)
| undefined = mocks.resolveEnvApiKey.getMockImplementation();
const runtime = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
function createRuntime() {
return {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
}
async function withAgentScopeOverrides<T>(
overrides: {
primary?: string;
fallbacks?: string[];
agentDir?: string;
},
run: () => Promise<T>,
) {
const originalPrimary = mocks.resolveAgentExplicitModelPrimary.getMockImplementation();
const originalEffectivePrimary = mocks.resolveAgentEffectiveModelPrimary.getMockImplementation();
const originalFallbacks = mocks.resolveAgentModelFallbacksOverride.getMockImplementation();
const originalAgentDir = mocks.resolveAgentDir.getMockImplementation();
mocks.resolveAgentExplicitModelPrimary.mockReturnValue(overrides.primary);
mocks.resolveAgentEffectiveModelPrimary.mockReturnValue(overrides.primary);
mocks.resolveAgentModelFallbacksOverride.mockReturnValue(overrides.fallbacks);
if (overrides.agentDir) {
mocks.resolveAgentDir.mockReturnValue(overrides.agentDir);
}
try {
return await run();
} finally {
if (originalPrimary) {
mocks.resolveAgentExplicitModelPrimary.mockImplementation(originalPrimary);
} else {
mocks.resolveAgentExplicitModelPrimary.mockReturnValue(undefined);
}
if (originalEffectivePrimary) {
mocks.resolveAgentEffectiveModelPrimary.mockImplementation(originalEffectivePrimary);
} else {
mocks.resolveAgentEffectiveModelPrimary.mockReturnValue(undefined);
}
if (originalFallbacks) {
mocks.resolveAgentModelFallbacksOverride.mockImplementation(originalFallbacks);
} else {
mocks.resolveAgentModelFallbacksOverride.mockReturnValue(undefined);
}
if (originalAgentDir) {
mocks.resolveAgentDir.mockImplementation(originalAgentDir);
} else {
mocks.resolveAgentDir.mockReturnValue("/tmp/openclaw-agent");
}
}
}
describe("modelsStatusCommand auth overview", () => {
it("includes masked auth sources in JSON output", async () => {
await modelsStatusCommand({ json: true }, runtime as never);
const payload = JSON.parse(String((runtime.log as Mock).mock.calls[0]?.[0]));
expect(mocks.resolveOpenClawAgentDir).toHaveBeenCalled();
expect(payload.defaultModel).toBe("anthropic/claude-opus-4-5");
expect(payload.auth.storePath).toBe("/tmp/openclaw-agent/auth-profiles.json");
expect(payload.auth.shellEnvFallback.enabled).toBe(true);
expect(payload.auth.shellEnvFallback.appliedKeys).toContain("OPENAI_API_KEY");
expect(payload.auth.missingProvidersInUse).toEqual([]);
expect(payload.auth.oauth.warnAfterMs).toBeGreaterThan(0);
expect(payload.auth.oauth.profiles.length).toBeGreaterThan(0);
const providers = payload.auth.providers as Array<{
provider: string;
profiles: { labels: string[] };
env?: { value: string; source: string };
}>;
const anthropic = providers.find((p) => p.provider === "anthropic");
expect(anthropic).toBeTruthy();
expect(anthropic?.profiles.labels.join(" ")).toContain("OAuth");
expect(anthropic?.profiles.labels.join(" ")).toContain("...");
const openai = providers.find((p) => p.provider === "openai");
expect(openai?.env?.source).toContain("OPENAI_API_KEY");
expect(openai?.env?.value).toContain("...");
expect(
(payload.auth.providersWithOAuth as string[]).some((e) => e.startsWith("anthropic")),
).toBe(true);
expect(
(payload.auth.providersWithOAuth as string[]).some((e) => e.startsWith("openai-codex")),
).toBe(true);
});
it("does not emit raw short api-key values in JSON labels", async () => {
const localRuntime = createRuntime();
const shortSecret = "abc123"; // pragma: allowlist secret
const originalProfiles = { ...mocks.store.profiles };
mocks.store.profiles = {
...mocks.store.profiles,
"openai:default": {
type: "api_key",
provider: "openai",
key: shortSecret,
},
};
try {
await modelsStatusCommand({ json: true }, localRuntime as never);
const payload = JSON.parse(String((localRuntime.log as Mock).mock.calls[0]?.[0]));
const providers = payload.auth.providers as Array<{
provider: string;
profiles: { labels: string[] };
}>;
const openai = providers.find((p) => p.provider === "openai");
const labels = openai?.profiles.labels ?? [];
expect(labels.join(" ")).toContain("...");
expect(labels.join(" ")).not.toContain(shortSecret);
} finally {
mocks.store.profiles = originalProfiles;
}
});
it("uses agent overrides and reports sources", async () => {
const localRuntime = createRuntime();
await withAgentScopeOverrides(
{
primary: "openai/gpt-4",
fallbacks: ["openai/gpt-3.5"],
agentDir: "/tmp/openclaw-agent-custom",
},
async () => {
await modelsStatusCommand({ json: true, agent: "Jeremiah" }, localRuntime as never);
expect(mocks.resolveAgentDir).toHaveBeenCalledWith(expect.anything(), "jeremiah");
const payload = JSON.parse(String((localRuntime.log as Mock).mock.calls[0]?.[0]));
expect(payload.agentId).toBe("jeremiah");
expect(payload.agentDir).toBe("/tmp/openclaw-agent-custom");
expect(payload.defaultModel).toBe("openai/gpt-4");
expect(payload.fallbacks).toEqual(["openai/gpt-3.5"]);
expect(payload.modelConfig).toEqual({
defaultSource: "agent",
fallbacksSource: "agent",
});
},
);
});
it("labels defaults when --agent has no overrides", async () => {
const localRuntime = createRuntime();
await withAgentScopeOverrides(
{
primary: undefined,
fallbacks: undefined,
},
async () => {
await modelsStatusCommand({ agent: "main" }, localRuntime as never);
const output = (localRuntime.log as Mock).mock.calls
.map((call: unknown[]) => String(call[0]))
.join("\n");
expect(output).toContain("Default (defaults)");
expect(output).toContain("Fallbacks (0) (defaults)");
},
);
});
it("reports defaults source in JSON when --agent has no overrides", async () => {
const localRuntime = createRuntime();
await withAgentScopeOverrides(
{
primary: undefined,
fallbacks: undefined,
},
async () => {
await modelsStatusCommand({ json: true, agent: "main" }, localRuntime as never);
const payload = JSON.parse(String((localRuntime.log as Mock).mock.calls[0]?.[0]));
expect(payload.modelConfig).toEqual({
defaultSource: "defaults",
fallbacksSource: "defaults",
});
},
);
});
it("throws when agent id is unknown", async () => {
const localRuntime = createRuntime();
await expect(modelsStatusCommand({ agent: "unknown" }, localRuntime as never)).rejects.toThrow(
'Unknown agent id "unknown".',
);
});
it("exits non-zero when auth is missing", async () => {
const originalProfiles = { ...mocks.store.profiles };
mocks.store.profiles = {};
const localRuntime = createRuntime();
const originalEnvImpl = mocks.resolveEnvApiKey.getMockImplementation();
mocks.resolveEnvApiKey.mockImplementation(() => null);
try {
await modelsStatusCommand({ check: true, plain: true }, localRuntime as never);
expect(localRuntime.exit).toHaveBeenCalledWith(1);
} finally {
mocks.store.profiles = originalProfiles;
if (originalEnvImpl) {
mocks.resolveEnvApiKey.mockImplementation(originalEnvImpl);
} else if (defaultResolveEnvApiKeyImpl) {
mocks.resolveEnvApiKey.mockImplementation(defaultResolveEnvApiKeyImpl);
} else {
mocks.resolveEnvApiKey.mockImplementation(() => null);
}
}
});
});