import fs from "node:fs" ;
import os from "node:os" ;
import path from "node:path" ;
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest" ;
const resolveProviderUsageAuthWithPluginMock = vi.fn(
async (..._args: unknown[]): Promise<unknown> => null ,
);
const hasAnyAuthProfileStoreSourceMock = vi.fn(() => false );
const ensureAuthProfileStoreMock = vi.fn(() => ({
profiles: {},
}));
const ensureAuthProfileStoreWithoutExternalProfilesMock = vi.fn(() => ({
profiles: {},
}));
const resolveAuthProfileOrderMock = vi.fn((_params: unknown): string[] => []);
vi.mock("../agents/auth-profiles.js" , () => ({
dedupeProfileIds: (profileIds: string[]) => [...new Set(profileIds)],
ensureAuthProfileStore: () => ensureAuthProfileStoreMock(),
ensureAuthProfileStoreWithoutExternalProfiles: () =>
ensureAuthProfileStoreWithoutExternalProfilesMock(),
hasAnyAuthProfileStoreSource: () => hasAnyAuthProfileStoreSourceMock(),
listProfilesForProvider: () => [],
resolveApiKeyForProfile: async () => null ,
resolveAuthProfileOrder: (params: unknown) => resolveAuthProfileOrderMock(params),
}));
vi.mock("../plugins/provider-runtime.js" , async () => {
const actual = await vi.importActual<typeof import ("../plugins/provider-runtime.js" )>(
"../plugins/provider-runtime.js" ,
);
return {
...actual,
resolveProviderUsageAuthWithPlugin: resolveProviderUsageAuthWithPluginMock,
};
});
let resolveProviderAuths: typeof import ("./provider-usage.auth.js" ).resolveProviderAuths;
async function withTempHome<T>(fn: (homeDir: string) => Promise<T>): Promise<T> {
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-provider-usage-" ));
try {
return await fn(homeDir);
} finally {
fs.rmSync(homeDir, { recursive: true , force: true });
}
}
describe("resolveProviderAuths plugin boundary" , () => {
beforeAll(async () => {
({ resolveProviderAuths } = await import ("./provider-usage.auth.js" ));
});
beforeEach(() => {
hasAnyAuthProfileStoreSourceMock.mockReset();
hasAnyAuthProfileStoreSourceMock.mockReturnValue(false );
ensureAuthProfileStoreMock.mockClear();
ensureAuthProfileStoreMock.mockReturnValue({
profiles: {},
});
ensureAuthProfileStoreWithoutExternalProfilesMock.mockClear();
ensureAuthProfileStoreWithoutExternalProfilesMock.mockReturnValue({
profiles: {},
});
resolveAuthProfileOrderMock.mockReset();
resolveAuthProfileOrderMock.mockReturnValue([]);
resolveProviderUsageAuthWithPluginMock.mockReset();
resolveProviderUsageAuthWithPluginMock.mockResolvedValue(null );
});
it("prefers plugin-owned usage auth when available" , async () => {
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
token: "plugin-zai-token" ,
});
await expect(
resolveProviderAuths({
providers: ["zai" ],
}),
).resolves.toEqual([
{
provider: "zai" ,
token: "plugin-zai-token" ,
},
]);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("skips plugin usage auth when requested and no direct credential source exists" , async () => {
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["zai" ],
skipPluginAuthWithoutCredentialSource: true ,
env: { HOME: homeDir },
}),
).resolves.toEqual([]);
});
expect(resolveProviderUsageAuthWithPluginMock).not.toHaveBeenCalled();
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("keeps plugin usage auth when a shared legacy plugin credential source exists" , async () => {
await withTempHome(async (homeDir) => {
fs.mkdirSync(path.join(homeDir, ".pi" , "agent" ), { recursive: true });
fs.writeFileSync(
path.join(homeDir, ".pi" , "agent" , "auth.json" ),
`${JSON.stringify({ "z-ai" : { access: "legacy-zai-token" } })}\n`,
);
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
token: "legacy-zai-token" ,
});
await expect(
resolveProviderAuths({
providers: ["zai" ],
skipPluginAuthWithoutCredentialSource: true ,
env: { HOME: homeDir },
}),
).resolves.toEqual([
{
provider: "zai" ,
token: "legacy-zai-token" ,
},
]);
});
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "zai" ,
}),
);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("keeps legacy plugin credential sources provider-specific" , async () => {
await withTempHome(async (homeDir) => {
fs.mkdirSync(path.join(homeDir, ".pi" , "agent" ), { recursive: true });
fs.writeFileSync(
path.join(homeDir, ".pi" , "agent" , "auth.json" ),
`${JSON.stringify({ "z-ai" : { access: "legacy-zai-token" } })}\n`,
);
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
token: "legacy-zai-token" ,
});
await expect(
resolveProviderAuths({
providers: ["anthropic" , "zai" ],
skipPluginAuthWithoutCredentialSource: true ,
env: { HOME: homeDir },
}),
).resolves.toEqual([
{
provider: "zai" ,
token: "legacy-zai-token" ,
},
]);
});
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledTimes(1 );
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "zai" ,
}),
);
});
it("keeps auth-profile credential sources provider-specific" , async () => {
hasAnyAuthProfileStoreSourceMock.mockReturnValue(true );
ensureAuthProfileStoreWithoutExternalProfilesMock.mockReturnValue({
profiles: {
"anthropic:default" : {
type: "api_key" ,
provider: "anthropic" ,
key: "sk-ant" ,
},
},
});
resolveAuthProfileOrderMock.mockImplementation((params: unknown) => {
const provider =
params && typeof params === "object" && "provider" in params
? (params as { provider?: unknown }).provider
: undefined;
return provider === "anthropic" ? ["anthropic:default" ] : [];
});
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
token: "plugin-anthropic-token" ,
});
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["anthropic" , "zai" ],
skipPluginAuthWithoutCredentialSource: true ,
env: { HOME: homeDir },
}),
).resolves.toEqual([
{
provider: "anthropic" ,
token: "plugin-anthropic-token" ,
},
]);
});
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledTimes(1 );
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "anthropic" ,
}),
);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("keeps plugin usage auth when an owned alias provider has auth-profile credentials" , async () => {
hasAnyAuthProfileStoreSourceMock.mockReturnValue(true );
ensureAuthProfileStoreWithoutExternalProfilesMock.mockReturnValue({
profiles: {
"minimax-portal:default" : {
type: "oauth" ,
provider: "minimax-portal" ,
accessToken: "portal-oauth-token" ,
},
},
});
resolveAuthProfileOrderMock.mockImplementation((params: unknown) => {
const provider =
params && typeof params === "object" && "provider" in params
? (params as { provider?: unknown }).provider
: undefined;
return provider === "minimax-portal" ? ["minimax-portal:default" ] : [];
});
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
token: "plugin-minimax-token" ,
});
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["minimax" ],
skipPluginAuthWithoutCredentialSource: true ,
env: { HOME: homeDir },
}),
).resolves.toEqual([
{
provider: "minimax" ,
token: "plugin-minimax-token" ,
},
]);
});
expect(resolveAuthProfileOrderMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "minimax-portal" ,
}),
);
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "minimax" ,
}),
);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("keeps plugin usage auth when provider-owned usage env credentials exist" , async () => {
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
token: "plugin-minimax-token" ,
});
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["minimax" ],
skipPluginAuthWithoutCredentialSource: true ,
env: {
HOME: homeDir,
MINIMAX_CODE_PLAN_KEY: "code-plan-key" ,
},
}),
).resolves.toEqual([
{
provider: "minimax" ,
token: "plugin-minimax-token" ,
},
]);
});
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "minimax" ,
}),
);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("does not overlay external auth profiles while checking the skip gate" , async () => {
hasAnyAuthProfileStoreSourceMock.mockReturnValue(true );
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["anthropic" ],
skipPluginAuthWithoutCredentialSource: true ,
env: { HOME: homeDir },
}),
).resolves.toEqual([]);
});
expect(ensureAuthProfileStoreWithoutExternalProfilesMock).toHaveBeenCalledTimes(1 );
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
expect(resolveProviderUsageAuthWithPluginMock).not.toHaveBeenCalled();
});
it("skips plugin usage auth per provider when only another provider has direct credentials" , async () => {
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["anthropic" , "zai" ],
skipPluginAuthWithoutCredentialSource: true ,
env: {
HOME: homeDir,
ANTHROPIC_API_KEY: "sk-ant" ,
},
}),
).resolves.toEqual([
{
provider: "anthropic" ,
token: "sk-ant" ,
},
]);
});
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledTimes(1 );
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
expect.objectContaining({
provider: "anthropic" ,
}),
);
});
});
Messung V0.5 in Prozent C=99 H=96 G=97
¤ Dauer der Verarbeitung: 0.8 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland