1371 lines · 55.7 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, describe, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Logger from "effect/Logger";import * as ManagedRuntime from "effect/ManagedRuntime";import * as Option from "effect/Option";import * as Path from "effect/Path";import * as PlatformError from "effect/PlatformError";import * as Schema from "effect/Schema";import { ChildProcessSpawner } from "effect/unstable/process"; import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";import * as DesktopBackendConfiguration from "./DesktopBackendConfiguration.ts";import * as DesktopConfig from "../app/DesktopConfig.ts";import * as DesktopServerExposure from "./DesktopServerExposure.ts";import * as DesktopAppSettings from "../settings/DesktopAppSettings.ts";import * as DesktopWslEnvironment from "../wsl/DesktopWslEnvironment.ts";import * as DesktopWslServerTree from "../wsl/DesktopWslServerTree.ts"; const PersistedServerObservabilitySettingsDocument = Schema.Struct({  observability: Schema.Struct({    otlpTracesUrl: Schema.String,    otlpMetricsUrl: Schema.String,  }),}); const encodePersistedServerObservabilitySettingsDocument = Schema.encodeEffect(  Schema.fromJsonString(PersistedServerObservabilitySettingsDocument),); const isDesktopBackendObservabilitySettingsReadError = Schema.is(  DesktopBackendConfiguration.DesktopBackendObservabilitySettingsReadError,); const serverExposureLayer = Layer.succeed(DesktopServerExposure.DesktopServerExposure, {  getState: Effect.die("unexpected getState"),  backendConfig: Effect.succeed({    port: 4888,    bindHost: "0.0.0.0",    httpBaseUrl: new URL("http://127.0.0.1:4888"),    tailscaleServeEnabled: true,    tailscaleServePort: 8443,  }),  configureFromSettings: () => Effect.die("unexpected configureFromSettings"),  setMode: () => Effect.die("unexpected setMode"),  setTailscaleServeEnabled: () => Effect.die("unexpected setTailscaleServeEnabled"),  getAdvertisedEndpoints: Effect.succeed([]),} satisfies DesktopServerExposure.DesktopServerExposure["Service"]); function makeEnvironmentLayer(  baseDir: string,  options?: {    readonly appPath?: string;    readonly dirname?: string;    readonly isPackaged?: boolean;    readonly devServerUrl?: string;    readonly platform?: NodeJS.Platform;    readonly resourcesPath?: string;    readonly appVersion?: string;    readonly processArch?: NodeJS.Architecture;  },) {  return DesktopEnvironment.layer({    dirname: options?.dirname ?? "/repo/apps/desktop/src",    homeDirectory: baseDir,    platform: options?.platform ?? "darwin",    processArch: options?.processArch ?? "x64",    appVersion: options?.appVersion ?? "1.2.3",    appPath: options?.appPath ?? "/repo",    isPackaged: options?.isPackaged ?? true,    resourcesPath: options?.resourcesPath ?? "/missing/resources",    runningUnderArm64Translation: false,  }).pipe(    Layer.provide(      Layer.mergeAll(        NodeServices.layer,        DesktopConfig.layerTest({          T3CODE_HOME: baseDir,          T3CODE_PORT: "9999",          T3CODE_MODE: "desktop",          T3CODE_DESKTOP_LAN_HOST: "192.168.1.50",          VITE_DEV_SERVER_URL: options?.devServerUrl,        }),      ),    ),  );} const restoreEnv = (name: string, value: string | undefined) => {  if (value === undefined) {    delete process.env[name];  } else {    process.env[name] = value;  }}; const withHarness = <A, E, R>(  effect: Effect.Effect<    A,    E,    | R    | DesktopEnvironment.DesktopEnvironment    | FileSystem.FileSystem    | DesktopBackendConfiguration.DesktopBackendConfiguration  >,) =>  Effect.gen(function* () {    const fileSystem = yield* FileSystem.FileSystem;    const baseDir = yield* fileSystem.makeTempDirectoryScoped({      prefix: "t3-desktop-backend-config-test-",    });     return yield* effect.pipe(      Effect.provide(        DesktopBackendConfiguration.layer.pipe(          Layer.provideMerge(serverExposureLayer),          Layer.provideMerge(DesktopAppSettings.layerTest()),          Layer.provideMerge(DesktopWslEnvironment.layerTest()),          Layer.provideMerge(DesktopWslServerTree.layerTest()),          Layer.provideMerge(makeEnvironmentLayer(baseDir)),        ),      ),    );  }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)); interface PackagedWslHarnessContext {  readonly baseDir: string;  readonly archivePath: string;  readonly hashPath: string;  readonly archiveHash: string;  readonly mountedAppRoot: string;  readonly mountedEntryPath: string;} const withPackagedWslHarness = <A, E, R>(  input: {    readonly archiveHash: string;    readonly wsl: (      context: PackagedWslHarnessContext,    ) => DesktopWslEnvironment.DesktopWslEnvironmentTestStub;    readonly forbidFallback?: string;    readonly cleanupLegacy?: Effect.Effect<void>;    readonly forbidCleanup?: string;  },  effect: (    context: PackagedWslHarnessContext,  ) => Effect.Effect<    A,    E,    R | FileSystem.FileSystem | Path.Path | DesktopBackendConfiguration.DesktopBackendConfiguration  >,) =>  Effect.gen(function* () {    const fileSystem = yield* FileSystem.FileSystem;    const path = yield* Path.Path;    const baseDir = yield* fileSystem.makeTempDirectoryScoped({      prefix: "t3-desktop-backend-config-test-",    });    const archivePath = path.join(baseDir, "wsl-runtime.tar.gz");    const hashPath = `${archivePath}.sha256`;    const mountedAppRoot = "/mnt/c/app.asar.unpacked";    const mountedEntryPath = path.join(baseDir, "app.asar.unpacked/apps/server/dist/bin.mjs");    yield* fileSystem.makeDirectory(path.dirname(mountedEntryPath), { recursive: true });    yield* fileSystem.writeFileString(mountedEntryPath, "");    yield* fileSystem.writeFileString(archivePath, "archive");    yield* fileSystem.writeFileString(hashPath, `${input.archiveHash}\n`);     const context = {      baseDir,      archivePath,      hashPath,      archiveHash: input.archiveHash,      mountedAppRoot,      mountedEntryPath,    } satisfies PackagedWslHarnessContext;    const serverTreeLayer = input.forbidFallback      ? Layer.succeed(          DesktopWslServerTree.DesktopWslServerTree,          DesktopWslServerTree.DesktopWslServerTree.of({            ensure: Effect.die(input.forbidFallback),            cleanupLegacy: input.forbidCleanup              ? Effect.die(input.forbidCleanup)              : (input.cleanupLegacy ?? Effect.void),          }),        )      : DesktopWslServerTree.layerTest({          result: { ok: true, root: path.join(baseDir, "app.asar.unpacked") },          cleanupLegacy: input.cleanupLegacy ?? Effect.void,        });     return yield* effect(context).pipe(      Effect.provide(        DesktopBackendConfiguration.layer.pipe(          Layer.provideMerge(serverExposureLayer),          Layer.provideMerge(DesktopAppSettings.layerTest()),          Layer.provideMerge(serverTreeLayer),          Layer.provideMerge(            DesktopWslEnvironment.layerTest({              isAvailable: true,              distros: [{ name: "Ubuntu", isDefault: true, version: 2 }],              windowsToWslPath: () => Option.some(mountedAppRoot),              getDistroIp: () => Option.some("172.27.0.99"),              ...input.wsl(context),            }),          ),          Layer.provideMerge(            makeEnvironmentLayer(baseDir, {              appPath: baseDir,              platform: "win32",              resourcesPath: baseDir,            }),          ),        ),      ),    );  }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)); describe("DesktopBackendConfiguration", () => {  it("accepts only normalized SHA-256 archive identities", () => {    assert.equal(      DesktopBackendConfiguration.parseWslRuntimeArchiveHash(`  ${"A".repeat(64)}\n`),      "a".repeat(64),    );    assert.isNull(DesktopBackendConfiguration.parseWslRuntimeArchiveHash("abc123"));  });   it.effect("resolvePrimary produces a stable scoped bootstrap token", () =>    withHarness(      Effect.gen(function* () {        const environment = yield* DesktopEnvironment.DesktopEnvironment;        const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;         const first = yield* configuration.resolvePrimary;        const second = yield* configuration.resolvePrimary;         assert.equal(first.executablePath, process.execPath);        assert.equal(first.entryPath, environment.backendEntryPath);        assert.equal(first.cwd, environment.backendCwd);        assert.equal(first.captureOutput, true);        assert.equal(first.env.ELECTRON_RUN_AS_NODE, "1");        assert.isUndefined(first.env.T3CODE_PORT);        assert.isUndefined(first.env.T3CODE_MODE);        assert.isUndefined(first.env.T3CODE_DESKTOP_LAN_HOST);         assert.equal(first.bootstrap.mode, "desktop");        assert.equal(first.bootstrap.noBrowser, true);        assert.equal(first.bootstrap.port, 4888);        assert.equal(first.bootstrap.host, "0.0.0.0");        assert.equal(first.bootstrap.t3Home, environment.baseDir);        assert.equal(first.bootstrap.tailscaleServeEnabled, true);        assert.equal(first.bootstrap.tailscaleServePort, 8443);        assert.match(first.bootstrap.desktopBootstrapToken, /^[0-9a-f]{48}$/i);        assert.equal(second.bootstrap.desktopBootstrapToken, first.bootstrap.desktopBootstrapToken);      }),    ),  );   it.effect("resolvePrimary starts from server.asar without materializing the WSL tree", () =>    Effect.gen(function* () {      const fileSystem = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const baseDir = yield* fileSystem.makeTempDirectoryScoped({        prefix: "t3-desktop-backend-config-test-",      });      const resourcesPath = path.join(baseDir, "resources");       const config = yield* Effect.gen(function* () {        const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;        return yield* configuration.resolvePrimary;      }).pipe(        Effect.provide(          DesktopBackendConfiguration.layer.pipe(            Layer.provideMerge(serverExposureLayer),            Layer.provideMerge(DesktopAppSettings.layerTest()),            Layer.provideMerge(DesktopWslEnvironment.layerTest()),            Layer.provideMerge(              Layer.succeed(                DesktopWslServerTree.DesktopWslServerTree,                DesktopWslServerTree.DesktopWslServerTree.of({                  ensure: Effect.die("Windows primary must not extract the WSL server tree"),                  cleanupLegacy: Effect.die("Windows primary must not clean the WSL server tree"),                }),              ),            ),            Layer.provideMerge(              makeEnvironmentLayer(baseDir, {                appPath: `${resourcesPath}/app.asar`,                platform: "win32",                resourcesPath,              }),            ),          ),        ),      );       assert.equal(        config.entryPath,        path.join(resourcesPath, "server.asar/apps/server/dist/bin.mjs"),      );      assert.equal(config.env.ELECTRON_RUN_AS_NODE, "1");    }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),  );   it.effect("resolveWsl reuses the primary's bootstrap token", () =>    withHarness(      Effect.gen(function* () {        const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;         const primary = yield* configuration.resolvePrimary;        const wsl = yield* configuration.resolveWsl({ port: 5000, distro: null });         assert.equal(wsl.bootstrap.desktopBootstrapToken, primary.bootstrap.desktopBootstrapToken);      }),    ),  );   it.effect("resolveWsl pins a default-tracking run to the concrete default distro", () =>    Effect.gen(function* () {      const fileSystem = yield* FileSystem.FileSystem;      const path = yield* Path.Path;      const baseDir = yield* fileSystem.makeTempDirectoryScoped({        prefix: "t3-desktop-backend-config-test-",      });      const entryPath = path.join(baseDir, "apps/server/dist/bin.mjs");      yield* fileSystem.makeDirectory(path.dirname(entryPath), { recursive: true });      yield* fileSystem.writeFileString(entryPath, "");       const observedDistros: Array<string | null> = [];      const config = yield* Effect.gen(function* () {        const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;        return yield* configuration.resolveWsl({ port: 5000, distro: null });      }).pipe(        Effect.provide(          DesktopBackendConfiguration.layer.pipe(            Layer.provideMerge(serverExposureLayer),            Layer.provideMerge(DesktopAppSettings.layerTest()),            Layer.provideMerge(DesktopWslServerTree.layerTest()),            Layer.provideMerge(              DesktopWslEnvironment.layerTest({                isAvailable: true,                distros: [                  { name: "Debian", isDefault: false, version: 2 },                  { name: "Ubuntu", isDefault: true, version: 2 },                ],                windowsToWslPath: (distro) => {                  observedDistros.push(distro);                  return Option.some("/repo");                },                ensureNodePty: (distro) => {                  observedDistros.push(distro);                  return { ok: true, nodePath: "/usr/bin/node", resolvedPath: "/usr/bin:/bin" };                },                getDistroIp: (distro) => {                  observedDistros.push(distro);                  return Option.some("172.27.0.99");                },              }),            ),            Layer.provideMerge(              makeEnvironmentLayer(baseDir, {                appPath: baseDir,                platform: "win32",                resourcesPath: baseDir,              }),            ),          ),        ),      );       assert.equal(config.runningDistro, "Ubuntu");      assert.deepEqual(config.args.slice(0, 2), ["-d", "Ubuntu"]);      assert.deepEqual(observedDistros, ["Ubuntu", "Ubuntu", "Ubuntu"]);      assert.isTrue(Option.isNone(config.preflightFailure));    }).pipe(Effect.scoped, Effect.provide(NodeServices.layer)),  );   it.effect("resolveWsl launches a packaged backend from the WSL-local runtime cache", () => {    const observedArchives: Array<{      windowsArchivePath: string;      runtimeId: string;      sha256: string;    }> = [];    const observedNodePtyRoots: string[] = [];    let legacyCleanupCount = 0;    const linuxAppRoot = "/home/test/.t3/wsl-runtime/1.2.3-x64";     return withPackagedWslHarness(      {        archiveHash: "a".repeat(64),        forbidFallback: "A valid WSL archive must not extract the Windows fallback",        cleanupLegacy: Effect.sync(() => {          legacyCleanupCount += 1;        }),        wsl: () => ({          prepareRuntime: (_distro, archive) => {            observedArchives.push({              windowsArchivePath: archive.windowsPath,              runtimeId: archive.runtimeId,              sha256: archive.sha256,            });            return { ok: true, linuxAppRoot };          },          ensureNodePty: (_distro, root) => {            observedNodePtyRoots.push(root);            return { ok: true, nodePath: "/usr/bin/node", resolvedPath: "/usr/bin:/bin" };          },        }),      },      ({ archiveHash, archivePath, baseDir }) =>        Effect.gen(function* () {          const path = yield* Path.Path;          const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;          const config = yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });           assert.deepEqual(observedArchives, [            {              windowsArchivePath: archivePath,              runtimeId: `sha256-${archiveHash}`,              sha256: archiveHash,            },          ]);          assert.deepEqual(observedNodePtyRoots, [linuxAppRoot]);          assert.equal(            config.entryPath,            path.join(baseDir, "server.asar/apps/server/dist/bin.mjs"),          );          assert.include(config.args, `${linuxAppRoot}/apps/server/dist/bin.mjs`);          assert.equal(config.wslRuntimeId, `sha256-${archiveHash}`);          assert.equal(legacyCleanupCount, 1);          assert.isTrue(Option.isNone(config.preflightFailure));        }),    );  });   it.effect("resolveWsl changes the cache id when the packaged archive changes", () => {    const firstHash = "a".repeat(64);    const secondHash = "b".repeat(64);    const observedRuntimeIds: string[] = [];    return withPackagedWslHarness(      {        archiveHash: firstHash,        wsl: () => ({          prepareRuntime: (_distro, archive) => {            observedRuntimeIds.push(archive.runtimeId);            return { ok: true, linuxAppRoot: `/runtime/${archive.runtimeId}` };          },          ensureNodePty: () => ({            ok: true,            nodePath: "/usr/bin/node",            resolvedPath: "/usr/bin:/bin",          }),        }),      },      ({ hashPath, mountedAppRoot }) =>        Effect.gen(function* () {          const fileSystem = yield* FileSystem.FileSystem;          const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;          const first = yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });          yield* fileSystem.writeFileString(hashPath, secondHash);          const second = yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });          yield* fileSystem.writeFileString(hashPath, "not-a-sha256");          const invalidIdentity = yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });           assert.deepEqual(observedRuntimeIds, [`sha256-${firstHash}`, `sha256-${secondHash}`]);          assert.equal(first.wslRuntimeId, observedRuntimeIds[0]);          assert.equal(second.wslRuntimeId, observedRuntimeIds[1]);          assert.isUndefined(invalidIdentity.wslRuntimeId);          assert.include(invalidIdentity.args, `${mountedAppRoot}/apps/server/dist/bin.mjs`);        }),    );  });   it.effect("resolveWsl falls back to the mounted runtime when archive staging fails", () => {    const observedNodePtyRoots: string[] = [];    return withPackagedWslHarness(      {        archiveHash: "b".repeat(64),        wsl: () => ({          prepareRuntime: () => ({ ok: false, reason: "archive is corrupt" }),          ensureNodePty: (_distro, root) => {            observedNodePtyRoots.push(root);            return { ok: true, nodePath: "/usr/bin/node", resolvedPath: "/usr/bin:/bin" };          },        }),      },      ({ mountedAppRoot, mountedEntryPath }) =>        Effect.gen(function* () {          const configuration = yield* DesktopBackendConfiguration.DesktopBackendConfiguration;          const config = yield* configuration.resolveWsl({ port: 5000, distro: "Ubuntu" });           assert.deepEqual(observedNodePtyRoots, [mountedAppRoot]);          assert.equal(config.entryPath, mountedEntryPath);          assert.include(config.args, `${mountedAppRoot}/apps/server/dist/bin.mjs`);          assert.isUndefined(config.wslRuntimeId);          assert.isTrue(Option.isNone(config.preflightFailure));        }),    );  });

Lines 1–500 of 1371.