mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-24 06:41:16 +00:00
Fixes rough edges in the standalone install flow (install.sh -> openclaw onboard), found and verified by running the flow in a clean container and on a clean macOS Tahoe VM: - Provider auth setup failures (e.g. the preselected "Anthropic Claude CLI" option on a host without a Claude CLI login) no longer kill the whole wizard. The interactive wizard notes the error and returns to the provider picker; explicit --auth-choice automation still fails fast. - Onboarding config now persists before the channel/search/skills steps, so a crash or cancel during channel pairing no longer loses auth + gateway decisions. - With model auth skipped, finalize no longer auto-sends the "Wake up, my friend!" message (which always failed with a provider auth error). The hatch seed is gated on usable model credentials and a "Model auth missing" note explains the next step. - Search provider picker no longer labels non-key credentials (e.g. SearXNG base URL) as "API key required". - install.sh no longer warns "PATH missing npm global bin dir" with manual fix steps after it already persisted the export line; it reports the PATH was updated and how to reload the current shell. - Removed the dead interactive hooks onboarding step (setupInternalHooks); quickstart enables default hooks silently. Verified live per fix in a clean Debian/Node 24 container and on a clean macOS 26.5 Parallels VM (wizard re-prompt, SearXNG label), plus wizard/onboard test suites and tsgo:core. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
113 lines
3.6 KiB
TypeScript
113 lines
3.6 KiB
TypeScript
// Regression tests: provider auth failures re-prompt instead of killing the wizard.
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import type { RuntimeEnv } from "../runtime.js";
|
|
import { WizardCancelledError, type WizardPrompter } from "./prompts.js";
|
|
import { runSetupModelAuthStep } from "./setup.model-auth.js";
|
|
|
|
const applyAuthChoice = vi.hoisted(() => vi.fn());
|
|
const warnIfModelConfigLooksOff = vi.hoisted(() => vi.fn());
|
|
const resolvePreferredProviderForAuthChoice = vi.hoisted(() => vi.fn());
|
|
const promptDefaultModel = vi.hoisted(() => vi.fn());
|
|
const applyPrimaryModel = vi.hoisted(() => vi.fn((config: unknown) => config));
|
|
const promptAuthChoiceGrouped = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("../commands/auth-choice.js", () => ({
|
|
applyAuthChoice,
|
|
warnIfModelConfigLooksOff,
|
|
resolvePreferredProviderForAuthChoice,
|
|
}));
|
|
|
|
vi.mock("../commands/model-picker.js", () => ({
|
|
applyPrimaryModel,
|
|
promptDefaultModel,
|
|
}));
|
|
|
|
vi.mock("../commands/auth-choice-prompt.js", () => ({
|
|
KEEP_CURRENT_AUTH_CHOICE: "__keep_current__",
|
|
promptAuthChoiceGrouped,
|
|
}));
|
|
|
|
vi.mock("../agents/auth-profiles.runtime.js", () => ({
|
|
ensureAuthProfileStore: vi.fn(() => ({ profiles: {} })),
|
|
}));
|
|
|
|
function createPrompter(): WizardPrompter {
|
|
return {
|
|
intro: vi.fn(),
|
|
outro: vi.fn(),
|
|
note: vi.fn(),
|
|
select: vi.fn(),
|
|
multiselect: vi.fn(),
|
|
text: vi.fn(),
|
|
confirm: vi.fn(),
|
|
progress: vi.fn(() => ({ stop: vi.fn(), update: vi.fn() })),
|
|
disableBackNavigation: vi.fn(),
|
|
} as unknown as WizardPrompter;
|
|
}
|
|
|
|
function createRuntime(): RuntimeEnv {
|
|
return { log: vi.fn(), error: vi.fn(), exit: vi.fn() } as unknown as RuntimeEnv;
|
|
}
|
|
|
|
describe("runSetupModelAuthStep provider failures", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
promptDefaultModel.mockResolvedValue({});
|
|
warnIfModelConfigLooksOff.mockResolvedValue(undefined);
|
|
});
|
|
|
|
it("re-prompts after a provider setup error instead of aborting", async () => {
|
|
promptAuthChoiceGrouped.mockResolvedValueOnce("anthropic-cli").mockResolvedValueOnce("skip");
|
|
applyAuthChoice.mockRejectedValueOnce(
|
|
new Error("Claude CLI is not authenticated on this host."),
|
|
);
|
|
const prompter = createPrompter();
|
|
|
|
const result = await runSetupModelAuthStep({
|
|
config: {},
|
|
opts: {},
|
|
prompter,
|
|
runtime: createRuntime(),
|
|
workspaceDir: "/tmp/workspace",
|
|
});
|
|
|
|
expect(result).toEqual({});
|
|
expect(promptAuthChoiceGrouped).toHaveBeenCalledTimes(2);
|
|
expect(prompter.note).toHaveBeenCalledWith(
|
|
expect.stringContaining("Claude CLI is not authenticated on this host."),
|
|
"Provider setup failed",
|
|
);
|
|
});
|
|
|
|
it("still fails loudly when the auth choice came from a flag", async () => {
|
|
applyAuthChoice.mockRejectedValueOnce(
|
|
new Error("Claude CLI is not authenticated on this host."),
|
|
);
|
|
|
|
await expect(
|
|
runSetupModelAuthStep({
|
|
config: {},
|
|
opts: { authChoice: "anthropic-cli" },
|
|
prompter: createPrompter(),
|
|
runtime: createRuntime(),
|
|
workspaceDir: "/tmp/workspace",
|
|
}),
|
|
).rejects.toThrow("Claude CLI is not authenticated");
|
|
});
|
|
|
|
it("propagates wizard cancellation from provider setup", async () => {
|
|
promptAuthChoiceGrouped.mockResolvedValueOnce("anthropic-cli");
|
|
applyAuthChoice.mockRejectedValueOnce(new WizardCancelledError());
|
|
|
|
await expect(
|
|
runSetupModelAuthStep({
|
|
config: {},
|
|
opts: {},
|
|
prompter: createPrompter(),
|
|
runtime: createRuntime(),
|
|
workspaceDir: "/tmp/workspace",
|
|
}),
|
|
).rejects.toThrow(WizardCancelledError);
|
|
});
|
|
});
|