Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
297 changes: 297 additions & 0 deletions apps/desktop/src/app/DesktopConnectionCatalogStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
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.ElectronSafeStorageShape);
}

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.DesktopConnectionCatalogStoreReadError,
);
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}/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.cause, permissionError);
}).pipe(Effect.provide(NodeServices.layer), Effect.scoped),
);

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, ElectronSafeStorage.ElectronSafeStorageDecryptError);
yield* Ref.set(failDecrypt, false);
assert.deepStrictEqual(yield* store.get, Option.some('{"schemaVersion":1,"targets":[]}'));
}).pipe(Effect.provide(NodeServices.layer), Effect.scoped),
);
});
Loading
Loading