1506 lines · 56.3 KB · TypeScript
RawDownload
import {  DesktopBackendBootstrap,  type DesktopBackendBootstrap as DesktopBackendBootstrapValue,  DesktopTelemetryControlMessage,} from "@t3tools/contracts";import { assert, describe, it } from "@effect/vitest";import * as Deferred from "effect/Deferred";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as PlatformError from "effect/PlatformError";import * as Queue from "effect/Queue";import * as Ref from "effect/Ref";import * as Schema from "effect/Schema";import * as Sink from "effect/Sink";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import * as TestClock from "effect/testing/TestClock";import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import * as DesktopBackendManager from "./DesktopBackendManager.ts";import * as DesktopObservability from "../app/DesktopObservability.ts";import * as DesktopTelemetryPublisher from "../telemetry/DesktopTelemetryPublisher.ts";import * as DesktopWslEnvironment from "../wsl/DesktopWslEnvironment.ts"; const decodeDesktopBackendBootstrap = Schema.decodeEffect(  Schema.fromJsonString(DesktopBackendBootstrap),);const isBackendProcessError = Schema.is(DesktopBackendManager.BackendProcessError);const encodeDesktopTelemetryControl = Schema.encodeSync(  Schema.fromJsonString(DesktopTelemetryControlMessage),); const baseConfig: DesktopBackendManager.DesktopBackendStartConfig = {  executablePath: "/electron",  args: ["/server/bin.mjs", "--bootstrap-fd", "3"],  entryPath: "/server/bin.mjs",  cwd: "/server",  env: { ELECTRON_RUN_AS_NODE: "1" },  bootstrap: {    mode: "desktop",    noBrowser: true,    port: 3773,    t3Home: "/tmp/t3",    host: "127.0.0.1",    desktopBootstrapToken: "token",    tailscaleServeEnabled: false,    tailscaleServePort: 443,    desktopTelemetryFd: 4,    desktopTelemetryControlFd: 5,  },  bootstrapDelivery: "fd3",  extendEnv: true,  httpBaseUrl: new URL("http://127.0.0.1:3773"),  captureOutput: true,  preflightFailure: Option.none(),}; const configWithObservability: DesktopBackendBootstrapValue = {  ...baseConfig.bootstrap,  tailscaleServeEnabled: true,  desktopTelemetryFd: 4,  otlpTracesUrl: "http://127.0.0.1:4318/v1/traces",}; function makeProcess(options?: {  readonly stdout?: Stream.Stream<Uint8Array, PlatformError.PlatformError>;  readonly stderr?: Stream.Stream<Uint8Array, PlatformError.PlatformError>;  readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode, PlatformError.PlatformError>;  readonly kill?: ChildProcessSpawner.ChildProcessHandle["kill"];  readonly getOutputFd?: ChildProcessSpawner.ChildProcessHandle["getOutputFd"];}): ChildProcessSpawner.ChildProcessHandle {  return ChildProcessSpawner.makeHandle({    pid: ChildProcessSpawner.ProcessId(123),    stdout: options?.stdout ?? Stream.empty,    stderr: options?.stderr ?? Stream.empty,    all: Stream.merge(options?.stdout ?? Stream.empty, options?.stderr ?? Stream.empty),    exitCode: options?.exitCode ?? Effect.succeed(ChildProcessSpawner.ExitCode(0)),    isRunning: Effect.succeed(false),    kill: options?.kill ?? (() => Effect.void),    stdin: Sink.drain,    getInputFd: () => Sink.drain,    getOutputFd: options?.getOutputFd ?? (() => Stream.empty),    unref: Effect.succeed(Effect.void),  });} function responseForRequest(  request: HttpClientRequest.HttpClientRequest,  status: number,): HttpClientResponse.HttpClientResponse {  return HttpClientResponse.fromWeb(request, new Response(null, { status }));} function httpClientLayer(  handler: (    request: HttpClientRequest.HttpClientRequest,  ) => Effect.Effect<HttpClientResponse.HttpClientResponse>,) {  return Layer.succeed(    HttpClient.HttpClient,    HttpClient.make((request) => handler(request)),  );} const healthyHttpClientLayer = httpClientLayer((request) =>  Effect.succeed(responseForRequest(request, 200)),); function decodeBootstrap(raw: string) {  return decodeDesktopBackendBootstrap(raw);} interface MakeInstanceInput {  readonly spawnerLayer: Layer.Layer<ChildProcessSpawner.ChildProcessSpawner>;  readonly httpClientLayer?: Layer.Layer<HttpClient.HttpClient>;  readonly backendOutputLog?: Partial<DesktopObservability.DesktopBackendOutputLogShape>;  readonly onReady?: Effect.Effect<void>;  readonly onShutdown?: Effect.Effect<void>;  readonly onPreflightFailed?: (    failure: DesktopBackendManager.PreflightFailure,  ) => Effect.Effect<boolean>;  readonly config?: DesktopBackendManager.DesktopBackendStartConfig;  readonly configResolve?: Effect.Effect<    DesktopBackendManager.DesktopBackendStartConfig,    PlatformError.PlatformError  >;  readonly desktopTelemetryStream?: Stream.Stream<Uint8Array>;  readonly desktopTelemetryPublisher?: Partial<    DesktopTelemetryPublisher.DesktopTelemetryPublisher["Service"]  >;  readonly pruneRuntimes?: (distro: string | null, runtimeId: string) => Effect.Effect<void>;} // Helper that constructs a primary backend instance using the factory// directly. The factory's deps (FileSystem, ChildProcessSpawner,// HttpClient, DesktopBackendOutputLogFactory) are provided per-test via// a scoped layer; tests yield the returned Effect inside `Effect.scoped`// to drive the instance's lifecycle.function makeTestInstance(input: MakeInstanceInput) {  const stubLog: DesktopObservability.DesktopBackendOutputLogShape = {    beginSession: () => Effect.void,    writeOutputChunk: () => Effect.void,    persistFailureSnapshot: () => Effect.void,    persistFailure: () => Effect.void,    discardSession: Effect.void,    ...input.backendOutputLog,  };  const servicesLayer = Layer.mergeAll(    FileSystem.layerNoop({      exists: () => Effect.succeed(true),    }),    input.spawnerLayer,    input.httpClientLayer ?? healthyHttpClientLayer,    Layer.succeed(DesktopObservability.DesktopBackendOutputLogFactory, {      forInstance: () => Effect.succeed(stubLog),    } satisfies DesktopObservability.DesktopBackendOutputLogFactory["Service"]),    Layer.succeed(DesktopTelemetryPublisher.DesktopTelemetryPublisher, {      latest: Effect.succeed(Option.none()),      changes: Stream.empty,      encoded: input.desktopTelemetryStream ?? Stream.empty,      handleControl: () => Effect.void,      handleControlForSource: (_sourceId, message) =>        (input.desktopTelemetryPublisher?.handleControl ?? (() => Effect.void))(message),      removeControlSource: () => Effect.void,      publishUpdateReport: () => Effect.void,      updateRequests: Stream.empty,      updateCommits: Stream.empty,      updateCancellations: Stream.empty,      ...input.desktopTelemetryPublisher,    }),    DesktopWslEnvironment.layerTest(      input.pruneRuntimes === undefined ? {} : { pruneRuntimes: input.pruneRuntimes },    ),  );   const instance = DesktopBackendManager.makeBackendInstance({    id: DesktopBackendManager.PRIMARY_INSTANCE_ID,    label: Effect.succeed("Windows"),    configResolve: input.configResolve ?? Effect.succeed(input.config ?? baseConfig),    ...(input.onReady ? { onReady: () => input.onReady! } : {}),    ...(input.onShutdown ? { onShutdown: () => input.onShutdown! } : {}),    ...(input.onPreflightFailed ? { onPreflightFailed: input.onPreflightFailed } : {}),  });   return instance.pipe(Effect.provide(servicesLayer));} describe("DesktopBackendManager", () => {  it.effect("spawns the backend with fd3 bootstrap and fd4 telemetry", () =>    Effect.scoped(      Effect.gen(function* () {        let spawnedCommand: ChildProcess.Command | undefined;        let bootstrapJson = "";        let telemetryJson = "";        let readyCount = 0;        const ready = yield* Deferred.make<void>();        const exited = yield* Queue.unbounded<void>();         const spawnerLayer = Layer.succeed(          ChildProcessSpawner.ChildProcessSpawner,          ChildProcessSpawner.make((command) =>            Effect.gen(function* () {              spawnedCommand = command;              if (command._tag === "StandardCommand") {                const fd3 = command.options.additionalFds?.fd3;                if (fd3?.type === "input" && fd3.stream) {                  bootstrapJson = yield* fd3.stream.pipe(Stream.decodeText(), Stream.mkString);                }                const fd4 = command.options.additionalFds?.fd4;                if (fd4?.type === "input" && fd4.stream) {                  telemetryJson = yield* fd4.stream.pipe(Stream.decodeText(), Stream.mkString);                }              }               return makeProcess({                exitCode: Deferred.await(ready).pipe(Effect.as(ChildProcessSpawner.ExitCode(0))),              });            }),          ),        );         const instance = yield* makeTestInstance({          config: {            ...baseConfig,            bootstrap: configWithObservability,          },          spawnerLayer,          desktopTelemetryStream: Stream.encodeText(            Stream.make('{"version":1,"type":"desktopTelemetryHello","electronPid":123}\n'),          ),          onReady: Effect.sync(() => {            readyCount += 1;          }).pipe(Effect.andThen(Deferred.succeed(ready, void 0)), Effect.asVoid),          backendOutputLog: {            persistFailure: () => Queue.offer(exited, void 0).pipe(Effect.asVoid),          },        });         yield* instance.start;        yield* Queue.take(exited);         assert.equal(readyCount, 1);        assert.isDefined(spawnedCommand);        if (spawnedCommand._tag !== "StandardCommand") {          throw new Error("Expected backend to spawn a standard command.");        }         assert.equal(spawnedCommand.command, "/electron");        assert.deepEqual(spawnedCommand.args, ["/server/bin.mjs", "--bootstrap-fd", "3"]);        assert.equal(spawnedCommand.options.cwd, "/server");        assert.equal(spawnedCommand.options.extendEnv, true);        assert.equal(spawnedCommand.options.stdout, "pipe");        assert.equal(spawnedCommand.options.stderr, "pipe");        assert.equal(spawnedCommand.options.killSignal, "SIGTERM");        assert.isDefined(spawnedCommand.options.forceKillAfter);        assert.equal(spawnedCommand.options.additionalFds?.fd4?.type, "input");        assert.equal(spawnedCommand.options.additionalFds?.fd5?.type, "output");        assert.equal(          Duration.toMillis(Duration.fromInputUnsafe(spawnedCommand.options.forceKillAfter)),          2_000,        );         assert.deepEqual(yield* decodeBootstrap(bootstrapJson), configWithObservability);        assert.equal(          telemetryJson,          '{"version":1,"type":"desktopTelemetryHello","electronPid":123}\n',        );      }),    ),  );   it.effect("preserves the readiness timeout cause and process context", () =>    Effect.gen(function* () {      const requested = yield* Deferred.make<HttpClientRequest.HttpClientRequest>();      const layer = Layer.merge(        TestClock.layer(),        httpClientLayer((request) =>          Deferred.succeed(requested, request).pipe(Effect.andThen(Effect.never)),        ),      );       yield* Effect.gen(function* () {        const readiness = yield* DesktopBackendManager.waitForHttpReady({          executablePath: baseConfig.executablePath,          entryPath: baseConfig.entryPath,          cwd: baseConfig.cwd,          httpBaseUrl: baseConfig.httpBaseUrl,          timeout: Duration.millis(50),        }).pipe(Effect.flip, Effect.forkChild);         const request = yield* Deferred.await(requested);        assert.equal(request.url, "http://127.0.0.1:3773/.well-known/t3/environment");         yield* TestClock.adjust(Duration.millis(50));        const error = yield* Fiber.join(readiness);         assert.instanceOf(error, DesktopBackendManager.BackendReadinessTimeoutError);        assert.equal(error.executablePath, "/electron");        assert.equal(error.entryPath, "/server/bin.mjs");        assert.equal(error.cwd, "/server");        assert.equal(error.httpBaseUrl.href, "http://127.0.0.1:3773/");        assert.equal(error.readinessUrl.href, "http://127.0.0.1:3773/.well-known/t3/environment");        assert.equal(error.timeoutMs, 50);        assert.isDefined(error.cause);        assert.equal(          error.message,          "Timed out after 50ms waiting for desktop backend readiness at http://127.0.0.1:3773/.well-known/t3/environment.",        );      }).pipe(Effect.provide(layer));    }),  );   it.effect("reports bootstrap encoding failures with stable process context", () =>    Effect.gen(function* () {      const spawnerLayer = Layer.succeed(        ChildProcessSpawner.ChildProcessSpawner,        ChildProcessSpawner.make(() => Effect.die("unexpected backend spawn")),      );      const error = yield* DesktopBackendManager.runBackendProcess({        ...baseConfig,        desktopTelemetryStream: Stream.empty,        bootstrap: {          ...baseConfig.bootstrap,          port: 0,        },      }).pipe(        Effect.flip,        Effect.scoped,        Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)),      );       if (error._tag !== "BackendProcessBootstrapEncodeError") {        return assert.fail(`Expected bootstrap encode error, received ${error._tag}`);      }      assert.equal(error.executablePath, "/electron");      assert.equal(error.entryPath, "/server/bin.mjs");      assert.equal(error.cwd, "/server");      assert.equal(error.httpBaseUrl.href, "http://127.0.0.1:3773/");      assert.isDefined(error.cause);      assert.equal(        error.message,        "Failed to encode the desktop backend bootstrap payload for /server/bin.mjs.",      );      assert.isTrue(isBackendProcessError(error));    }),  );   it.effect("preserves spawn failures without deriving their message from the cause", () =>    Effect.gen(function* () {      const spawnCause = PlatformError.systemError({        _tag: "PermissionDenied",        module: "ChildProcessSpawner",        method: "spawn",        pathOrDescriptor: baseConfig.executablePath,        description: "low-level detail that must not become the public message",      });      const spawnerLayer = Layer.succeed(        ChildProcessSpawner.ChildProcessSpawner,        ChildProcessSpawner.make(() => Effect.fail(spawnCause)),      );      const error = yield* DesktopBackendManager.runBackendProcess({        ...baseConfig,        desktopTelemetryStream: Stream.empty,      }).pipe(        Effect.flip,        Effect.scoped,        Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)),      );       if (error._tag !== "BackendProcessSpawnError") {        return assert.fail(`Expected backend spawn error, received ${error._tag}`);      }      assert.equal(error.executablePath, "/electron");      assert.equal(error.entryPath, "/server/bin.mjs");      assert.equal(error.cwd, "/server");      assert.equal(error.httpBaseUrl.href, "http://127.0.0.1:3773/");      assert.strictEqual(error.cause, spawnCause);      assert.equal(        error.message,        "Failed to spawn desktop backend entry /server/bin.mjs with /electron.",      );      assert.notInclude(error.message, spawnCause.message);      assert.isTrue(isBackendProcessError(error));    }),  );   it.effect("preserves exit-status failures without copying their detail into the message", () =>    Effect.gen(function* () {      const exitCause = PlatformError.systemError({        _tag: "PermissionDenied",        module: "ChildProcess",        method: "exitCode",        description: "exit-status-secret-sentinel",      });      const spawnerLayer = Layer.succeed(        ChildProcessSpawner.ChildProcessSpawner,        ChildProcessSpawner.make(() =>          Effect.succeed(            makeProcess({              exitCode: Effect.fail(exitCause),            }),          ),        ),      );      const error = yield* DesktopBackendManager.runBackendProcess({        ...baseConfig,        desktopTelemetryStream: Stream.empty,      }).pipe(        Effect.flip,        Effect.scoped,        Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)),      );       if (error._tag !== "BackendProcessExitStatusError") {        return assert.fail(`Expected backend exit-status error, received ${error._tag}`);      }      assert.equal(error.pid, 123);      assert.equal(error.executablePath, "/electron");      assert.equal(error.entryPath, "/server/bin.mjs");      assert.equal(error.cwd, "/server");      assert.equal(error.httpBaseUrl.href, "http://127.0.0.1:3773/");      assert.strictEqual(error.cause, exitCause);      assert.equal(error.message, "Failed to read the exit status of desktop backend process 123.");      assert.notInclude(error.message, "exit-status-secret-sentinel");      assert.isTrue(isBackendProcessError(error));    }),  );   it.effect("reports output stream failures with process and stream context", () =>    Effect.gen(function* () {      const outputCause = PlatformError.systemError({        _tag: "BadResource",        module: "ChildProcess",        method: "stdout",        description: "output-stream-secret-sentinel",      });      const reported = yield* Deferred.make<DesktopBackendManager.BackendProcessOutputError>();      const spawnerLayer = Layer.succeed(        ChildProcessSpawner.ChildProcessSpawner,        ChildProcessSpawner.make(() =>          Effect.succeed(            makeProcess({              stdout: Stream.fail(outputCause),              exitCode: Deferred.await(reported).pipe(Effect.as(ChildProcessSpawner.ExitCode(0))),            }),          ),        ),      );       const exit = yield* DesktopBackendManager.runBackendProcess({        ...baseConfig,        desktopTelemetryStream: Stream.empty,        onOutputFailure: (error) => Deferred.succeed(reported, error).pipe(Effect.asVoid),      }).pipe(Effect.scoped, Effect.provide(Layer.merge(spawnerLayer, healthyHttpClientLayer)));      const error = yield* Deferred.await(reported);       assert.equal(exit.code.pipe(Option.getOrUndefined), 0);      if (error._tag !== "BackendProcessOutputReadError") {        return assert.fail(`Expected output read error, received ${error._tag}`);      }      assert.equal(error.executablePath, "/electron");      assert.equal(error.entryPath, "/server/bin.mjs");      assert.equal(error.cwd, "/server");      assert.equal(error.httpBaseUrl.href, "http://127.0.0.1:3773/");      assert.equal(error.pid, 123);      assert.equal(error.streamName, "stdout");      assert.strictEqual(error.cause, outputCause);      assert.equal(error.message, "Failed to read stdout from desktop backend process 123.");      assert.notInclude(error.message, "output-stream-secret-sentinel");    }),  );   it.effect("reports output handler failures separately from stream read failures", () =>    Effect.gen(function* () {      const chunk = new TextEncoder().encode("backend output");      const nextChunk = new TextEncoder().encode("still draining");      const outputCause = new Error("output-handler-secret-sentinel");      const reported = yield* Deferred.make<DesktopBackendManager.BackendProcessOutputError>();      const drained = yield* Deferred.make<void>();      let outputCount = 0;      const spawnerLayer = Layer.succeed(        ChildProcessSpawner.ChildProcessSpawner,        ChildProcessSpawner.make(() =>          Effect.succeed(            makeProcess({              stdout: Stream.make(chunk, nextChunk),              exitCode: Deferred.await(drained).pipe(Effect.as(ChildProcessSpawner.ExitCode(0))),            }),          ),        ),      );       const exit = yield* DesktopBackendManager.runBackendProcess({        ...baseConfig,        desktopTelemetryStream: Stream.empty,

Lines 1–500 of 1506.