135 lines · 5.3 KB · TypeScript
RawDownload
import { beforeEach, describe, expect, it } from "@effect/vitest";import { Effect } from "effect";// oxlint-disable-next-line executor/no-vitest-import -- boundary: vi.mock/vi.hoisted must come from vitest itself for mock hoisting to resolveimport { vi } from "vitest"; import { OnePasswordError } from "./errors";import { makeOnePasswordService } from "./service"; const opMocks = vi.hoisted(() => ({  setGlobalFlags: vi.fn(),  setServiceAccount: vi.fn(),  vaultList: vi.fn(),  itemList: vi.fn(),  readParse: vi.fn(),})); const sdkMocks = vi.hoisted(() => ({  createClient: vi.fn(),  DesktopAuth: vi.fn((accountName: string) => ({ accountName })),  exports: {} as {    createClient?: unknown;    DesktopAuth?: unknown;  },})); vi.mock("@1password/op-js", () => ({  setGlobalFlags: opMocks.setGlobalFlags,  setServiceAccount: opMocks.setServiceAccount,  vault: { list: opMocks.vaultList },  item: { list: opMocks.itemList },  read: { parse: opMocks.readParse },})); vi.mock("@1password/sdk", () => sdkMocks.exports); describe("makeOnePasswordService", () => {  beforeEach(() => {    vi.clearAllMocks();    opMocks.vaultList.mockReturnValue([]);    opMocks.itemList.mockReturnValue([]);    opMocks.readParse.mockReturnValue("secret");    sdkMocks.exports.createClient = sdkMocks.createClient;    sdkMocks.exports.DesktopAuth = sdkMocks.DesktopAuth;    sdkMocks.createClient.mockResolvedValue({      secrets: { resolve: vi.fn(async () => "secret") },      vaults: { list: vi.fn(async () => []) },      items: { list: vi.fn(async () => []) },    });  });   it.effect("falls back to the SDK when the CLI throws while listing vaults", () =>    Effect.gen(function* () {      const sdkVaultsList = vi.fn(async () => [{ id: "sdk-vault", title: "SDK Vault" }]);      opMocks.vaultList.mockImplementation(() => {        // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: simulates the untyped op-js CLI wrapper throwing        throw new Error("spawn op ENOENT");      });      sdkMocks.createClient.mockResolvedValue({        secrets: { resolve: vi.fn(async () => "secret") },        vaults: { list: sdkVaultsList },        items: { list: vi.fn(async () => []) },      });       const service = yield* makeOnePasswordService(        { kind: "service-account", token: "ops_test_token" },        { timeoutMs: 1_000 },      );      const vaults = yield* service.listVaults();       expect(vaults).toEqual([{ id: "sdk-vault", title: "SDK Vault" }]);      expect(sdkMocks.createClient).toHaveBeenCalledTimes(1);      expect(sdkVaultsList).toHaveBeenCalledTimes(1);    }),  );   it.effect("includes the backend cause when both vault listing backends fail", () =>    Effect.gen(function* () {      opMocks.vaultList.mockImplementation(() => {        // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: simulates the untyped op-js CLI wrapper throwing        throw new Error("spawn op ENOENT");      });      sdkMocks.createClient.mockResolvedValue({        secrets: { resolve: vi.fn(async () => "secret") },        vaults: {          list: vi.fn(async () => {            // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: simulates the untyped 1Password SDK rejecting            throw new Error("desktop approval refused for account");          }),        },        items: { list: vi.fn(async () => []) },      });       const error = yield* makeOnePasswordService(        { kind: "service-account", token: "ops_test_token" },        { timeoutMs: 1_000 },      ).pipe(        Effect.flatMap((service) => service.listVaults()),        Effect.flip,      );       expect(error).toBeInstanceOf(OnePasswordError);      // oxlint-disable executor/no-unknown-error-message -- boundary: OnePasswordError carries a typed message; asserting its contents      expect(error.message).toContain("1Password SDK vault listing failed:");      expect(error.message).toContain("desktop approval refused for account");      expect(error.message).not.toBe("1Password CLI vault listing failed");      // oxlint-enable executor/no-unknown-error-message    }),  );   it.effect("reports a clear SDK load error when the compiled namespace is empty", () =>    Effect.gen(function* () {      opMocks.vaultList.mockImplementation(() => {        // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: simulates the untyped op-js CLI wrapper throwing        throw new Error("spawn op ENOENT");      });      sdkMocks.exports.createClient = undefined;      sdkMocks.exports.DesktopAuth = undefined;       const error = yield* makeOnePasswordService(        { kind: "service-account", token: "ops_test_token" },        { timeoutMs: 1_000 },      ).pipe(        Effect.flatMap((service) => service.listVaults()),        Effect.flip,      );       expect(error).toBeInstanceOf(OnePasswordError);      expect(error.operation).toBe("sdk module load");      // oxlint-disable executor/no-unknown-error-message -- boundary: OnePasswordError carries a typed message; asserting its contents      expect(error.message).toContain("did not expose createClient and DesktopAuth");      expect(error.message).toContain("/opt/homebrew/bin");      // oxlint-enable executor/no-unknown-error-message    }),  );});