test(model-provider-auth): cover prepared-state short-circuit and clear

Asserts hasAuthForModelProvider returns the warmed answer for providers
in the prepared map and skips the compute path, and that
clearCurrentProviderAuthState restores fall-through to compute.
This commit is contained in:
Sarah Fortune
2026-05-20 22:22:11 -07:00
committed by Peter Steinberger
parent 4f80cc1943
commit 180cecda85

View File

@@ -0,0 +1,77 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { ModelCatalogEntry } from "./model-catalog.types.js";
const modelCatalogMocks = vi.hoisted(() => ({
loadModelCatalog: vi.fn<() => Promise<ModelCatalogEntry[]>>(),
}));
const modelAuthMocks = vi.hoisted(() => ({
hasRuntimeAvailableProviderAuth: vi.fn<(params: { provider: string }) => boolean>(),
}));
const authProfilesMocks = vi.hoisted(() => ({
ensureAuthProfileStore: vi.fn(() => ({ profiles: {} })),
ensureAuthProfileStoreWithoutExternalProfiles: vi.fn(() => ({ profiles: {} })),
externalCliDiscoveryForProviders: vi.fn(() => ({}) as never),
externalCliDiscoveryForProviderAuth: vi.fn(() => ({}) as never),
listProfilesForProvider: vi.fn(() => []),
}));
vi.mock("./model-catalog.js", () => ({
loadModelCatalog: modelCatalogMocks.loadModelCatalog,
}));
vi.mock("./model-auth.js", () => ({
hasRuntimeAvailableProviderAuth: modelAuthMocks.hasRuntimeAvailableProviderAuth,
}));
vi.mock("./auth-profiles.js", () => ({
ensureAuthProfileStore: authProfilesMocks.ensureAuthProfileStore,
ensureAuthProfileStoreWithoutExternalProfiles:
authProfilesMocks.ensureAuthProfileStoreWithoutExternalProfiles,
externalCliDiscoveryForProviders: authProfilesMocks.externalCliDiscoveryForProviders,
externalCliDiscoveryForProviderAuth: authProfilesMocks.externalCliDiscoveryForProviderAuth,
listProfilesForProvider: authProfilesMocks.listProfilesForProvider,
}));
vi.mock("./workspace.js", () => ({
resolveDefaultAgentWorkspaceDir: () => undefined,
}));
const { clearCurrentProviderAuthState, hasAuthForModelProvider, warmCurrentProviderAuthState } =
await import("./model-provider-auth.js");
describe("prepared provider auth state", () => {
afterEach(() => {
clearCurrentProviderAuthState();
vi.clearAllMocks();
});
it("hasAuthForModelProvider returns the prepared answer after warm and falls through to compute after clear", async () => {
const cfg = {} as OpenClawConfig;
modelCatalogMocks.loadModelCatalog.mockResolvedValue([
{ id: "gpt", name: "gpt", provider: "openai" },
{ id: "claude", name: "claude", provider: "anthropic" },
]);
modelAuthMocks.hasRuntimeAvailableProviderAuth.mockImplementation(
({ provider }) => provider === "openai",
);
await warmCurrentProviderAuthState(cfg);
expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(2);
// Flip the underlying answer; if the prepared map is consulted first,
// hasAuthForModelProvider returns the cached answers without re-running
// the compute path.
modelAuthMocks.hasRuntimeAvailableProviderAuth.mockReturnValue(true);
expect(hasAuthForModelProvider({ provider: "openai", cfg })).toBe(true);
expect(hasAuthForModelProvider({ provider: "anthropic", cfg })).toBe(false);
expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(2);
// Clearing the prepared state forces the compute path on the next read.
clearCurrentProviderAuthState();
expect(hasAuthForModelProvider({ provider: "anthropic", cfg })).toBe(true);
expect(modelAuthMocks.hasRuntimeAvailableProviderAuth).toHaveBeenCalledTimes(3);
});
});