test: isolate agent runtime mocks

This commit is contained in:
Peter Steinberger
2026-04-10 18:06:13 +01:00
parent cbc4447d6b
commit e1a2a26ec9
4 changed files with 52 additions and 18 deletions

View File

@@ -3,6 +3,18 @@ import type { ProviderExternalAuthProfile } from "../../plugins/types.js";
import type { AuthProfileStore, OAuthCredential } from "./types.js";
type ExternalAuthProfileMap = Map<string, ProviderExternalAuthProfile>;
type ResolveExternalAuthProfiles = typeof resolveExternalAuthProfilesWithPlugins;
let resolveExternalAuthProfilesForRuntime: ResolveExternalAuthProfiles | undefined;
export const __testing = {
resetResolveExternalAuthProfilesForTest(): void {
resolveExternalAuthProfilesForRuntime = undefined;
},
setResolveExternalAuthProfilesForTest(resolver: ResolveExternalAuthProfiles): void {
resolveExternalAuthProfilesForRuntime = resolver;
},
};
function normalizeExternalAuthProfile(
profile: ProviderExternalAuthProfile,
@@ -22,7 +34,9 @@ function resolveExternalAuthProfileMap(params: {
env?: NodeJS.ProcessEnv;
}): ExternalAuthProfileMap {
const env = params.env ?? process.env;
const profiles = resolveExternalAuthProfilesWithPlugins({
const resolveProfiles =
resolveExternalAuthProfilesForRuntime ?? resolveExternalAuthProfilesWithPlugins;
const profiles = resolveProfiles({
env,
context: {
config: undefined,

View File

@@ -1,18 +1,16 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { ProviderExternalAuthProfile } from "../../plugins/types.js";
import {
__testing,
overlayExternalOAuthProfiles,
shouldPersistExternalOAuthProfile,
} from "./external-auth.js";
import type { AuthProfileStore, OAuthCredential } from "./types.js";
const resolveExternalAuthProfilesWithPluginsMock = vi.fn<
(params: unknown) => ProviderExternalAuthProfile[]
>(() => []);
vi.mock("../../plugins/provider-runtime.js", () => ({
resolveExternalAuthProfilesWithPlugins: (params: unknown) =>
resolveExternalAuthProfilesWithPluginsMock(params),
resolveExternalOAuthProfilesWithPlugins: (params: unknown) =>
resolveExternalAuthProfilesWithPluginsMock(params),
}));
function createStore(profiles: AuthProfileStore["profiles"] = {}): AuthProfileStore {
return { version: 1, profiles };
}
@@ -31,9 +29,15 @@ function createCredential(overrides: Partial<OAuthCredential> = {}): OAuthCreden
describe("auth external oauth helpers", () => {
beforeEach(() => {
resolveExternalAuthProfilesWithPluginsMock.mockReset();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValue([]);
__testing.setResolveExternalAuthProfilesForTest(resolveExternalAuthProfilesWithPluginsMock);
});
it("overlays provider-managed runtime oauth profiles onto the store", async () => {
afterEach(() => {
__testing.resetResolveExternalAuthProfilesForTest();
});
it("overlays provider-managed runtime oauth profiles onto the store", () => {
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
profileId: "openai-codex:default",
@@ -41,7 +45,6 @@ describe("auth external oauth helpers", () => {
},
]);
const { overlayExternalOAuthProfiles } = await import("./external-auth.js");
const store = overlayExternalOAuthProfiles(createStore());
expect(store.profiles["openai-codex:default"]).toMatchObject({
@@ -51,7 +54,7 @@ describe("auth external oauth helpers", () => {
});
});
it("omits exact runtime-only overlays from persisted store writes", async () => {
it("omits exact runtime-only overlays from persisted store writes", () => {
const credential = createCredential();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
@@ -60,7 +63,6 @@ describe("auth external oauth helpers", () => {
},
]);
const { shouldPersistExternalOAuthProfile } = await import("./external-auth.js");
const shouldPersist = shouldPersistExternalOAuthProfile({
store: createStore({ "openai-codex:default": credential }),
profileId: "openai-codex:default",
@@ -70,7 +72,7 @@ describe("auth external oauth helpers", () => {
expect(shouldPersist).toBe(false);
});
it("keeps persisted copies when the external overlay is marked persisted", async () => {
it("keeps persisted copies when the external overlay is marked persisted", () => {
const credential = createCredential();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
@@ -80,7 +82,6 @@ describe("auth external oauth helpers", () => {
},
]);
const { shouldPersistExternalOAuthProfile } = await import("./external-auth.js");
const shouldPersist = shouldPersistExternalOAuthProfile({
store: createStore({ "openai-codex:default": credential }),
profileId: "openai-codex:default",
@@ -90,7 +91,7 @@ describe("auth external oauth helpers", () => {
expect(shouldPersist).toBe(true);
});
it("keeps stale local copies when runtime overlay no longer matches", async () => {
it("keeps stale local copies when runtime overlay no longer matches", () => {
const credential = createCredential();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([
{
@@ -99,7 +100,6 @@ describe("auth external oauth helpers", () => {
},
]);
const { shouldPersistExternalOAuthProfile } = await import("./external-auth.js");
const shouldPersist = shouldPersistExternalOAuthProfile({
store: createStore({ "openai-codex:default": credential }),
profileId: "openai-codex:default",

View File

@@ -1,7 +1,8 @@
import { describe, it, expect, vi } from "vitest";
import { afterEach, beforeEach, describe, it, expect, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.js";
import { resetLogger, setLoggerOverride } from "../logging/logger.js";
import { createWarnLogCapture } from "../logging/test-helpers/warn-log-capture.js";
import { __testing as setupRegistryRuntimeTesting } from "../plugins/setup-registry.runtime.js";
import {
buildAllowedModelSet,
inferUniqueProviderFromConfiguredModels,
@@ -135,6 +136,23 @@ describe("model-selection", () => {
});
describe("isCliProvider", () => {
beforeEach(() => {
setupRegistryRuntimeTesting.resetRuntimeState();
setupRegistryRuntimeTesting.setRuntimeModuleForTest({
resolvePluginSetupCliBackend: ({ backend }) =>
backend === "claude-cli"
? {
pluginId: "anthropic",
backend: { id: "claude-cli", config: { command: "claude" } },
}
: undefined,
});
});
afterEach(() => {
setupRegistryRuntimeTesting.resetRuntimeState();
});
it("returns true for setup-registered cli backends", () => {
expect(isCliProvider("claude-cli", {} as OpenClawConfig)).toBe(true);
});

View File

@@ -3,6 +3,8 @@ import type { OpenClawConfig } from "../config/config.js";
import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js";
const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = {
anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"],
openai: ["OPENAI_API_KEY"],
voyage: ["VOYAGE_API_KEY"],
cerebras: ["CEREBRAS_API_KEY"],
"anthropic-openai": ["ANTHROPIC_API_KEY"],