415 lines · 16.7 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import { assert, describe, it } from "@effect/vitest";import { ConnectionCatalogDocument } from "@t3tools/client-runtime/platform";import { EnvironmentId, type PersistedSavedEnvironmentRecord } from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as PlatformError from "effect/PlatformError";import * as Ref from "effect/Ref";import * as Schema from "effect/Schema"; import * as ElectronSafeStorage from "../electron/ElectronSafeStorage.ts";import * as DesktopSavedEnvironments from "../settings/DesktopSavedEnvironments.ts";import * as DesktopConfig from "./DesktopConfig.ts";import * as DesktopConnectionCatalogStore from "./DesktopConnectionCatalogStore.ts";import * as DesktopEnvironment from "./DesktopEnvironment.ts"; const textDecoder = new TextDecoder();const textEncoder = new TextEncoder();const decodeConnectionCatalog = Schema.decodeEffect(  Schema.fromJsonString(ConnectionCatalogDocument),);function makeSafeStorageLayer(available: boolean, failDecrypt: Ref.Ref<boolean> | null = null) {  return Layer.succeed(ElectronSafeStorage.ElectronSafeStorage, {    isEncryptionAvailable: Effect.succeed(available),    encryptString: (value) => Effect.succeed(textEncoder.encode(`encrypted:${value}`)),    decryptString: (value) => {      return Effect.gen(function* () {        const decoded = textDecoder.decode(value);        if (          !decoded.startsWith("encrypted:") ||          (failDecrypt !== null && (yield* Ref.get(failDecrypt)))        ) {          return yield* new ElectronSafeStorage.ElectronSafeStorageDecryptError({            cause: new Error("invalid encrypted catalog"),          });        }        return decoded.slice("encrypted:".length);      });    },  } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]);} function makeLayer(  baseDir: string,  encryptionAvailable = true,  failDecrypt: Ref.Ref<boolean> | null = null,  fileSystemLayer: Layer.Layer<FileSystem.FileSystem> = NodeServices.layer,) {  const environmentLayer = DesktopEnvironment.layer({    dirname: "/repo/apps/desktop/src",    homeDirectory: baseDir,    platform: "darwin",    processArch: "arm64",    appVersion: "1.2.3",    appPath: "/repo",    isPackaged: true,    resourcesPath: "/missing/resources",    runningUnderArm64Translation: false,  }).pipe(    Layer.provide(      Layer.mergeAll(NodeServices.layer, DesktopConfig.layerTest({ T3CODE_HOME: baseDir })),    ),  );  const safeStorageLayer = makeSafeStorageLayer(encryptionAvailable, failDecrypt);  const dependencies = Layer.mergeAll(    environmentLayer,    safeStorageLayer,    NodeServices.layer,    fileSystemLayer,  );  const savedEnvironmentsLayer = DesktopSavedEnvironments.layer.pipe(    Layer.provideMerge(dependencies),  );   return DesktopConnectionCatalogStore.layer.pipe(    Layer.provideMerge(savedEnvironmentsLayer),    Layer.provideMerge(dependencies),  );} const withStore = <A, E, R>(  effect: Effect.Effect<A, E, R | DesktopConnectionCatalogStore.DesktopConnectionCatalogStore>,  encryptionAvailable = true,) =>  Effect.gen(function* () {    const fileSystem = yield* FileSystem.FileSystem;    const baseDir = yield* fileSystem.makeTempDirectoryScoped({      prefix: "t3-desktop-connection-catalog-test-",    });    return yield* effect.pipe(Effect.provide(makeLayer(baseDir, encryptionAvailable)));  }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); describe("DesktopConnectionCatalogStore", () => {  it.effect("persists, reads, and clears an encrypted connection catalog", () =>    withStore(      Effect.gen(function* () {        const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;        const catalog = '{"schemaVersion":1,"targets":[]}';         assert.isTrue(yield* store.set(catalog));        assert.deepStrictEqual(yield* store.get, Option.some(catalog));         yield* store.clear;        assert.deepStrictEqual(yield* store.get, Option.none());      }),    ),  );   it.effect("does not persist when secure storage is unavailable", () =>    withStore(      Effect.gen(function* () {        const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;        assert.isFalse(yield* store.set("{}"));        assert.deepStrictEqual(yield* store.get, Option.none());      }),      false,    ),  );   it.effect("migrates legacy relay, SSH, bearer profile, and credential data", () =>    withStore(      Effect.gen(function* () {        const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;        const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments;        const records: readonly PersistedSavedEnvironmentRecord[] = [          {            environmentId: EnvironmentId.make("relay-environment"),            label: "Relay",            httpBaseUrl: "https://relay.example.com/",            wsBaseUrl: "wss://relay.example.com/",            createdAt: "2026-06-01T00:00:00.000Z",            lastConnectedAt: null,            relayManaged: { relayUrl: "https://relay-control.example.com/" },          },          {            environmentId: EnvironmentId.make("ssh-environment"),            label: "SSH",            httpBaseUrl: "http://127.0.0.1:41773/",            wsBaseUrl: "ws://127.0.0.1:41773/",            createdAt: "2026-06-02T00:00:00.000Z",            lastConnectedAt: null,            desktopSsh: {              alias: "devbox",              hostname: "devbox.example.com",              username: "julius",              port: 22,            },          },          {            environmentId: EnvironmentId.make("bearer-environment"),            label: "Bearer",            httpBaseUrl: "https://bearer.example.com/",            wsBaseUrl: "wss://bearer.example.com/",            createdAt: "2026-06-03T00:00:00.000Z",            lastConnectedAt: null,          },        ];        yield* savedEnvironments.setRegistry(records);        assert.isTrue(          yield* savedEnvironments.setSecret({            environmentId: EnvironmentId.make("bearer-environment"),            secret: "legacy-token",          }),        );         const migrated = yield* store.get;        assert.isTrue(Option.isSome(migrated));        if (Option.isNone(migrated)) {          return;        }        const catalog = yield* decodeConnectionCatalog(migrated.value);         assert.deepInclude(catalog.targets[0], {          _tag: "RelayConnectionTarget",          environmentId: EnvironmentId.make("relay-environment"),          label: "Relay",        });        assert.deepInclude(catalog.targets[1], {          _tag: "SshConnectionTarget",          environmentId: EnvironmentId.make("ssh-environment"),          label: "SSH",          connectionId: "ssh:ssh-environment",        });        assert.deepInclude(catalog.targets[2], {          _tag: "BearerConnectionTarget",          environmentId: EnvironmentId.make("bearer-environment"),          label: "Bearer",          connectionId: "bearer:bearer-environment",        });        assert.deepInclude(catalog.profiles[0], {          _tag: "SshConnectionProfile",          connectionId: "ssh:ssh-environment",          environmentId: EnvironmentId.make("ssh-environment"),          label: "SSH",          target: {            alias: "devbox",            hostname: "devbox.example.com",            username: "julius",            port: 22,          },        });        assert.deepInclude(catalog.profiles[1], {          _tag: "BearerConnectionProfile",          connectionId: "bearer:bearer-environment",          environmentId: EnvironmentId.make("bearer-environment"),          label: "Bearer",          httpBaseUrl: "https://bearer.example.com/",          wsBaseUrl: "wss://bearer.example.com/",        });        assert.equal(catalog.credentials.length, 1);        assert.equal(catalog.credentials[0]?.connectionId, "bearer:bearer-environment");        assert.equal(catalog.credentials[0]?.credential._tag, "BearerConnectionCredential");        if (catalog.credentials[0]?.credential._tag === "BearerConnectionCredential") {          assert.equal(catalog.credentials[0].credential.token, "legacy-token");        }         yield* savedEnvironments.setRegistry([]);        assert.deepEqual(yield* store.get, migrated);      }),    ),  );   it.effect("surfaces malformed catalog documents without deleting them", () =>    withStore(      Effect.gen(function* () {        const environment = yield* DesktopEnvironment.DesktopEnvironment;        const fileSystem = yield* FileSystem.FileSystem;        const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;        const catalogPath = `${environment.stateDir}/connection-catalog.json`;        yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true });        yield* fileSystem.writeFileString(catalogPath, "{not-json");         const error = yield* store.get.pipe(Effect.flip);        assert.instanceOf(          error,          DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreDocumentDecodeError,        );        assert.equal(error.catalogPath, catalogPath);        assert.exists(error.cause);        assert.equal(yield* fileSystem.readFileString(catalogPath), "{not-json");      }),    ),  );   it.effect("surfaces catalog filesystem failures instead of treating them as missing", () =>    Effect.gen(function* () {      const baseFileSystem = yield* FileSystem.FileSystem;      const baseDir = yield* baseFileSystem.makeTempDirectoryScoped({        prefix: "t3-desktop-connection-catalog-test-",      });      const permissionError = PlatformError.systemError({        _tag: "PermissionDenied",        module: "FileSystem",        method: "readFileString",        pathOrDescriptor: `${baseDir}/userdata/connection-catalog.json`,      });      const fileSystemLayer = Layer.succeed(        FileSystem.FileSystem,        FileSystem.makeNoop({          readFileString: () => Effect.fail(permissionError),        }),      );      const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe(        Effect.provide(makeLayer(baseDir, true, null, fileSystemLayer)),      );       const error = yield* store.get.pipe(Effect.flip);      assert.instanceOf(        error,        DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreReadError,      );      assert.equal(error.catalogPath, `${baseDir}/userdata/connection-catalog.json`);      assert.strictEqual(error.cause, permissionError);      assert.equal(        error.message,        `Failed to read the desktop connection catalog at ${baseDir}/userdata/connection-catalog.json.`,      );      assert.notEqual(error.message, permissionError.message);    }).pipe(Effect.provide(NodeServices.layer), Effect.scoped),  );   it.effect("reports the failed catalog write operation and path", () =>    Effect.gen(function* () {      const baseFileSystem = yield* FileSystem.FileSystem;      const baseDir = yield* baseFileSystem.makeTempDirectoryScoped({        prefix: "t3-desktop-connection-catalog-test-",      });      const permissionError = PlatformError.systemError({        _tag: "PermissionDenied",        module: "FileSystem",        method: "makeDirectory",        pathOrDescriptor: `${baseDir}/userdata`,      });      const fileSystemLayer = Layer.succeed(        FileSystem.FileSystem,        FileSystem.makeNoop({          makeDirectory: () => Effect.fail(permissionError),        }),      );      const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe(        Effect.provide(makeLayer(baseDir, true, null, fileSystemLayer)),      );       const error = yield* store.set("{}").pipe(Effect.flip);      assert.instanceOf(        error,        DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreWriteError,      );      assert.equal(error.operation, "create-directory");      assert.equal(error.path, `${baseDir}/userdata`);      assert.strictEqual(error.cause, permissionError);      assert.equal(        error.message,        `Desktop connection catalog write failed during create-directory at ${baseDir}/userdata.`,      );      assert.notEqual(error.message, permissionError.message);    }).pipe(Effect.provide(NodeServices.layer), Effect.scoped),  );   it.effect("reports the legacy migration stage", () =>    withStore(      Effect.gen(function* () {        const environment = yield* DesktopEnvironment.DesktopEnvironment;        const fileSystem = yield* FileSystem.FileSystem;        const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;        yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true });        yield* fileSystem.writeFileString(environment.savedEnvironmentRegistryPath, "{not-json");         const error = yield* store.get.pipe(Effect.flip);        assert.instanceOf(          error,          DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreMigrationError,        );        assert.equal(error.operation, "read-legacy-registry");        assert.equal(error.catalogPath, `${environment.stateDir}/connection-catalog.json`);        assert.instanceOf(          error.cause,          DesktopSavedEnvironments.DesktopSavedEnvironmentsDocumentDecodeError,        );        const registryError =          error.cause as DesktopSavedEnvironments.DesktopSavedEnvironmentsDocumentDecodeError;        assert.exists(registryError.cause);        assert.equal(          error.message,          `Legacy desktop saved-environment migration failed during read-legacy-registry into ${environment.stateDir}/connection-catalog.json.`,        );        assert.notEqual(error.message, registryError.message);      }),    ),  );   it.effect("reports invalid encrypted catalog data without exposing it", () =>    withStore(      Effect.gen(function* () {        const environment = yield* DesktopEnvironment.DesktopEnvironment;        const fileSystem = yield* FileSystem.FileSystem;        const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore;        const catalogPath = `${environment.stateDir}/connection-catalog.json`;        yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true });        yield* fileSystem.writeFileString(catalogPath, '{"version":1,"encryptedCatalog":"%%%"}\n');         const error = yield* store.get.pipe(Effect.flip);        assert.instanceOf(          error,          DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreDecodeError,        );        assert.equal(error.resource, "encryptedCatalog");        assert.equal(error.catalogPath, catalogPath);        assert.exists(error.cause);        assert.equal(          error.message,          `Failed to decode encryptedCatalog for the desktop connection catalog at ${catalogPath}.`,        );        assert.notInclude(error.message, "%%%");      }),    ),  );   it.effect("surfaces a catalog that can no longer be decrypted without deleting it", () =>    Effect.gen(function* () {      const fileSystem = yield* FileSystem.FileSystem;      const baseDir = yield* fileSystem.makeTempDirectoryScoped({        prefix: "t3-desktop-connection-catalog-test-",      });      const failDecrypt = yield* Ref.make(false);      const layer = makeLayer(baseDir, true, failDecrypt);      const store = yield* DesktopConnectionCatalogStore.DesktopConnectionCatalogStore.pipe(        Effect.provide(layer),      );       assert.isTrue(yield* store.set('{"schemaVersion":1,"targets":[]}'));      yield* Ref.set(failDecrypt, true);      const error = yield* store.get.pipe(Effect.flip);      assert.instanceOf(        error,        DesktopConnectionCatalogStore.DesktopConnectionCatalogStoreProtectionError,      );      assert.equal(error.operation, "decrypt-catalog");      assert.equal(error.catalogPath, `${baseDir}/userdata/connection-catalog.json`);      assert.instanceOf(error.cause, ElectronSafeStorage.ElectronSafeStorageDecryptError);      const decryptError = error.cause as ElectronSafeStorage.ElectronSafeStorageDecryptError;      assert.instanceOf(decryptError.cause, Error);      assert.equal(decryptError.cause.message, "invalid encrypted catalog");      assert.equal(        error.message,        `Desktop connection catalog protection failed during decrypt-catalog at ${baseDir}/userdata/connection-catalog.json.`,      );      assert.notEqual(error.message, decryptError.message);      yield* Ref.set(failDecrypt, false);      assert.deepStrictEqual(yield* store.get, Option.some('{"schemaVersion":1,"targets":[]}'));    }).pipe(Effect.provide(NodeServices.layer), Effect.scoped),  );});