import * as NodeServices from "@effect/platform-node/NodeServices"; import { assert, describe, it } from "@effect/vitest"; 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 Schema from "effect/Schema"; import * as DesktopConfig from "../app/DesktopConfig.ts"; import * as DesktopEnvironment from "../app/DesktopEnvironment.ts"; import * as ElectronSafeStorage from "../electron/ElectronSafeStorage.ts"; import * as DesktopSavedEnvironments from "./DesktopSavedEnvironments.ts"; const textDecoder = new TextDecoder(); const textEncoder = new TextEncoder(); const savedRegistryRecord: PersistedSavedEnvironmentRecord = { environmentId: EnvironmentId.make("environment-1"), label: "Remote environment", httpBaseUrl: "https://remote.example.com/", wsBaseUrl: "wss://remote.example.com/", createdAt: "2026-04-09T00:00:00.000Z", lastConnectedAt: "2026-04-09T01:00:00.000Z", desktopSsh: { alias: "devbox", hostname: "devbox.example.com", username: "julius", port: 22, }, }; const SavedEnvironmentRegistryDocumentProbe = Schema.Struct({ version: Schema.Number, records: Schema.Array(Schema.Unknown), }); const SavedEnvironmentRegistryDocumentProbeJson = Schema.fromJsonString( SavedEnvironmentRegistryDocumentProbe, ); const decodeSavedEnvironmentRegistryDocumentProbe = Schema.decodeEffect( SavedEnvironmentRegistryDocumentProbeJson, ); const encodeSavedEnvironmentRegistryDocumentProbe = Schema.encodeEffect( SavedEnvironmentRegistryDocumentProbeJson, ); function makeSafeStorageLayer(input: { readonly available: boolean; readonly availabilityError?: unknown; readonly encryptError?: unknown; readonly decryptError?: unknown; }) { return Layer.succeed(ElectronSafeStorage.ElectronSafeStorage, { isEncryptionAvailable: input.availabilityError === undefined ? Effect.succeed(input.available) : Effect.fail( new ElectronSafeStorage.ElectronSafeStorageAvailabilityError({ cause: input.availabilityError, }), ), encryptString: (value) => input.encryptError === undefined ? Effect.succeed(textEncoder.encode(`enc:${value}`)) : Effect.fail( new ElectronSafeStorage.ElectronSafeStorageEncryptError({ cause: input.encryptError, }), ), decryptString: (value) => { if (input.decryptError !== undefined) { return Effect.fail( new ElectronSafeStorage.ElectronSafeStorageDecryptError({ cause: input.decryptError, }), ); } const decoded = textDecoder.decode(value); if (!decoded.startsWith("enc:")) { return Effect.fail( new ElectronSafeStorage.ElectronSafeStorageDecryptError({ cause: new Error("invalid secret"), }), ); } return Effect.succeed(decoded.slice("enc:".length)); }, } satisfies ElectronSafeStorage.ElectronSafeStorage["Service"]); } function makeLayer( baseDir: string, options?: { readonly availableSecretStorage?: boolean; readonly availabilityError?: unknown; readonly encryptError?: unknown; readonly decryptError?: unknown; }, fileSystemLayer: Layer.Layer = NodeServices.layer, ) { const environmentLayer = DesktopEnvironment.layer({ dirname: "/repo/apps/desktop/src", homeDirectory: baseDir, platform: "darwin", processArch: "x64", 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({ available: options?.availableSecretStorage ?? true, availabilityError: options?.availabilityError, encryptError: options?.encryptError, decryptError: options?.decryptError, }); const dependencies = Layer.mergeAll( environmentLayer, safeStorageLayer, NodeServices.layer, fileSystemLayer, ); return DesktopSavedEnvironments.layer.pipe(Layer.provideMerge(dependencies)); } const withSavedEnvironments = ( effect: Effect.Effect, options?: { readonly availableSecretStorage?: boolean; readonly availabilityError?: unknown; readonly encryptError?: unknown; readonly decryptError?: unknown; }, ) => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; const baseDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-desktop-saved-environments-test-", }); return yield* effect.pipe(Effect.provide(makeLayer(baseDir, options))); }).pipe(Effect.provide(NodeServices.layer), Effect.scoped); describe("DesktopSavedEnvironments", () => { it.effect("persists and reloads saved environment metadata", () => withSavedEnvironments( Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; const fileSystem = yield* FileSystem.FileSystem; const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); assert.deepEqual(yield* savedEnvironments.getRegistry, [savedRegistryRecord]); const persisted = yield* decodeSavedEnvironmentRegistryDocumentProbe( yield* fileSystem.readFileString(environment.savedEnvironmentRegistryPath), ); assert.equal(persisted.version, 1); assert.lengthOf(persisted.records, 1); }), ), ); it.effect("loads lenient saved environment registry documents", () => withSavedEnvironments( Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; const fileSystem = yield* FileSystem.FileSystem; const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true }); yield* fileSystem.writeFileString( environment.savedEnvironmentRegistryPath, `{ // Same optional envelope shape as browser saved environments. "version": 1, "records": [ { "environmentId": "${savedRegistryRecord.environmentId}", "label": "Remote environment", "httpBaseUrl": "https://remote.example.com/", "wsBaseUrl": "wss://remote.example.com/", "createdAt": "2026-04-09T00:00:00.000Z", "lastConnectedAt": "2026-04-09T01:00:00.000Z", "desktopSsh": { "alias": "devbox", "hostname": "devbox.example.com", "username": "julius", "port": 22, }, }, ], }\n`, ); assert.deepEqual(yield* savedEnvironments.getRegistry, [savedRegistryRecord]); }), ), ); it.effect("persists encrypted saved environment secrets when encryption is available", () => withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); assert.isTrue( yield* savedEnvironments.setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "bearer-token", }), ); assert.deepEqual( yield* savedEnvironments.getSecret(savedRegistryRecord.environmentId), Option.some("bearer-token"), ); }), ), ); it.effect("reports invalid saved secret encoding without exposing the secret", () => withSavedEnvironments( Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; const fileSystem = yield* FileSystem.FileSystem; const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true }); const encoded = yield* encodeSavedEnvironmentRegistryDocumentProbe({ version: 1, records: [{ ...savedRegistryRecord, encryptedBearerToken: "%%%" }], }); yield* fileSystem.writeFileString(environment.savedEnvironmentRegistryPath, `${encoded}\n`); const error = yield* savedEnvironments .getSecret(savedRegistryRecord.environmentId) .pipe(Effect.flip); assert.instanceOf(error, DesktopSavedEnvironments.DesktopSavedEnvironmentSecretDecodeError); assert.equal(error.environmentId, savedRegistryRecord.environmentId); assert.equal(error.registryPath, environment.savedEnvironmentRegistryPath); assert.equal(error.field, "encryptedBearerToken"); assert.exists(error.cause); assert.equal( error.message, `Failed to decode encryptedBearerToken for environment ${savedRegistryRecord.environmentId} at ${environment.savedEnvironmentRegistryPath}.`, ); assert.notInclude(error.message, "%%%"); }), ), ); it.effect("returns false when writing secrets while encryption is unavailable", () => withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); assert.isFalse( yield* savedEnvironments.setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "next-token", }), ); }), { availableSecretStorage: false }, ), ); it.effect("surfaces typed safe storage availability failures", () => { const cause = new Error("safe storage unavailable"); return withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); const error = yield* savedEnvironments .setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "next-token", }) .pipe(Effect.flip); assert.instanceOf(error, ElectronSafeStorage.ElectronSafeStorageAvailabilityError); assert.equal(error.cause, cause); }), { availabilityError: cause }, ); }); it.effect("removes saved environment secrets", () => withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); yield* savedEnvironments.setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "bearer-token", }); yield* savedEnvironments.removeSecret(savedRegistryRecord.environmentId); assert.isTrue( Option.isNone(yield* savedEnvironments.getSecret(savedRegistryRecord.environmentId)), ); }), ), ); it.effect("removes saved environment metadata and its embedded secret atomically", () => withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); yield* savedEnvironments.setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "bearer-token", }); yield* savedEnvironments.removeEnvironment(savedRegistryRecord.environmentId); assert.deepEqual(yield* savedEnvironments.getRegistry, []); assert.isTrue( Option.isNone(yield* savedEnvironments.getSecret(savedRegistryRecord.environmentId)), ); }), ), ); it.effect("treats empty saved environment documents as empty", () => withSavedEnvironments( Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; const fileSystem = yield* FileSystem.FileSystem; const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true }); yield* fileSystem.writeFileString(environment.savedEnvironmentRegistryPath, "{}\n"); assert.deepEqual(yield* savedEnvironments.getRegistry, []); assert.isTrue( Option.isNone(yield* savedEnvironments.getSecret(savedRegistryRecord.environmentId)), ); }), ), ); it.effect("surfaces malformed saved environment documents", () => withSavedEnvironments( Effect.gen(function* () { const environment = yield* DesktopEnvironment.DesktopEnvironment; const fileSystem = yield* FileSystem.FileSystem; const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* fileSystem.makeDirectory(environment.stateDir, { recursive: true }); yield* fileSystem.writeFileString(environment.savedEnvironmentRegistryPath, "{not-json"); const registryError = yield* savedEnvironments.getRegistry.pipe(Effect.flip); assert.instanceOf( registryError, DesktopSavedEnvironments.DesktopSavedEnvironmentsDocumentDecodeError, ); assert.equal(registryError.registryPath, environment.savedEnvironmentRegistryPath); assert.exists(registryError.cause); const secretError = yield* savedEnvironments .getSecret(savedRegistryRecord.environmentId) .pipe(Effect.flip); assert.instanceOf( secretError, DesktopSavedEnvironments.DesktopSavedEnvironmentsDocumentDecodeError, ); const mutationError = yield* savedEnvironments .setRegistry([savedRegistryRecord]) .pipe(Effect.flip); assert.instanceOf( mutationError, DesktopSavedEnvironments.DesktopSavedEnvironmentsDocumentDecodeError, ); }), ), ); it.effect("reports saved environment filesystem reads separately from document decoding", () => Effect.gen(function* () { const baseFileSystem = yield* FileSystem.FileSystem; const baseDir = yield* baseFileSystem.makeTempDirectoryScoped({ prefix: "t3-desktop-saved-environments-test-", }); const registryPath = `${baseDir}/userdata/saved-environments.json`; const permissionError = PlatformError.systemError({ _tag: "PermissionDenied", module: "FileSystem", method: "readFileString", pathOrDescriptor: registryPath, }); const fileSystemLayer = Layer.succeed( FileSystem.FileSystem, FileSystem.makeNoop({ readFileString: () => Effect.fail(permissionError), }), ); const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments.pipe( Effect.provide(makeLayer(baseDir, undefined, fileSystemLayer)), ); const error = yield* savedEnvironments.getRegistry.pipe(Effect.flip); assert.instanceOf(error, DesktopSavedEnvironments.DesktopSavedEnvironmentsReadError); assert.equal(error.registryPath, registryPath); assert.strictEqual(error.cause, permissionError); assert.equal(error.message, `Failed to read desktop saved environments at ${registryPath}.`); assert.notEqual(error.message, permissionError.message); }).pipe(Effect.provide(NodeServices.layer), Effect.scoped), ); it.effect("reports the failed saved environment write operation and path", () => Effect.gen(function* () { const baseFileSystem = yield* FileSystem.FileSystem; const baseDir = yield* baseFileSystem.makeTempDirectoryScoped({ prefix: "t3-desktop-saved-environments-test-", }); const permissionError = PlatformError.systemError({ _tag: "PermissionDenied", module: "FileSystem", method: "makeDirectory", pathOrDescriptor: `${baseDir}/userdata`, }); const fileSystemLayer = Layer.succeed( FileSystem.FileSystem, FileSystem.makeNoop({ readFileString: baseFileSystem.readFileString, makeDirectory: () => Effect.fail(permissionError), }), ); const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments.pipe( Effect.provide(makeLayer(baseDir, undefined, fileSystemLayer)), ); const error = yield* savedEnvironments.setRegistry([savedRegistryRecord]).pipe(Effect.flip); assert.instanceOf(error, DesktopSavedEnvironments.DesktopSavedEnvironmentsWriteError); assert.equal(error.operation, "create-directory"); assert.equal(error.path, `${baseDir}/userdata`); assert.strictEqual(error.cause, permissionError); assert.equal( error.message, `Desktop saved-environment write failed during create-directory at ${baseDir}/userdata.`, ); assert.notEqual(error.message, permissionError.message); }).pipe(Effect.provide(NodeServices.layer), Effect.scoped), ); it.effect("returns false when writing a secret without metadata", () => withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; assert.isFalse( yield* savedEnvironments.setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "bearer-token", }), ); }), ), ); it.effect("preserves encrypted secrets when metadata is rewritten", () => withSavedEnvironments( Effect.gen(function* () { const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments; yield* savedEnvironments.setRegistry([savedRegistryRecord]); yield* savedEnvironments.setSecret({ environmentId: savedRegistryRecord.environmentId, secret: "bearer-token", }); yield* savedEnvironments.setRegistry([savedRegistryRecord]); assert.deepEqual(yield* savedEnvironments.getRegistry, [savedRegistryRecord]); assert.deepEqual( yield* savedEnvironments.getSecret(savedRegistryRecord.environmentId), Option.some("bearer-token"), ); }), ), ); });