/** * Ticket #68 — "Desktop only" is a code/test guard, not just a claim. * * The web client never mounts `ProjectsView` (it routes through `features/web`), * so the Deployment surface is unreachable there *today*. This pins that: the * web feature must not import the settings surface, and the web transport must * refuse the embedded-server port rather than reach for Tauri (absent on web). */ import { describe, it, expect } from "vitest"; import { readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import { WebDesktopServerGateway } from "@/adapters/http/unsupported"; import type { GatewayError } from "@/domain"; const WEB_FEATURE_DIR = join(process.cwd(), "src", "features", "web"); function collectSourceFiles(dir: string, out: string[] = []): string[] { for (const entry of readdirSync(dir)) { const full = join(dir, entry); if (statSync(full).isDirectory()) collectSourceFiles(full, out); else if (/\.tsx?$/.test(entry)) out.push(full); } return out; } describe("the Deployment surface is desktop-only", () => { it("features/web does not import the settings surface", () => { const offenders = collectSourceFiles(WEB_FEATURE_DIR).filter((file) => /@\/features\/settings/.test(readFileSync(file, "utf8")), ); expect(offenders, `offending files: ${offenders.join(", ")}`).toEqual([]); }); it("the web transport refuses the embedded-server port", async () => { const gateway = new WebDesktopServerGateway(); // Every real operation fails explicitly, with a stable code the UI can branch on. for (const call of [ () => gateway.getExposureSettings(), () => gateway.status(), () => gateway.start(), () => gateway.stop(), () => gateway.saveExposureSettings({ mode: "localOnly", autoStart: false, port: 0, trustedProxies: [], }), () => gateway.previewExposure({ mode: "localOnly", autoStart: false, port: 0, trustedProxies: [], }), ]) { const error: Partial = { code: "UNSUPPORTED_ON_WEB" }; await expect(call()).rejects.toMatchObject(error); } }); it("web status subscription is inert rather than throwing", async () => { // Teardown must stay callable: consumers unsubscribe unconditionally. const unsubscribe = await new WebDesktopServerGateway().onStatusChanged(() => { throw new Error("must never fire on web"); }); expect(() => unsubscribe()).not.toThrow(); }); });