

























@@ -90,6 +90,10 @@ vi.mock("../../config/io.js", () => ({
9090})),
9191}));
929293+vi.mock("../../config/mutate.js", () => ({
94+replaceConfigFile: replaceConfigFileMock,
95+}));
96+9397vi.mock("../../config/paths.js", () => ({
9498resolveGatewayPort: resolveGatewayPortMock,
9599resolveIsNixMode: resolveIsNixModeMock,
@@ -197,13 +201,13 @@ function readFirstInstallPlanArg(): Record<string, unknown> {
197201}
198202199203function readFirstConfigWriteParams(): {
200-nextConfig?: { gateway?: { auth?: { token?: string } } };
204+nextConfig?: { gateway?: { mode?: string; auth?: { token?: string } } };
201205} {
202206const [params] = replaceConfigFileMock.mock.calls[0] ?? [];
203207if (!params || typeof params !== "object") {
204208throw new Error("expected first config write params");
205209}
206-return params as { nextConfig?: { gateway?: { auth?: { token?: string } } } };
210+return params as { nextConfig?: { gateway?: { mode?: string; auth?: { token?: string } } } };
207211}
208212209213function readFirstNodeStartupTlsEnvironmentArg(): Record<string, unknown> {
@@ -255,12 +259,12 @@ describe("runDaemonInstall", () => {
255259actionState.emitted.length = 0;
256260actionState.failed.length = 0;
257261258-loadConfigMock.mockReturnValue({ gateway: { auth: { mode: "token" } } });
262+loadConfigMock.mockReturnValue({ gateway: { mode: "local", auth: { mode: "token" } } });
259263readConfigFileSnapshotMock.mockResolvedValue({
260264exists: false,
261265valid: true,
262266config: {},
263-sourceConfig: { gateway: { auth: { mode: "token" } } },
267+sourceConfig: { gateway: { mode: "local", auth: { mode: "token" } } },
264268});
265269resolveGatewayPortMock.mockReturnValue(18789);
266270resolveIsNixModeMock.mockReturnValue(false);
@@ -381,7 +385,7 @@ describe("runDaemonInstall", () => {
381385exists: true,
382386valid: true,
383387config: { gateway: { auth: { mode: "token" } } },
384-sourceConfig: { gateway: { auth: { mode: "token" } } },
388+sourceConfig: { gateway: { mode: "local", auth: { mode: "token" } } },
385389});
386390387391await runDaemonInstall({ json: true });
@@ -396,6 +400,63 @@ describe("runDaemonInstall", () => {
396400expect(actionState.warnings.join("\n")).toContain("Auto-generated");
397401});
398402403+it("persists local gateway mode when installing from config missing gateway.mode", async () => {
404+readConfigFileSnapshotMock
405+.mockResolvedValueOnce({
406+exists: true,
407+valid: true,
408+config: { gateway: { auth: { mode: "token", token: "durable-token" } } },
409+sourceConfig: { gateway: { auth: { mode: "token", token: "durable-token" } } },
410+})
411+.mockResolvedValue({
412+exists: true,
413+valid: true,
414+config: {
415+gateway: { mode: "local", auth: { mode: "token", token: "durable-token" } },
416+},
417+sourceConfig: {
418+gateway: { mode: "local", auth: { mode: "token", token: "durable-token" } },
419+},
420+});
421+resolveGatewayAuthMock.mockReturnValue({
422+mode: "token",
423+token: "durable-token",
424+password: undefined,
425+allowTailscale: false,
426+});
427+428+await runDaemonInstall({ json: true });
429+430+expect(actionState.failed).toStrictEqual([]);
431+expect(replaceConfigFileMock).toHaveBeenCalledTimes(1);
432+expect(readFirstConfigWriteParams().nextConfig?.gateway?.mode).toBe("local");
433+expect(actionState.warnings).toContain(
434+"No gateway.mode found. Set gateway.mode=local for managed gateway install.",
435+);
436+expectFields(readFirstInstallPlanArg().config as Record<string, unknown>, {
437+gateway: {
438+mode: "local",
439+auth: { mode: "token", token: "durable-token" },
440+},
441+});
442+});
443+444+it("does not persist gateway mode when runtime validation fails", async () => {
445+readConfigFileSnapshotMock.mockResolvedValue({
446+exists: true,
447+valid: true,
448+config: { gateway: { auth: { mode: "token", token: "durable-token" } } },
449+sourceConfig: { gateway: { auth: { mode: "token", token: "durable-token" } } },
450+});
451+isGatewayDaemonRuntimeMock.mockReturnValue(false);
452+453+await runDaemonInstall({ json: true, runtime: "bogus" });
454+455+expect(actionState.failed[0]?.message).toContain("Invalid --runtime");
456+expect(replaceConfigFileMock).not.toHaveBeenCalled();
457+expect(installDaemonServiceAndEmitMock).not.toHaveBeenCalled();
458+});
459+399460it("continues Linux install when service probe hits a non-fatal systemd bus failure", async () => {
400461service.isLoaded.mockRejectedValueOnce(
401462new Error("systemctl is-enabled unavailable: Failed to connect to bus"),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。