import * as Http from "node:http"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, it, vi } from "@effect/vitest"; import type { OrchestrationReadModel } from "@t3tools/contracts"; import * as ConfigProvider from "effect/ConfigProvider"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Command from "effect/unstable/cli/Command"; import { FetchHttpClient } from "effect/unstable/http"; import { beforeEach } from "vitest"; import { NetService } from "@t3tools/shared/Net"; import { CliConfig, recordStartupHeartbeat, t3Cli, type CliConfigShape } from "./main"; import { ServerConfig, type ServerConfigShape } from "./config"; import { Open, type OpenShape } from "./open"; import { ProjectionSnapshotQuery } from "./orchestration/Services/ProjectionSnapshotQuery"; import { AnalyticsService } from "./telemetry/Services/AnalyticsService"; import { Server, type ServerShape } from "./wsServer"; const start = vi.fn(() => undefined); const stop = vi.fn(() => undefined); let resolvedConfig: ServerConfigShape | null = null; const serverStart = Effect.acquireRelease( Effect.gen(function* () { resolvedConfig = yield* ServerConfig; start(); return {} as unknown as Http.Server; }), () => Effect.sync(() => stop()), ); const findAvailablePort = vi.fn((preferred: number) => Effect.succeed(preferred)); // Shared service layer used by this CLI test suite. const testLayer = Layer.mergeAll( Layer.succeed(CliConfig, { cwd: "/tmp/t3-test-workspace", fixPath: Effect.void, resolveStaticDir: Effect.undefined, } satisfies CliConfigShape), Layer.succeed(NetService, { canListenOnHost: () => Effect.succeed(true), isPortAvailableOnLoopback: () => Effect.succeed(true), reserveLoopbackPort: () => Effect.succeed(0), findAvailablePort, }), Layer.succeed(Server, { start: serverStart, stopSignal: Effect.void, } satisfies ServerShape), Layer.succeed(Open, { openBrowser: (_target: string) => Effect.void, openInEditor: () => Effect.void, } satisfies OpenShape), AnalyticsService.layerTest, FetchHttpClient.layer, NodeServices.layer, ); const runCli = ( args: ReadonlyArray, env: Record = { T3CODE_NO_BROWSER: "true" }, ) => { const uniqueStateDir = `/tmp/t3-cli-state-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; return Command.runWith(t3Cli, { version: "0.0.0-test" })(args).pipe( Effect.provide( ConfigProvider.layer( ConfigProvider.fromEnv({ env: { T3CODE_STATE_DIR: uniqueStateDir, ...env, }, }), ), ), ); }; beforeEach(() => { vi.clearAllMocks(); resolvedConfig = null; start.mockImplementation(() => undefined); stop.mockImplementation(() => undefined); findAvailablePort.mockImplementation((preferred: number) => Effect.succeed(preferred)); }); it.layer(testLayer)("server CLI command", (it) => { it.effect("parses all CLI flags and wires scoped start/stop", () => Effect.gen(function* () { yield* runCli([ "--mode", "desktop", "--port", "4010", "--host", "0.0.0.0", "--state-dir", "/tmp/t3-cli-state", "--dev-url", "http://127.0.0.1:5173", "--no-browser", "--auth-token", "auth-secret", ]); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.mode, "desktop"); assert.equal(resolvedConfig?.port, 4010); assert.equal(resolvedConfig?.host, "0.0.0.0"); assert.equal(resolvedConfig?.stateDir, "/tmp/t3-cli-state"); assert.equal(resolvedConfig?.devUrl?.toString(), "http://127.0.0.1:5173/"); assert.equal(resolvedConfig?.noBrowser, true); assert.equal(resolvedConfig?.authToken, "auth-secret"); assert.equal(resolvedConfig?.autoBootstrapProjectFromCwd, false); assert.equal(resolvedConfig?.logWebSocketEvents, true); assert.equal(stop.mock.calls.length, 1); }), ); it.effect("supports --token as an alias for --auth-token", () => Effect.gen(function* () { yield* runCli(["--token", "token-secret"]); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.authToken, "token-secret"); }), ); it.effect("uses env fallbacks when flags are not provided", () => Effect.gen(function* () { yield* runCli([], { T3CODE_MODE: "desktop", T3CODE_PORT: "4999", T3CODE_HOST: "100.88.10.4", T3CODE_STATE_DIR: "/tmp/t3-env-state", VITE_DEV_SERVER_URL: "http://localhost:5173", T3CODE_NO_BROWSER: "true", T3CODE_AUTH_TOKEN: "env-token", }); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.mode, "desktop"); assert.equal(resolvedConfig?.port, 4999); assert.equal(resolvedConfig?.host, "100.88.10.4"); assert.equal(resolvedConfig?.stateDir, "/tmp/t3-env-state"); assert.equal(resolvedConfig?.devUrl?.toString(), "http://localhost:5173/"); assert.equal(resolvedConfig?.noBrowser, true); assert.equal(resolvedConfig?.authToken, "env-token"); assert.equal(resolvedConfig?.autoBootstrapProjectFromCwd, false); assert.equal(resolvedConfig?.logWebSocketEvents, true); assert.equal(findAvailablePort.mock.calls.length, 0); }), ); it.effect("prefers --mode over T3CODE_MODE", () => Effect.gen(function* () { findAvailablePort.mockImplementation((_preferred: number) => Effect.succeed(4666)); yield* runCli(["--mode", "web"], { T3CODE_MODE: "desktop", T3CODE_NO_BROWSER: "true", }); assert.deepStrictEqual(findAvailablePort.mock.calls, [[3773]]); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.mode, "web"); assert.equal(resolvedConfig?.port, 4666); assert.equal(resolvedConfig?.host, undefined); }), ); it.effect("prefers --no-browser over T3CODE_NO_BROWSER", () => Effect.gen(function* () { yield* runCli(["--no-browser"], { T3CODE_NO_BROWSER: "false", }); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.noBrowser, true); }), ); it.effect("uses dynamic port discovery in web mode when port is omitted", () => Effect.gen(function* () { findAvailablePort.mockImplementation((_preferred: number) => Effect.succeed(5444)); yield* runCli([]); assert.deepStrictEqual(findAvailablePort.mock.calls, [[3773]]); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.port, 5444); assert.equal(resolvedConfig?.mode, "web"); }), ); it.effect("uses fixed localhost defaults in desktop mode", () => Effect.gen(function* () { yield* runCli([], { T3CODE_MODE: "desktop", T3CODE_NO_BROWSER: "true", }); assert.equal(findAvailablePort.mock.calls.length, 0); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.port, 3773); assert.equal(resolvedConfig?.host, "127.0.0.1"); assert.equal(resolvedConfig?.mode, "desktop"); }), ); it.effect("allows overriding desktop host with --host", () => Effect.gen(function* () { yield* runCli(["--host", "0.0.0.0"], { T3CODE_MODE: "desktop", T3CODE_NO_BROWSER: "true", }); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.mode, "desktop"); assert.equal(resolvedConfig?.host, "0.0.0.0"); }), ); it.effect("supports CLI and env for bootstrap/log websocket toggles", () => Effect.gen(function* () { yield* runCli(["--auto-bootstrap-project-from-cwd"], { T3CODE_MODE: "desktop", T3CODE_LOG_WS_EVENTS: "false", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "false", T3CODE_NO_BROWSER: "true", }); assert.equal(start.mock.calls.length, 1); assert.equal(resolvedConfig?.autoBootstrapProjectFromCwd, true); assert.equal(resolvedConfig?.logWebSocketEvents, false); }), ); it.effect("records a startup heartbeat with thread/project counts", () => Effect.gen(function* () { const recordTelemetry = vi.fn( (_event: string, _properties?: Readonly>) => Effect.void, ); const getSnapshot = vi.fn(() => Effect.succeed({ snapshotSequence: 2, projects: [{} as OrchestrationReadModel["projects"][number]], threads: [ {} as OrchestrationReadModel["threads"][number], {} as OrchestrationReadModel["threads"][number], ], updatedAt: new Date(1).toISOString(), } satisfies OrchestrationReadModel), ); yield* recordStartupHeartbeat.pipe( Effect.provideService(ProjectionSnapshotQuery, { getSnapshot, }), Effect.provideService(AnalyticsService, { record: recordTelemetry, flush: Effect.void, }), ); assert.deepEqual(recordTelemetry.mock.calls[0], [ "server.boot.heartbeat", { threadCount: 2, projectCount: 1, }, ]); }), ); it.effect("does not start server for invalid --mode values", () => Effect.gen(function* () { yield* runCli(["--mode", "invalid"]); assert.equal(start.mock.calls.length, 0); assert.equal(stop.mock.calls.length, 0); }), ); it.effect("does not start server for invalid --dev-url values", () => Effect.gen(function* () { yield* runCli(["--dev-url", "not-a-url"]).pipe(Effect.catch(() => Effect.void)); assert.equal(start.mock.calls.length, 0); assert.equal(stop.mock.calls.length, 0); }), ); it.effect("does not start server for out-of-range --port values", () => Effect.gen(function* () { yield* runCli(["--port", "70000"]); // effect/unstable/cli renders help/errors for parse failures and returns success. assert.equal(start.mock.calls.length, 0); assert.equal(stop.mock.calls.length, 0); }), ); });