mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:30:43 +00:00
test: move custom onboarding edge cases down-stack
This commit is contained in:
@@ -1130,22 +1130,6 @@ async function runOnboardingAndReadConfig(
|
||||
return readJsonFile<ProviderAuthConfigSnapshot>(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<string, unknown> = {},
|
||||
): Promise<void> {
|
||||
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.');
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user