Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions frontend/preview/mock/mockwaveenv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,27 @@ describe("makeMockWaveEnv", () => {
const imageBytes = base64ToArray(readPackets[1].data64);
expect(Array.from(imageBytes.slice(0, 4))).toEqual([0x89, 0x50, 0x4e, 0x47]);
});

it("implements secrets commands with in-memory storage", async () => {
const { makeMockWaveEnv } = await import("./mockwaveenv");
const env = makeMockWaveEnv({ platform: "linux" });

await env.rpc.SetSecretsCommand(null as any, {
OPENAI_API_KEY: "sk-test",
ANTHROPIC_API_KEY: "anthropic-test",
} as any);

expect(await env.rpc.GetSecretsLinuxStorageBackendCommand(null as any)).toBe("libsecret");
expect(await env.rpc.GetSecretsNamesCommand(null as any)).toEqual(["ANTHROPIC_API_KEY", "OPENAI_API_KEY"]);
expect(await env.rpc.GetSecretsCommand(null as any, ["OPENAI_API_KEY", "MISSING_SECRET"])).toEqual({
OPENAI_API_KEY: "sk-test",
});

await env.rpc.SetSecretsCommand(null as any, { OPENAI_API_KEY: null } as any);

expect(await env.rpc.GetSecretsNamesCommand(null as any)).toEqual(["ANTHROPIC_API_KEY"]);
expect(await env.rpc.GetSecretsCommand(null as any, ["OPENAI_API_KEY", "ANTHROPIC_API_KEY"])).toEqual({
ANTHROPIC_API_KEY: "anthropic-test",
});
});
});
39 changes: 38 additions & 1 deletion frontend/preview/mock/mockwaveenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AllServiceTypes } from "@/app/store/services";
import { handleWaveEvent } from "@/app/store/wps";
import { RpcApiType } from "@/app/store/wshclientapi";
import { WaveEnv } from "@/app/waveenv/waveenv";
import { PlatformMacOS, PlatformWindows } from "@/util/platformutil";
import { PlatformLinux, PlatformMacOS, PlatformWindows } from "@/util/platformutil";
import { Atom, atom, PrimitiveAtom, useAtomValue } from "jotai";
import { DefaultFullConfig } from "./defaultconfig";
import { DefaultMockFilesystem } from "./mockfilesystem";
Expand All @@ -20,8 +20,13 @@ import { previewElectronApi } from "./preview-electron-api";
// - rpc.EventPublishCommand -- dispatches to handleWaveEvent(); works when the subscriber
// is purely FE-based (registered via WPS on the frontend)
// - rpc.GetMetaCommand -- reads .meta from the mock WOS atom for the given oref
// - rpc.GetSecretsCommand -- reads secrets from an in-memory mock secret store
// - rpc.GetSecretsLinuxStorageBackendCommand
// returns "libsecret" on Linux previews and "" elsewhere
// - rpc.GetSecretsNamesCommand -- lists secret names from the in-memory mock secret store
// - rpc.SetMetaCommand -- writes .meta to the mock WOS atom (null values delete keys)
// - rpc.SetConfigCommand -- merges settings into fullConfigAtom (null values delete keys)
// - rpc.SetSecretsCommand -- writes/deletes secrets in the in-memory mock secret store
// - rpc.UpdateTabNameCommand -- updates .name on the Tab WaveObj in the mock WOS
// - rpc.UpdateWorkspaceTabIdsCommand -- updates .tabids on the Workspace WaveObj in the mock WOS
//
Expand Down Expand Up @@ -172,11 +177,13 @@ type MockWosFns = {
getWaveObjectAtom: <T extends WaveObj>(oref: string) => PrimitiveAtom<T>;
mockSetWaveObj: <T extends WaveObj>(oref: string, obj: T) => void;
fullConfigAtom: PrimitiveAtom<FullConfigType>;
platform: NodeJS.Platform;
};

export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiType {
const callDispatchMap = new Map<string, (...args: any[]) => Promise<any>>();
const streamDispatchMap = new Map<string, (...args: any[]) => AsyncGenerator<any, void, boolean>>();
const secrets = new Map<string, string>();
const setCallHandler = (command: string, fn: (...args: any[]) => Promise<any>) => {
callDispatchMap.set(command, fn);
};
Expand Down Expand Up @@ -230,6 +237,35 @@ export function makeMockRpc(overrides: RpcOverrides, wos: MockWosFns): RpcApiTyp
globalStore.set(wos.fullConfigAtom, { ...current, settings: updatedSettings as SettingsType });
return null;
});
setCallHandler("getsecretslinuxstoragebackend", async () => {
if (wos.platform !== PlatformLinux) {
return "";
}
return "libsecret";
});
setCallHandler("getsecretsnames", async () => {
return Array.from(secrets.keys()).sort();
});
setCallHandler("getsecrets", async (_client, data: string[]) => {
const foundSecrets: Record<string, string> = {};
for (const name of data ?? []) {
const value = secrets.get(name);
if (value != null) {
foundSecrets[name] = value;
}
}
return foundSecrets;
});
setCallHandler("setsecrets", async (_client, data: Record<string, string>) => {
for (const [name, value] of Object.entries(data ?? {})) {
if (value == null) {
secrets.delete(name);
continue;
}
secrets.set(name, value);
}
return null;
});
setCallHandler("updateworkspacetabids", async (_client, data: { args: [string, string[]] }) => {
const [workspaceId, tabIds] = data.args;
const wsORef = "workspace:" + workspaceId;
Expand Down Expand Up @@ -319,6 +355,7 @@ export function makeMockWaveEnv(mockEnv?: MockEnv): MockWaveEnv {
const mockWosFns: MockWosFns = {
getWaveObjectAtom,
fullConfigAtom: atoms.fullConfigAtom,
platform,
mockSetWaveObj: <T extends WaveObj>(oref: string, obj: T) => {
if (!waveObjectValueAtomCache.has(oref)) {
waveObjectValueAtomCache.set(oref, atom(null as WaveObj));
Expand Down
Loading