diff --git a/src/commands/onboard-non-interactive.provider-auth.test.ts b/src/commands/onboard-non-interactive.provider-auth.test.ts index d6c5a14c6ed..851c8cdd40a 100644 --- a/src/commands/onboard-non-interactive.provider-auth.test.ts +++ b/src/commands/onboard-non-interactive.provider-auth.test.ts @@ -1130,22 +1130,6 @@ async function runOnboardingAndReadConfig( return readJsonFile(env.configPath); } -const CUSTOM_LOCAL_BASE_URL = "https://models.custom.local/v1"; -const CUSTOM_LOCAL_MODEL_ID = "local-large"; - -async function runCustomLocalNonInteractive( - runtime: NonInteractiveRuntime, - overrides: Record = {}, -): Promise { - await runNonInteractiveSetupWithDefaults(runtime, { - authChoice: "custom-api-key", - customBaseUrl: CUSTOM_LOCAL_BASE_URL, - customModelId: CUSTOM_LOCAL_MODEL_ID, - skipSkills: true, - ...overrides, - }); -} - async function expectApiKeyProfile(params: { profileId: string; provider: string; @@ -1565,77 +1549,4 @@ describe("onboard (non-interactive): provider auth", () => { }, ); }); - - it("fails fast for custom provider ref mode when --custom-api-key is set but CUSTOM_API_KEY env is missing", async () => { - await withOnboardEnv("openclaw-onboard-custom-provider-ref-flag-", async ({ runtime }) => { - const providedSecret = "custom-inline-key-should-not-leak"; // pragma: allowlist secret - await withEnvAsync({ CUSTOM_API_KEY: undefined }, async () => { - let thrown: Error | undefined; - try { - await runCustomLocalNonInteractive(runtime, { - secretInputMode: "ref", // pragma: allowlist secret - customApiKey: providedSecret, - }); - } catch (error) { - thrown = error as Error; - } - expect(thrown).toBeDefined(); - const message = thrown?.message ?? ""; - expect(message).toContain( - "--custom-api-key cannot be used with --secret-input-mode ref unless CUSTOM_API_KEY is set in env.", - ); - expect(message).toContain( - "Set CUSTOM_API_KEY in env and omit --custom-api-key, or use --secret-input-mode plaintext.", - ); - expect(message).not.toContain(providedSecret); - }); - }); - }); - - it("fails custom provider auth when compatibility is invalid", async () => { - await withOnboardEnv( - "openclaw-onboard-custom-provider-invalid-compat-", - async ({ runtime }) => { - await expect( - runNonInteractiveSetupWithDefaults(runtime, { - authChoice: "custom-api-key", - customBaseUrl: "https://models.custom.local/v1", - customModelId: "local-large", - customCompatibility: "xmlrpc", - skipSkills: true, - }), - ).rejects.toThrow('Invalid --custom-compatibility (use "openai" or "anthropic").'); - }, - ); - }); - - it("fails custom provider auth when explicit provider id is invalid", async () => { - await withOnboardEnv("openclaw-onboard-custom-provider-invalid-id-", async ({ runtime }) => { - await expect( - runNonInteractiveSetupWithDefaults(runtime, { - authChoice: "custom-api-key", - customBaseUrl: "https://models.custom.local/v1", - customModelId: "local-large", - customProviderId: "!!!", - skipSkills: true, - }), - ).rejects.toThrow( - "Invalid custom provider config: Custom provider ID must include letters, numbers, or hyphens.", - ); - }); - }); - - it("fails inferred custom auth when required flags are incomplete", async () => { - await withOnboardEnv( - "openclaw-onboard-custom-provider-missing-required-", - async ({ runtime }) => { - await expect( - runNonInteractiveSetupWithDefaults(runtime, { - customApiKey: "custom-test-key", // pragma: allowlist secret - skipSkills: true, - }), - ).rejects.toThrow('Auth choice "custom-api-key" requires a base URL and model ID.'); - }, - ); - }); }); diff --git a/src/commands/onboard-non-interactive/api-keys.test.ts b/src/commands/onboard-non-interactive/api-keys.test.ts index 8e0da116281..55b61d3562b 100644 --- a/src/commands/onboard-non-interactive/api-keys.test.ts +++ b/src/commands/onboard-non-interactive/api-keys.test.ts @@ -60,37 +60,54 @@ describe("resolveNonInteractiveApiKey", () => { expect(runtime.exit).not.toHaveBeenCalled(); }); - it("rejects flag input in secret-ref mode without broad env discovery", async () => { - const runtime = createRuntime(); - resolveEnvApiKey.mockReturnValue(null); - const previousXaiApiKey = process.env.XAI_API_KEY; - delete process.env.XAI_API_KEY; + it.each([ + { + provider: "xai", + flagValue: "xai-flag-key", + flagName: "--xai-api-key", + envVar: "XAI_API_KEY", + }, + { + provider: "custom-models-custom-local", + flagValue: "custom-inline-key-should-not-leak", + flagName: "--custom-api-key", + envVar: "CUSTOM_API_KEY", + }, + ])( + "rejects $flagName input in secret-ref mode without broad env discovery", + async ({ provider, flagValue, flagName, envVar }) => { + const runtime = createRuntime(); + resolveEnvApiKey.mockReturnValue(null); + const previousValue = process.env[envVar]; + delete process.env[envVar]; - try { - const result = await resolveNonInteractiveApiKey({ - provider: "xai", - cfg: {}, - flagValue: "xai-flag-key", - flagName: "--xai-api-key", - envVar: "XAI_API_KEY", - runtime: runtime as never, - secretInputMode: "ref", - }); + try { + const result = await resolveNonInteractiveApiKey({ + provider, + cfg: {}, + flagValue, + flagName, + envVar, + runtime: runtime as never, + secretInputMode: "ref", + }); - expect(result).toBeNull(); - expect(resolveEnvApiKey).not.toHaveBeenCalled(); - expect(runtime.exit).toHaveBeenCalledWith(1); - expect(runtime.error).toHaveBeenCalledWith( - expect.stringContaining("--secret-input-mode ref"), - ); - } finally { - if (previousXaiApiKey === undefined) { - delete process.env.XAI_API_KEY; - } else { - process.env.XAI_API_KEY = previousXaiApiKey; + const errorText = runtime.error.mock.calls.map(([message]) => String(message)).join("\n"); + expect(result).toBeNull(); + expect(resolveEnvApiKey).not.toHaveBeenCalled(); + expect(runtime.exit).toHaveBeenCalledWith(1); + expect(errorText).toContain(flagName); + expect(errorText).toContain(envVar); + expect(errorText).not.toContain(flagValue); + } finally { + if (previousValue === undefined) { + delete process.env[envVar]; + } else { + process.env[envVar] = previousValue; + } } - } - }); + }, + ); it("returns explicit env fallback keys when provider env discovery misses", async () => { const runtime = createRuntime();