302 lines · 10.0 KB · TypeScript
RawDownload
import { assert, describe, it } from "@effect/vitest";import * as NodeServices from "@effect/platform-node/NodeServices";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option"; import * as ElectronApp from "../electron/ElectronApp.ts";import * as ElectronWindow from "../electron/ElectronWindow.ts";import * as IpcChannels from "../ipc/channels.ts";import * as DesktopCloudAuth from "./DesktopCloudAuth.ts";import * as DesktopEnvironment from "./DesktopEnvironment.ts"; interface CloudAuthHarness {  readonly app: ElectronApp.ElectronAppShape;  readonly window: ElectronWindow.ElectronWindowShape;  readonly listeners: Map<string, readonly ((...args: readonly unknown[]) => void)[]>;  readonly protocolRegistrations: {    readonly protocol: string;    readonly path?: string;    readonly args?: readonly string[];  }[];  readonly sends: { readonly channel: string; readonly args: readonly unknown[] }[];  readonly reveals: unknown[];  readonly layer: Layer.Layer<    | DesktopCloudAuth.DesktopCloudAuth    | DesktopEnvironment.DesktopEnvironment    | ElectronApp.ElectronApp    | ElectronWindow.ElectronWindow  >;} function makeHarness(input: { readonly isDevelopment: boolean }): CloudAuthHarness {  const listeners = new Map<string, ((...args: readonly unknown[]) => void)[]>();  const protocolRegistrations: CloudAuthHarness["protocolRegistrations"] = [];  const sends: CloudAuthHarness["sends"] = [];  const reveals: unknown[] = [];  const mainWindow = { id: "main-window" };   const app = ElectronApp.ElectronApp.of({    metadata: Effect.succeed({      appVersion: "0.0.0-test",      appPath: "/tmp/t3-code-test",      isPackaged: !input.isDevelopment,      resourcesPath: "/tmp/t3-code-test/resources",      runningUnderArm64Translation: false,    }),    name: Effect.succeed("T3 Code"),    whenReady: Effect.void,    quit: Effect.void,    exit: () => Effect.void,    relaunch: () => Effect.void,    setPath: () => Effect.void,    setName: () => Effect.void,    setAboutPanelOptions: () => Effect.void,    setAppUserModelId: () => Effect.void,    requestSingleInstanceLock: Effect.succeed(true),    isDefaultProtocolClient: () => Effect.succeed(false),    setAsDefaultProtocolClient: (protocol, path, args) =>      Effect.sync(() => {        protocolRegistrations.push({          protocol,          ...(path === undefined ? {} : { path }),          ...(args === undefined ? {} : { args }),        });        return true;      }),    setDesktopName: () => Effect.void,    setDockIcon: () => Effect.void,    appendCommandLineSwitch: () => Effect.void,    on: (eventName, listener) =>      Effect.sync(() => {        const erasedListener = listener as (...args: readonly unknown[]) => void;        listeners.set(eventName, [...(listeners.get(eventName) ?? []), erasedListener]);      }),  });   const window = ElectronWindow.ElectronWindow.of({    create: () => Effect.die("not used"),    main: Effect.succeed(Option.some(mainWindow as never)),    currentMainOrFirst: Effect.succeed(Option.some(mainWindow as never)),    focusedMainOrFirst: Effect.succeed(Option.some(mainWindow as never)),    setMain: () => Effect.void,    clearMain: () => Effect.void,    reveal: (target) =>      Effect.sync(() => {        reveals.push(target);      }),    sendAll: (channel, ...args) =>      Effect.sync(() => {        sends.push({ channel, args });      }),    destroyAll: Effect.void,    syncAllAppearance: () => Effect.void,  });   const environment = DesktopEnvironment.DesktopEnvironment.of({    isDevelopment: input.isDevelopment,  } as DesktopEnvironment.DesktopEnvironmentShape);  const environmentLayer = Layer.succeed(DesktopEnvironment.DesktopEnvironment, environment);   return {    app,    window,    listeners,    protocolRegistrations,    sends,    reveals,    layer: Layer.mergeAll(      DesktopCloudAuth.layer.pipe(        Layer.provideMerge(environmentLayer),        Layer.provide(NodeServices.layer),      ),      Layer.succeed(ElectronApp.ElectronApp, app),      Layer.succeed(ElectronWindow.ElectronWindow, window),    ),  };} function emitAppEvent(  harness: CloudAuthHarness,  eventName: string,  ...args: readonly unknown[]): void {  for (const listener of harness.listeners.get(eventName) ?? []) {    listener(...args);  }} const flushCloudAuthDispatch = Effect.promise(() => Promise.resolve()); describe("DesktopCloudAuth", () => {  it("uses separate callback schemes for packaged and development builds", () => {    assert.equal(      DesktopCloudAuth.resolveCloudAuthCallbackScheme({ isDevelopment: false }),      "t3code",    );    assert.equal(      DesktopCloudAuth.resolveCloudAuthCallbackScheme({ isDevelopment: true }),      "t3code-dev",    );  });   it("builds a native callback URL with request state", () => {    assert.equal(      DesktopCloudAuth.buildCloudAuthCallbackUrl({        scheme: "t3code",        state: "state-1",      }),      "t3code://auth/callback?t3_state=state-1",    );  });   it("accepts only the expected scheme, host, path, and state", () => {    assert.isNotNull(      DesktopCloudAuth.parseCloudAuthCallbackUrl({        rawUrl: "t3code://auth/callback?rotating_token_nonce=nonce&t3_state=state-1",        scheme: "t3code",        state: "state-1",      }),    );    assert.isNull(      DesktopCloudAuth.parseCloudAuthCallbackUrl({        rawUrl: "t3code://auth/callback?rotating_token_nonce=nonce&t3_state=wrong",        scheme: "t3code",        state: "state-1",      }),    );    assert.isNull(      DesktopCloudAuth.parseCloudAuthCallbackUrl({        rawUrl: "https://example.com/callback?rotating_token_nonce=nonce&t3_state=state-1",        scheme: "t3code",        state: "state-1",      }),    );  });   it("builds a native development callback URL with request state", () => {    assert.equal(      DesktopCloudAuth.buildCloudAuthCallbackUrl({        scheme: "t3code-dev",        state: "state-1",      }),      "t3code-dev://auth/callback?t3_state=state-1",    );  });   it.effect("registers the development protocol client and dispatches matching callbacks", () => {    const harness = makeHarness({ isDevelopment: true });     return Effect.gen(function* () {      const cloudAuth = yield* DesktopCloudAuth.DesktopCloudAuth;      yield* cloudAuth.configure;      const redirectUrl = yield* cloudAuth.createRequest;      const callbackUrl = new URL(redirectUrl);      callbackUrl.searchParams.set("rotating_token_nonce", "nonce-1");       let prevented = false;      emitAppEvent(        harness,        "open-url",        { preventDefault: () => (prevented = true) },        callbackUrl.toString(),      );      yield* flushCloudAuthDispatch;       assert.isTrue(prevented);      assert.deepEqual(        harness.protocolRegistrations.map((registration) => registration.protocol),        ["t3code-dev"],      );      assert.isString(harness.protocolRegistrations[0]?.path);      assert.isArray(harness.protocolRegistrations[0]?.args);      assert.deepEqual(harness.sends, [        {          channel: IpcChannels.CLOUD_AUTH_CALLBACK_CHANNEL,          args: [callbackUrl.toString()],        },      ]);      assert.lengthOf(harness.reveals, 1);    }).pipe(Effect.provide(harness.layer), Effect.scoped);  });   it.effect("rejects mismatched callback state and only consumes the pending request once", () => {    const harness = makeHarness({ isDevelopment: false });     return Effect.gen(function* () {      const cloudAuth = yield* DesktopCloudAuth.DesktopCloudAuth;      yield* cloudAuth.configure;      const redirectUrl = yield* cloudAuth.createRequest;      const validCallback = new URL(redirectUrl);      validCallback.searchParams.set("rotating_token_nonce", "nonce-1");      const invalidCallback = new URL(validCallback);      invalidCallback.searchParams.set(DesktopCloudAuth.CLOUD_AUTH_CALLBACK_STATE_PARAM, "wrong");       emitAppEvent(        harness,        "open-url",        { preventDefault: () => undefined },        invalidCallback.toString(),      );      yield* flushCloudAuthDispatch;      assert.deepEqual(harness.sends, []);       emitAppEvent(        harness,        "open-url",        { preventDefault: () => undefined },        validCallback.toString(),      );      yield* flushCloudAuthDispatch;      emitAppEvent(        harness,        "open-url",        { preventDefault: () => undefined },        validCallback.toString(),      );      yield* flushCloudAuthDispatch;       assert.deepEqual(        harness.protocolRegistrations.map((registration) => registration.protocol),        ["t3code"],      );      assert.deepEqual(harness.sends, [        {          channel: IpcChannels.CLOUD_AUTH_CALLBACK_CHANNEL,          args: [validCallback.toString()],        },      ]);    }).pipe(Effect.provide(harness.layer), Effect.scoped);  });   it.effect(    "routes second-instance callbacks and reveals the window for non-callback launches",    () => {      const harness = makeHarness({ isDevelopment: true });       return Effect.gen(function* () {        const cloudAuth = yield* DesktopCloudAuth.DesktopCloudAuth;        yield* cloudAuth.configure;        const redirectUrl = yield* cloudAuth.createRequest;        const callbackUrl = new URL(redirectUrl);        callbackUrl.searchParams.set("rotating_token_nonce", "nonce-1");         emitAppEvent(harness, "second-instance", {}, ["electron", callbackUrl.toString()]);        yield* flushCloudAuthDispatch;         const revealCountAfterCallback = harness.reveals.length;        emitAppEvent(harness, "second-instance", {}, ["electron", "--opened-from-dock"]);        yield* flushCloudAuthDispatch;         assert.deepEqual(harness.sends, [          {            channel: IpcChannels.CLOUD_AUTH_CALLBACK_CHANNEL,            args: [callbackUrl.toString()],          },        ]);        assert.equal(revealCountAfterCallback, 1);        assert.equal(harness.reveals.length, 2);      }).pipe(Effect.provide(harness.layer), Effect.scoped);    },  );});