test: speed up agent auth config tests

This commit is contained in:
Peter Steinberger
2026-04-08 00:29:47 +01:00
parent cda0c66258
commit c01666f7ab
6 changed files with 96 additions and 91 deletions

View File

@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeAll, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import type { AuthProfileStore } from "./types.js";
@@ -16,8 +16,7 @@ vi.mock("../../plugins/provider-runtime.runtime.js", () => ({
let resolveApiKeyForProfile: typeof import("./oauth.js").resolveApiKeyForProfile;
async function loadFreshOAuthModuleForTest() {
vi.resetModules();
async function loadOAuthModuleForTest() {
({ resolveApiKeyForProfile } = await import("./oauth.js"));
}
@@ -110,11 +109,9 @@ async function expectResolvedApiKey(params: {
});
}
describe("resolveApiKeyForProfile config compatibility", () => {
beforeEach(async () => {
await loadFreshOAuthModuleForTest();
});
beforeAll(loadOAuthModuleForTest);
describe("resolveApiKeyForProfile config compatibility", () => {
it("accepts token credentials when config mode is oauth", async () => {
const profileId = "anthropic:token";
const store: AuthProfileStore = {
@@ -201,10 +198,6 @@ describe("resolveApiKeyForProfile config compatibility", () => {
});
describe("resolveApiKeyForProfile token expiry handling", () => {
beforeEach(async () => {
await loadFreshOAuthModuleForTest();
});
it("accepts token credentials when expires is undefined", async () => {
const profileId = "anthropic:token-no-expiry";
const result = await resolveWithConfig({
@@ -301,10 +294,6 @@ describe("resolveApiKeyForProfile token expiry handling", () => {
});
describe("resolveApiKeyForProfile secret refs", () => {
beforeEach(async () => {
await loadFreshOAuthModuleForTest();
});
it("resolves api_key keyRef from env", async () => {
const profileId = "openai:default";
const previous = process.env.OPENAI_API_KEY;

View File

@@ -1,24 +1,32 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const ORIGINAL_MODELSTUDIO_API_KEY = process.env.MODELSTUDIO_API_KEY;
const ORIGINAL_XAI_API_KEY = process.env.XAI_API_KEY;
let collectProviderApiKeys: typeof import("./live-auth-keys.js").collectProviderApiKeys;
let clearPluginManifestRegistryCache: typeof import("../plugins/manifest-registry.js").clearPluginManifestRegistryCache;
async function clearManifestRegistryCache(): Promise<void> {
const { clearPluginManifestRegistryCache } = await import("../plugins/manifest-registry.js");
async function loadModulesForTest(): Promise<void> {
({ clearPluginManifestRegistryCache } = await import("../plugins/manifest-registry.js"));
({ collectProviderApiKeys } = await import("./live-auth-keys.js"));
}
function clearManifestRegistryCache(): void {
clearPluginManifestRegistryCache();
}
describe("collectProviderApiKeys", () => {
beforeEach(async () => {
vi.resetModules();
beforeAll(async () => {
vi.doUnmock("../plugins/manifest-registry.js");
vi.doUnmock("../secrets/provider-env-vars.js");
await clearManifestRegistryCache();
await loadModulesForTest();
});
afterEach(async () => {
vi.resetModules();
await clearManifestRegistryCache();
beforeEach(() => {
clearManifestRegistryCache();
});
afterEach(() => {
clearManifestRegistryCache();
if (ORIGINAL_MODELSTUDIO_API_KEY === undefined) {
delete process.env.MODELSTUDIO_API_KEY;
} else {
@@ -33,16 +41,12 @@ describe("collectProviderApiKeys", () => {
it("honors manifest-declared provider auth env vars for nonstandard provider ids", async () => {
process.env.MODELSTUDIO_API_KEY = "modelstudio-live-key";
vi.resetModules();
const { collectProviderApiKeys } = await import("./live-auth-keys.js");
expect(collectProviderApiKeys("alibaba")).toContain("modelstudio-live-key");
});
it("dedupes manifest env vars against direct provider env naming", async () => {
process.env.XAI_API_KEY = "xai-live-key";
vi.resetModules();
const { collectProviderApiKeys } = await import("./live-auth-keys.js");
expect(collectProviderApiKeys("xai")).toEqual(["xai-live-key"]);
});

View File

@@ -1,60 +1,58 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeAll, describe, expect, it, vi } from "vitest";
let listKnownProviderEnvApiKeyNames: typeof import("./model-auth-env-vars.js").listKnownProviderEnvApiKeyNames;
let GCP_VERTEX_CREDENTIALS_MARKER: typeof import("./model-auth-markers.js").GCP_VERTEX_CREDENTIALS_MARKER;
let NON_ENV_SECRETREF_MARKER: typeof import("./model-auth-markers.js").NON_ENV_SECRETREF_MARKER;
let isKnownEnvApiKeyMarker: typeof import("./model-auth-markers.js").isKnownEnvApiKeyMarker;
let isNonSecretApiKeyMarker: typeof import("./model-auth-markers.js").isNonSecretApiKeyMarker;
let resolveOAuthApiKeyMarker: typeof import("./model-auth-markers.js").resolveOAuthApiKeyMarker;
async function loadMarkerModules() {
vi.doUnmock("../plugins/manifest-registry.js");
vi.doUnmock("../secrets/provider-env-vars.js");
vi.resetModules();
return Promise.all([import("./model-auth-env-vars.js"), import("./model-auth-markers.js")]);
const [envVarsModule, markersModule] = await Promise.all([
import("./model-auth-env-vars.js"),
import("./model-auth-markers.js"),
]);
listKnownProviderEnvApiKeyNames = envVarsModule.listKnownProviderEnvApiKeyNames;
GCP_VERTEX_CREDENTIALS_MARKER = markersModule.GCP_VERTEX_CREDENTIALS_MARKER;
NON_ENV_SECRETREF_MARKER = markersModule.NON_ENV_SECRETREF_MARKER;
isKnownEnvApiKeyMarker = markersModule.isKnownEnvApiKeyMarker;
isNonSecretApiKeyMarker = markersModule.isNonSecretApiKeyMarker;
resolveOAuthApiKeyMarker = markersModule.resolveOAuthApiKeyMarker;
}
beforeEach(() => {
vi.doUnmock("../plugins/manifest-registry.js");
vi.doUnmock("../secrets/provider-env-vars.js");
});
beforeAll(loadMarkerModules);
describe("model auth markers", () => {
it("recognizes explicit non-secret markers", async () => {
const [
,
{
GCP_VERTEX_CREDENTIALS_MARKER,
NON_ENV_SECRETREF_MARKER,
isNonSecretApiKeyMarker,
resolveOAuthApiKeyMarker,
},
] = await loadMarkerModules();
it("recognizes explicit non-secret markers", () => {
expect(isNonSecretApiKeyMarker(NON_ENV_SECRETREF_MARKER)).toBe(true);
expect(isNonSecretApiKeyMarker(resolveOAuthApiKeyMarker("chutes"))).toBe(true);
expect(isNonSecretApiKeyMarker("ollama-local")).toBe(true);
expect(isNonSecretApiKeyMarker(GCP_VERTEX_CREDENTIALS_MARKER)).toBe(true);
});
it("does not treat removed provider markers as active auth markers", async () => {
const [, { isNonSecretApiKeyMarker }] = await loadMarkerModules();
it("does not treat removed provider markers as active auth markers", () => {
expect(isNonSecretApiKeyMarker("qwen-oauth")).toBe(false);
});
it("recognizes known env marker names but not arbitrary all-caps keys", async () => {
const [, { isNonSecretApiKeyMarker }] = await loadMarkerModules();
it("recognizes known env marker names but not arbitrary all-caps keys", () => {
expect(isNonSecretApiKeyMarker("OPENAI_API_KEY")).toBe(true);
expect(isNonSecretApiKeyMarker("ALLCAPS_EXAMPLE")).toBe(false);
});
it("recognizes all built-in provider env marker names", async () => {
const [{ listKnownProviderEnvApiKeyNames }, { isNonSecretApiKeyMarker }] =
await loadMarkerModules();
it("recognizes all built-in provider env marker names", () => {
for (const envVarName of listKnownProviderEnvApiKeyNames()) {
expect(isNonSecretApiKeyMarker(envVarName)).toBe(true);
}
});
it("can exclude env marker-name interpretation for display-only paths", async () => {
const [, { isNonSecretApiKeyMarker }] = await loadMarkerModules();
it("can exclude env marker-name interpretation for display-only paths", () => {
expect(isNonSecretApiKeyMarker("OPENAI_API_KEY", { includeEnvVarName: false })).toBe(false);
});
it("excludes aws-sdk env markers from known api key env marker helper", async () => {
const [, { isKnownEnvApiKeyMarker }] = await loadMarkerModules();
it("excludes aws-sdk env markers from known api key env marker helper", () => {
expect(isKnownEnvApiKeyMarker("OPENAI_API_KEY")).toBe(true);
expect(isKnownEnvApiKeyMarker("AWS_PROFILE")).toBe(false);
});

View File

@@ -1,6 +1,6 @@
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import type { AuthProfileStore } from "./auth-profiles.js";
import { makeModelFallbackCfg } from "./test-helpers/model-fallback-config-fixture.js";
@@ -52,7 +52,6 @@ const makeCfg = makeModelFallbackCfg;
let unregisterLogTransport: (() => void) | undefined;
async function loadModelFallbackProbeModules() {
vi.resetModules();
const authProfilesStoreModule = await import("./auth-profiles/store.js");
const authProfilesUsageModule = await import("./auth-profiles/usage.js");
const authProfilesOrderModule = await import("./auth-profiles/order.js");
@@ -72,6 +71,8 @@ async function loadModelFallbackProbeModules() {
setLoggerOverride = loggerModule.setLoggerOverride;
}
beforeAll(loadModelFallbackProbeModules);
function expectFallbackUsed(
result: { result: unknown; attempts: Array<{ reason?: string }> },
run: {
@@ -170,8 +171,7 @@ describe("runWithModelFallback probe logic", () => {
run,
});
beforeEach(async () => {
await loadModelFallbackProbeModules();
beforeEach(() => {
realDateNow = Date.now;
Date.now = vi.fn(() => NOW);

View File

@@ -1,13 +1,21 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import type { ExistingProviderConfig } from "./models-config.merge.js";
import type { ProviderConfig } from "./models-config.providers.secrets.js";
let NON_ENV_SECRETREF_MARKER: typeof import("./model-auth-markers.js").NON_ENV_SECRETREF_MARKER;
let mergeProviderModels: typeof import("./models-config.merge.js").mergeProviderModels;
let mergeProviders: typeof import("./models-config.merge.js").mergeProviders;
let mergeWithExistingProviderSecrets: typeof import("./models-config.merge.js").mergeWithExistingProviderSecrets;
async function loadMergeModules() {
vi.doUnmock("../plugins/manifest-registry.js");
vi.resetModules();
return Promise.all([import("./model-auth-markers.js"), import("./models-config.merge.js")]);
({ NON_ENV_SECRETREF_MARKER } = await import("./model-auth-markers.js"));
({ mergeProviderModels, mergeProviders, mergeWithExistingProviderSecrets } =
await import("./models-config.merge.js"));
}
beforeAll(loadMergeModules);
beforeEach(() => {
vi.doUnmock("../plugins/manifest-registry.js");
});
@@ -51,7 +59,6 @@ describe("models-config merge helpers", () => {
}
it("refreshes implicit model metadata while preserving explicit reasoning overrides", async () => {
const [, { mergeProviderModels }] = await loadMergeModules();
const merged = mergeProviderModels(
{
api: "openai-responses",
@@ -95,7 +102,6 @@ describe("models-config merge helpers", () => {
});
it("merges explicit providers onto trimmed keys", async () => {
const [, { mergeProviders }] = await loadMergeModules();
const merged = mergeProviders({
explicit: {
" custom ": {
@@ -111,7 +117,6 @@ describe("models-config merge helpers", () => {
});
it("keeps existing providers alongside newly configured providers in merge mode", async () => {
const [, { mergeWithExistingProviderSecrets }] = await loadMergeModules();
const merged = mergeWithExistingProviderSecrets({
nextProviders: {
"custom-proxy": {
@@ -137,7 +142,6 @@ describe("models-config merge helpers", () => {
});
it("preserves non-empty existing apiKey while explicit baseUrl wins", async () => {
const [, { mergeWithExistingProviderSecrets }] = await loadMergeModules();
const merged = mergeWithExistingProviderSecrets({
nextProviders: {
custom: createConfigProvider(),
@@ -154,7 +158,6 @@ describe("models-config merge helpers", () => {
});
it("preserves existing apiKey after explicit provider key normalization", async () => {
const [, { mergeProviders, mergeWithExistingProviderSecrets }] = await loadMergeModules();
const normalized = mergeProviders({
explicit: {
" custom ": createConfigProvider(),
@@ -174,7 +177,6 @@ describe("models-config merge helpers", () => {
});
it("preserves implicit provider headers when explicit config adds extra headers", async () => {
const [, { mergeProviderModels }] = await loadMergeModules();
const merged = mergeProviderModels(
{
baseUrl: "https://api.example.com",
@@ -211,7 +213,6 @@ describe("models-config merge helpers", () => {
});
it("replaces stale baseUrl when model api surface changes", async () => {
const [, { mergeWithExistingProviderSecrets }] = await loadMergeModules();
const merged = mergeWithExistingProviderSecrets({
nextProviders: {
custom: {
@@ -239,7 +240,6 @@ describe("models-config merge helpers", () => {
});
it("replaces stale baseUrl when only model-level apis change", async () => {
const [, { mergeWithExistingProviderSecrets }] = await loadMergeModules();
const nextProvider = createConfigProvider();
delete (nextProvider as { api?: string }).api;
nextProvider.models = [createModel({ api: "openai-responses" })];
@@ -263,7 +263,6 @@ describe("models-config merge helpers", () => {
});
it("does not preserve stale plaintext apiKey when next entry is a marker", async () => {
const [, { mergeWithExistingProviderSecrets }] = await loadMergeModules();
const merged = mergeWithExistingProviderSecrets({
nextProviders: {
custom: {
@@ -285,8 +284,6 @@ describe("models-config merge helpers", () => {
});
it("does not preserve a stale non-env marker when config returns to plaintext", async () => {
const [{ NON_ENV_SECRETREF_MARKER }, { mergeWithExistingProviderSecrets }] =
await loadMergeModules();
const merged = mergeWithExistingProviderSecrets({
nextProviders: {
custom: createConfigProvider({ apiKey: "ALLCAPS_SAMPLE" }), // pragma: allowlist secret
@@ -305,7 +302,6 @@ describe("models-config merge helpers", () => {
});
it("uses config apiKey/baseUrl when existing values are empty", async () => {
const [, { mergeWithExistingProviderSecrets }] = await loadMergeModules();
const merged = mergeWithExistingProviderSecrets({
nextProviders: {
custom: createConfigProvider(),

View File

@@ -1,23 +1,45 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { captureEnv } from "../test-utils/env.js";
vi.mock("../plugins/provider-runtime.js", () => ({
resolveProviderSyntheticAuthWithPlugin: vi.fn(),
}));
type ProviderRuntimeModule = typeof import("../plugins/provider-runtime.js");
let NON_ENV_SECRETREF_MARKER: typeof import("./model-auth-markers.js").NON_ENV_SECRETREF_MARKER;
let MINIMAX_OAUTH_MARKER: typeof import("./model-auth-markers.js").MINIMAX_OAUTH_MARKER;
let resolveApiKeyFromCredential: typeof import("./models-config.providers.secrets.js").resolveApiKeyFromCredential;
let createProviderAuthResolver: typeof import("./models-config.providers.secrets.js").createProviderAuthResolver;
let mockedResolveProviderSyntheticAuthWithPlugin: ReturnType<
typeof vi.mocked<ProviderRuntimeModule["resolveProviderSyntheticAuthWithPlugin"]>
>;
async function loadProviderAuthModules() {
vi.doUnmock("../plugins/manifest-registry.js");
vi.doUnmock("../plugins/provider-runtime.js");
vi.doUnmock("../secrets/provider-env-vars.js");
vi.resetModules();
return Promise.all([
const [providerRuntimeModule, markersModule, secretsModule] = await Promise.all([
import("../plugins/provider-runtime.js"),
import("./model-auth-markers.js"),
import("./models-config.providers.secrets.js"),
]);
mockedResolveProviderSyntheticAuthWithPlugin = vi.mocked(
providerRuntimeModule.resolveProviderSyntheticAuthWithPlugin,
);
NON_ENV_SECRETREF_MARKER = markersModule.NON_ENV_SECRETREF_MARKER;
MINIMAX_OAUTH_MARKER = markersModule.MINIMAX_OAUTH_MARKER;
resolveApiKeyFromCredential = secretsModule.resolveApiKeyFromCredential;
createProviderAuthResolver = secretsModule.createProviderAuthResolver;
}
beforeEach(() => {
vi.doUnmock("../plugins/manifest-registry.js");
vi.doUnmock("../plugins/provider-runtime.js");
vi.doUnmock("../secrets/provider-env-vars.js");
mockedResolveProviderSyntheticAuthWithPlugin.mockReset().mockReturnValue(undefined);
});
beforeAll(loadProviderAuthModules);
function buildPairedApiKeyProviders(apiKey: string) {
return {
provider: { apiKey },
@@ -26,8 +48,7 @@ function buildPairedApiKeyProviders(apiKey: string) {
}
describe("models-config provider auth provenance", () => {
it("persists env keyRef and tokenRef auth profiles as env var markers", async () => {
const [, { resolveApiKeyFromCredential }] = await loadProviderAuthModules();
it("persists env keyRef and tokenRef auth profiles as env var markers", () => {
const envSnapshot = captureEnv(["VOLCANO_ENGINE_API_KEY", "TOGETHER_API_KEY"]);
delete process.env.VOLCANO_ENGINE_API_KEY;
delete process.env.TOGETHER_API_KEY;
@@ -52,9 +73,7 @@ describe("models-config provider auth provenance", () => {
}
});
it("uses non-env marker for ref-managed profiles even when runtime plaintext is present", async () => {
const [{ NON_ENV_SECRETREF_MARKER }, { resolveApiKeyFromCredential }] =
await loadProviderAuthModules();
it("uses non-env marker for ref-managed profiles even when runtime plaintext is present", () => {
const byteplusApiKey = resolveApiKeyFromCredential({
type: "api_key",
provider: "byteplus",
@@ -74,8 +93,7 @@ describe("models-config provider auth provenance", () => {
expect(togetherApiKey).toBe(NON_ENV_SECRETREF_MARKER);
});
it("keeps oauth compatibility markers for minimax-portal", async () => {
const [{ MINIMAX_OAUTH_MARKER }] = await loadProviderAuthModules();
it("keeps oauth compatibility markers for minimax-portal", () => {
const providers = {
"minimax-portal": {
apiKey: MINIMAX_OAUTH_MARKER,
@@ -84,8 +102,7 @@ describe("models-config provider auth provenance", () => {
expect(providers["minimax-portal"]?.apiKey).toBe(MINIMAX_OAUTH_MARKER);
});
it("prefers profile auth over env auth in provider summaries to match runtime resolution", async () => {
const [, { createProviderAuthResolver }] = await loadProviderAuthModules();
it("prefers profile auth over env auth in provider summaries to match runtime resolution", () => {
const auth = createProviderAuthResolver(
{
OPENAI_API_KEY: "env-openai-key",
@@ -111,9 +128,10 @@ describe("models-config provider auth provenance", () => {
});
});
it("resolves plugin-owned synthetic auth through the provider hook", async () => {
const [{ NON_ENV_SECRETREF_MARKER }, { createProviderAuthResolver }] =
await loadProviderAuthModules();
it("resolves plugin-owned synthetic auth through the provider hook", () => {
mockedResolveProviderSyntheticAuthWithPlugin.mockReturnValue({
apiKey: "xai-plugin-key",
});
const auth = createProviderAuthResolver(
{} as NodeJS.ProcessEnv,
{