openclaw / src /agents /agent-auth-discovery.external-cli.test.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
35f99ee verified
Raw History Blame Contribute Delete
6.79 kB
/** Tests external CLI scoping during agent auth-profile credential discovery. */
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
const storeMocks = vi.hoisted(() => ({
ensureAuthProfileStore: vi.fn(() => ({ version: 1, profiles: {} })),
ensureAuthProfileStoreWithoutExternalProfiles: vi.fn(() => ({ version: 1, profiles: {} })),
}));
const credentialMocks = vi.hoisted(() => ({
resolveAgentCredentialMapFromStore: vi.fn(() => ({})),
}));
const discoveryCoreMocks = vi.hoisted(() => ({
addEnvBackedAgentCredentials: vi.fn((credentials: unknown) => credentials),
}));
const syntheticAuthMocks = vi.hoisted(() => ({
resolveRuntimeSyntheticAuthProviderRefs: vi.fn(() => []),
resolveProviderSyntheticAuthWithPlugin: vi.fn(),
prepareProviderSyntheticAuthWithPlugin: vi.fn(),
}));
vi.mock("./auth-profiles/store-runtime.js", () => storeMocks);
vi.mock("./agent-auth-credentials.js", () => credentialMocks);
vi.mock("./agent-auth-discovery-core.js", () => discoveryCoreMocks);
vi.mock("../plugins/synthetic-auth.runtime.js", () => ({
resolveRuntimeSyntheticAuthProviderRefs:
syntheticAuthMocks.resolveRuntimeSyntheticAuthProviderRefs,
}));
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderSyntheticAuthWithPlugin: syntheticAuthMocks.resolveProviderSyntheticAuthWithPlugin,
prepareProviderSyntheticAuthWithPlugin: syntheticAuthMocks.prepareProviderSyntheticAuthWithPlugin,
}));
import {
resolveAgentDiscoveryAuthFacts,
resolveAmbientAgentCredentialsForDiscovery,
prepareAmbientAgentCredentialsForDiscovery,
} from "./agent-auth-discovery.js";
import { externalCliDiscoveryForProviders } from "./auth-profiles/external-cli-discovery.js";
describe("resolveAgentDiscoveryAuthFacts external CLI scoping", () => {
beforeEach(() => {
vi.clearAllMocks();
credentialMocks.resolveAgentCredentialMapFromStore.mockReturnValue({});
});
it("threads scoped external CLI discovery into writable auth store loading", () => {
const cfg = {} as OpenClawConfig;
const externalCli = externalCliDiscoveryForProviders({
cfg,
providers: ["fireworks"],
});
resolveAgentDiscoveryAuthFacts("/tmp/openclaw-agent", {
config: cfg,
env: {},
externalCli,
});
expect(storeMocks.ensureAuthProfileStore).toHaveBeenCalledWith("/tmp/openclaw-agent", {
allowKeychainPrompt: false,
config: cfg,
externalCli,
});
});
it("reuses the active runtime generation for read-only auth discovery", () => {
const cfg = {} as OpenClawConfig;
const externalCli = externalCliDiscoveryForProviders({
cfg,
providers: ["fireworks"],
});
resolveAgentDiscoveryAuthFacts("/tmp/openclaw-agent", {
config: cfg,
env: {},
externalCli,
readOnly: true,
});
expect(storeMocks.ensureAuthProfileStore).toHaveBeenCalledWith("/tmp/openclaw-agent", {
allowKeychainPrompt: false,
config: cfg,
externalCli,
readOnly: true,
});
});
it("merges prepared ambient credentials without repeating ambient discovery", () => {
credentialMocks.resolveAgentCredentialMapFromStore.mockReturnValue({
fireworks: { type: "api_key", key: "agent-key" },
});
const { credentials } = resolveAgentDiscoveryAuthFacts("/tmp/openclaw-agent", {
ambientCredentials: {
fireworks: { type: "api_key", key: "ambient-key" },
"claude-cli": { type: "api_key", key: "synthetic-key" },
},
env: {},
readOnly: true,
});
expect(credentials).toEqual({
fireworks: { type: "api_key", key: "agent-key" },
"claude-cli": { type: "api_key", key: "synthetic-key" },
});
expect(discoveryCoreMocks.addEnvBackedAgentCredentials).not.toHaveBeenCalled();
expect(syntheticAuthMocks.resolveRuntimeSyntheticAuthProviderRefs).not.toHaveBeenCalled();
expect(syntheticAuthMocks.resolveProviderSyntheticAuthWithPlugin).not.toHaveBeenCalled();
});
it("can skip runtime external auth overlays and scope synthetic auth discovery", () => {
resolveAgentDiscoveryAuthFacts("/tmp/openclaw-agent", {
env: {},
skipExternalAuthProfiles: true,
syntheticAuthProviderRefs: ["fireworks"],
});
expect(storeMocks.ensureAuthProfileStoreWithoutExternalProfiles).toHaveBeenCalledWith(
"/tmp/openclaw-agent",
{
allowKeychainPrompt: false,
},
);
expect(storeMocks.ensureAuthProfileStore).not.toHaveBeenCalled();
expect(syntheticAuthMocks.resolveRuntimeSyntheticAuthProviderRefs).not.toHaveBeenCalled();
expect(syntheticAuthMocks.resolveProviderSyntheticAuthWithPlugin).toHaveBeenCalledWith({
provider: "fireworks",
config: undefined,
workspaceDir: undefined,
env: {},
context: {
config: undefined,
provider: "fireworks",
providerConfig: undefined,
},
});
});
it.each(["read", "prepare"])(
"keeps authoritative native auth separate from provider aliases during %s",
async (mode) => {
discoveryCoreMocks.addEnvBackedAgentCredentials.mockReturnValueOnce({
"claude-cli": { type: "api_key", key: "provider-key" },
});
const resolveSyntheticAuth = vi.fn((_provider: string) => undefined);
const options = {
env: { ANTHROPIC_API_KEY: "provider-key" },
syntheticAuthProviderRefs: ["claude-cli"],
authoritativeSyntheticAuthProviderRefs: ["claude-cli"],
resolveSyntheticAuth,
};
const credentials =
mode === "read"
? resolveAmbientAgentCredentialsForDiscovery(options)
: await prepareAmbientAgentCredentialsForDiscovery({
...options,
resolveSyntheticAuth: async (provider) => resolveSyntheticAuth(provider),
});
expect(credentials).toEqual({});
expect(resolveSyntheticAuth).toHaveBeenCalledWith("claude-cli");
},
);
it.each(["oauth", "token"] as const)(
"skips synthetic api-key fills under a %s provider pin",
(auth) => {
syntheticAuthMocks.resolveProviderSyntheticAuthWithPlugin.mockReturnValue({
apiKey: "synthetic-key",
});
const cfg = {
models: {
providers: {
fireworks: { auth, baseUrl: "https://example.invalid", models: [] },
},
},
} satisfies OpenClawConfig;
const { credentials } = resolveAgentDiscoveryAuthFacts("/tmp/openclaw-agent", {
config: cfg,
env: {},
syntheticAuthProviderRefs: ["fireworks"],
});
expect(credentials.fireworks).toBeUndefined();
expect(syntheticAuthMocks.resolveProviderSyntheticAuthWithPlugin).not.toHaveBeenCalled();
},
);
});