2021 lines · 78.7 KB · TypeScript
RawDownload
// ---------------------------------------------------------------------------// The membership mirror's FEEDERS, end to end through the code that runs in// production, against the real PGlite Postgres every cloud unit test runs on// (scripts/test-globalsetup.ts). WorkOS is a fake `WorkOSClient` (the// emulator has no list-users / events routes); the mirror, the user store,// and the directory read are the live layers over `DbService.Live`.//// What this pins://   - the login callback records the signed-in user and EVERY membership//     WorkOS lists (active and pending), with the org row minted so the FK//     holds — from the one membership list it already fetches//   - the callback picks the landing org from that same list: a returnTo//     slug or last-org cookie lands only in an ACTIVE membership, an unknown//     or pending one falls through//   - `inviteMember` mirrors the PENDING membership WorkOS created for the//     invitee (found by email among the org's pending memberships), so the//     member list shows the invite and can revoke it//   - `removeMember` tombstones the mirror row after the WorkOS delete,//     stamped with the membership's last WorkOS state (never a local clock),//     so a replay of the membership as it was before the delete cannot//     restore it while a replacement WorkOS created meanwhile is accepted//   - `updateMemberRole` writes the role WorkOS returned//   - deleting an org marks it deleted locally FIRST, so every member's//     session is refused at once even when the billing cancel, the WorkOS//     delete, or the local purge fails afterwards; billing is cancelled//     BEFORE the WorkOS delete, so a failed cancel leaves the WorkOS org//     intact and the retry finishes the deletion; a retry after WorkOS//     already deleted the org still runs the purge — even while the mirror//     is not ready, when WorkOS can no longer vouch for the admin; a marked//     org leaves the switcher//   - authorization scans an organization the backfill never covered (its//     `backfilled_at` is missing) from WorkOS before reading its mirror,//     once, so a member the mirror never recorded is admitted; an//     organization the mirror does not hold at all is resolved from WorkOS//     for a caller WorkOS confirms as its member, and minted for nobody else//   - the seat gate trusts the mirror's count only for an organization whose//     membership list was scanned from WorkOS in full: an unmarked one is//     scanned first (once), so a partial mirror never admits an invite past//     the plan limit//   - the seat reporter scans an unmarked organization before counting and//     never re-scans a marked one//   - the backfill mirrors every org's members and counts what it wrote,//     writes nothing on a dry run, converges on a re-run, tombstones a//     membership WorkOS no longer lists — but never one written after its//     listing was taken — marks each org backfilled as of its listing, and//     records the events replay boundary BEFORE its first listing and only//     ONCE: a run that fails part-way keeps the marks of the orgs it//     finished and the boundary it recorded, and its retry (like any later//     run) keeps that first boundary — so a user deleted between the failed//     attempt and the retry is still inside the events replay, and the//     reconciler clears their profile//   - two scans of one org that overlap cannot resurrect a membership: a scan//     that listed it, stalled, and resumed after a later listing (which no//     longer had it) was applied is refused whole//   - a login whose membership list was fetched BEFORE the org was purged and//     written after cannot re-mint the org or its membership, one fetched//     before a rename cannot revert the rename, and one fetched before a//     revocation the backfill has since scanned cannot reinstate the//     membership// --------------------------------------------------------------------------- import { describe, expect, it } from "@effect/vitest";import { sql } from "drizzle-orm";import { Effect, Exit, Fiber, Latch, Layer, Option } from "effect";import { HttpRouter, HttpServer } from "effect/unstable/http";import { HttpApiBuilder } from "effect/unstable/httpapi"; import { AccountForbidden } from "@executor-js/api";import {  AccountProvider,  MemberDirectory,  RouterConfigLive,  requestScopedMiddleware,} from "@executor-js/api/server"; import { AccountCaller, workosAccountProvider } from "../account/workos-account-service";import { RequestScopedServicesLive } from "../api/layers";import { DbService } from "../db/db";import { forkReportMemberSeats } from "../extensions/billing/member-seats";import { AutumnError, AutumnService, type AutumnFailure } from "../extensions/billing/service";import { ApiKeyService } from "./api-keys";import { UserStoreService } from "./context";import { UserStoreError, WorkOSError } from "./errors";import { CloudAuthPublicHandlers, CloudSessionAuthHandlers, NonProtectedApi } from "./handlers";import { LAST_ORG_COOKIE } from "./last-org-cookie";import { encodeLoginState } from "./login-state";import { cloudMemberDirectoryLayer } from "./member-directory";import { SessionAuthLive } from "./middleware-live";import { mirrorSignIn } from "./mirror-feeders";import {  ORG_SELECTOR_HEADER,  authorizeOrganization,  markOrganizationDeleted,} from "./organization";import { WorkOSClient, type WorkOSClientService } from "./workos";import { WorkOsMirror, type WorkOsMirrorShape } from "./workos-mirror";import { backfillOrganization, backfillWorkOsMirror } from "./workos-mirror-backfill";import type { WorkOsMembershipPayload, WorkOsUserPayload } from "./workos-mirror-store"; const T1 = "2026-01-01T00:00:00.000Z";const T2 = "2026-01-02T00:00:00.000Z"; // Synthetic identities only. Every test mints its own org ids so the shared// test database never couples two tests.const freshId = (prefix: string) => `${prefix}_${crypto.randomUUID().replaceAll("-", "")}`; const workosUser = (id: string, overrides: Partial<WorkOsUserPayload> = {}) => ({  object: "user" as const,  id,  email: `${id}@placeholder.test`,  emailVerified: true,  firstName: "Ada",  lastName: "Placeholder",  profilePictureUrl: null,  lastSignInAt: T1,  locale: null,  createdAt: T1,  updatedAt: T1,  externalId: null,  metadata: {},  ...overrides,}); interface FakeMembership extends WorkOsMembershipPayload {  readonly organizationName: string;} const workosMembership = (  userId: string,  organizationId: string,  overrides: Partial<FakeMembership> = {},): FakeMembership => ({  id: `om_${userId}_${organizationId}`,  userId,  organizationId,  organizationName: `Org ${organizationId}`,  role: { slug: "member" },  status: "active",  updatedAt: T1,  ...overrides,}); /** Mirrored rows for one org, read through the live cloud `MemberDirectory`. */const readMembers = (organizationId: string) =>  Effect.runPromise(    Effect.flatMap(MemberDirectory.asEffect(), (directory) =>      directory.members(organizationId, {        statuses: ["active", "pending", "inactive"],      }),    ).pipe(      Effect.provide(cloudMemberDirectoryLayer.pipe(Layer.provide(DbService.Live))),      Effect.scoped,    ),  ); /** Mirror an org row (named as of T1) and return the URL slug the store minted for it. */const seedOrganization = (id: string) =>  Effect.runPromise(    Effect.flatMap(UserStoreService.asEffect(), (users) =>      users.use("upsertOrganization", (s) =>        s.upsertOrganization({          id,          name: `Org ${id}`,          updatedAt: new Date(T1),        }),      ),    ).pipe(      Effect.map((org) => org.slug),      Effect.provide(UserStoreService.Live.pipe(Layer.provide(DbService.Live))),      Effect.scoped,    ),  ); const stubAutumn = Layer.succeed(AutumnService)({  use: () => Effect.die("feeders do not read billing"),  ensureCustomer: () => Effect.void,  checkExecutionBalance: () => Effect.die("feeders do not check balances"),  trackExecution: () => Effect.void,  setMemberSeats: () => Effect.void,}); /** * A `WorkOSClient` whose every method is one of `methods`; anything else is * an unexpected call and dies, so a feeder that silently adds a WorkOS read * fails the test instead of passing on a fake. */const stubWorkOS = (methods: Partial<WorkOSClientService>) =>  Layer.succeed(    WorkOSClient,    new Proxy({} as WorkOSClientService, {      get: (_target, prop) =>        (methods as Record<PropertyKey, unknown>)[prop] ??        (() => Effect.die(`unexpected WorkOSClient.${String(prop)} call`)),    }),  ); describe("login callback", () => {  const callbackHandler = (workos: Layer.Layer<WorkOSClient>) =>    HttpRouter.toWebHandler(      HttpApiBuilder.layer(NonProtectedApi).pipe(        Layer.provide(Layer.mergeAll(CloudAuthPublicHandlers, CloudSessionAuthHandlers)),        Layer.provide(requestScopedMiddleware(RequestScopedServicesLive).layer),        Layer.provideMerge(SessionAuthLive),        Layer.provideMerge(stubAutumn),        Layer.provideMerge(workos),        Layer.provideMerge(HttpServer.layerServices),        Layer.provideMerge(RouterConfigLive),      ),      { disableLogger: true },    ).handler;   const STATE_COOKIE = "wos-login-state";   /**   * A callback handler over a fake WorkOS that authenticates `user` with the   * memberships `listed`, recording every WorkOS read (`calls`) and every   * session refresh (`refreshedInto`, the org ids) so the landing-org choice   * is assertable from the outside.   */  const signIn = (user: ReturnType<typeof workosUser>, listed: readonly FakeMembership[]) => {    const calls: string[] = [];    const refreshedInto: (string | undefined)[] = [];    const handler = callbackHandler(      stubWorkOS({        authenticateWithCode: () =>          Effect.succeed({            user,            organizationId: undefined,            accessToken: "access",            refreshToken: "refresh",            sealedSession: "sealed",          }),        listUserMemberships: (id) => {          calls.push(`listUserMemberships:${id}`);          return Effect.succeed({            object: "list" as const,            data: listed as never[],            listMetadata: { before: null, after: null },          });        },        // The landing org's seat recount scans the org from WorkOS the first        // time it is counted (its per-org backfill mark is missing); the        // scan lists the org's members and fetches each user.        listOrgMembers: (organizationId) => {          calls.push(`listOrgMembers:${organizationId}`);          return Effect.succeed({            object: "list" as const,            data: listed.filter((m) => m.organizationId === organizationId) as never[],            listMetadata: { before: null, after: null },          });        },        getUser: (id) => {          calls.push(`getUser:${id}`);          return Effect.succeed(workosUser(id) as never);        },        refreshSession: (_sealed, organizationId) => {          refreshedInto.push(organizationId);          return Effect.succeed("sealed-refreshed");        },      }),    );    return { handler, calls, refreshedInto };  };   /**   * `GET /auth/callback` with the CSRF-matched login `state` (the callback   * refuses any request without one) and any extra cookies; `returnTo`   * rides inside the state as /login mints it.   */  const callbackRequest = (options: { returnTo?: string; cookies?: Record<string, string> }) => {    const url = new URL("http://test.local/auth/callback");    url.searchParams.set("code", "code_1");    const state = encodeLoginState({      nonce: "nonce",      ...(options.returnTo === undefined ? {} : { returnTo: options.returnTo }),    });    url.searchParams.set("state", state);    const cookies = { ...options.cookies, [STATE_COOKIE]: state };    const cookie = Object.entries(cookies)      .map(([name, value]) => `${name}=${value}`)      .join("; ");    return new Request(url, { headers: cookie ? { cookie } : {} });  };   it("records the user and every listed membership from the one list it already fetches", async () => {    const userId = freshId("user");    const activeOrg = freshId("org");    const pendingOrg = freshId("org");    const { handler, calls } = signIn(      workosUser(userId, {        firstName: "Grace",        lastName: "Hopper",        updatedAt: T2,      }),      [        workosMembership(userId, activeOrg, {          role: { slug: "admin" },          updatedAt: T2,        }),        workosMembership(userId, pendingOrg, { status: "pending" }),      ],    );     const response = await handler(callbackRequest({}));     expect(response.status).toBe(302);    expect(      calls,      "one membership list for the callback itself; the landing org, never scanned, is scanned once for its seat count",    ).toEqual([      `listUserMemberships:${userId}`,      `listOrgMembers:${activeOrg}`,      `getUser:${userId}`,    ]);    calls.length = 0;    expect((await handler(callbackRequest({}))).status).toBe(302);    expect(calls, "a second sign-in lists memberships only: the org is now marked").toEqual([      `listUserMemberships:${userId}`,    ]);     const active = await readMembers(activeOrg);    expect(active).toHaveLength(1);    expect(active[0]).toMatchObject({      accountId: userId,      membershipId: `om_${userId}_${activeOrg}`,      email: `${userId}@placeholder.test`,      name: "Grace Hopper",      role: "admin",      status: "active",      lastActiveAt: new Date(T1).getTime(),    });    const pending = await readMembers(pendingOrg);    expect(      pending.map((m) => m.status),      "pending memberships are mirrored too",    ).toEqual(["pending"]);  });   describe("lands in the org the returnTo slug names", () => {    it("when the user holds an active membership there", async () => {      const userId = freshId("user");      const requested = freshId("org");      const other = freshId("org");      const slug = await seedOrganization(requested);      const { handler, refreshedInto } = signIn(workosUser(userId), [        workosMembership(userId, other),        workosMembership(userId, requested),      ]);       const response = await handler(callbackRequest({ returnTo: `/${slug}/settings` }));       expect(response.status).toBe(302);      expect(response.headers.get("location")).toBe(`/${slug}/settings`);      expect(refreshedInto, "the session is switched into the requested org").toEqual([requested]);    });     it("never when the membership there is only pending", async () => {      const userId = freshId("user");      const requested = freshId("org");      const other = freshId("org");      const slug = await seedOrganization(requested);      const { handler, refreshedInto } = signIn(workosUser(userId), [        workosMembership(userId, other),        workosMembership(userId, requested, { status: "pending" }),      ]);       const response = await handler(callbackRequest({ returnTo: `/${slug}` }));       expect(response.status).toBe(302);      expect(response.headers.get("location")).toBe(`/${slug}`);      expect(        refreshedInto,        "a pending membership is not a landing candidate, and an explicit slug does not fall back to another org",      ).toEqual([]);    });  });   describe("without a returnTo org", () => {    it("lands in the last-org cookie's org when the user is active there", async () => {      const userId = freshId("user");      const last = freshId("org");      const other = freshId("org");      const slug = await seedOrganization(last);      const { handler, refreshedInto } = signIn(workosUser(userId), [        workosMembership(userId, other),        workosMembership(userId, last),      ]);       const response = await handler(callbackRequest({ cookies: { [LAST_ORG_COOKIE]: slug } }));       expect(response.status).toBe(302);      expect(response.headers.get("location")).toBe("/");      expect(refreshedInto).toEqual([last]);    });     it("falls through an unknown last-org slug to the first active membership", async () => {      const userId = freshId("user");      const pendingOrg = freshId("org");      const activeOrg = freshId("org");      const { handler, refreshedInto } = signIn(workosUser(userId), [        workosMembership(userId, pendingOrg, { status: "pending" }),        workosMembership(userId, activeOrg),      ]);       const response = await handler(        // Valid slug grammar, never minted: the store finds no org for it.        callbackRequest({ cookies: { [LAST_ORG_COOKIE]: "no-such-org-slug" } }),      );       expect(response.status).toBe(302);      expect(refreshedInto).toEqual([activeOrg]);    });  });}); describe("a delayed sign-in feeder", () => {  /** The live mirror, user store, and directory over one test-db socket. */  const Services = Layer.mergeAll(    UserStoreService.Live,    WorkOsMirror.Live,    cloudMemberDirectoryLayer,  ).pipe(Layer.provideMerge(DbService.Live));   const run = <A, E>(    body: Effect.Effect<A, E, UserStoreService | WorkOsMirror | MemberDirectory | DbService>,  ) => Effect.runPromise(body.pipe(Effect.provide(Services), Effect.scoped));   const readOrganization = (org: string) =>    Effect.flatMap(UserStoreService.asEffect(), (users) =>      users.use("getOrganization", (s) => s.getOrganization(org)),    );   const readMembership = (userId: string, org: string) =>    Effect.flatMap(MemberDirectory.asEffect(), (directory) =>      directory.membership(userId, org, ["active", "pending", "inactive"]),    );   it("cannot re-mint a purged organization or its membership from a list fetched before the purge", async () => {    const userId = freshId("user");    const org = freshId("org");    await seedOrganization(org);    const result = await run(      Effect.gen(function* () {        const users = yield* UserStoreService;        // The login fetched its membership list at T1, while the org lived...        const fetchedAt = new Date(T1);        const listed = [workosMembership(userId, org)];        // ...then stalled while cloud's deletion flow purged the org at T2.        yield* users.use("deleteOrganizationCascade", (s) =>          s.deleteOrganizationCascade(org, new Date(T2)),        );        // The stalled login resumes and writes what it holds.        yield* mirrorSignIn(workosUser(userId), listed, fetchedAt);        return {          organization: yield* readOrganization(org),          membership: yield* readMembership(userId, org),        };      }),    );    expect(result.organization?.deletedAt, "the org stays a deleted tombstone").toEqual(      new Date(T2),    );    expect(result.membership, "and holds no membership: nothing to authorize").toBeNull();  });   it("cannot reinstate a membership from a list fetched before a revocation the backfill has since scanned", async () => {    const userId = freshId("user");    const org = freshId("org");    await seedOrganization(org);    const result = await run(      Effect.gen(function* () {        const mirror = yield* WorkOsMirror;        // The login fetched its membership list at T1, while the user was        // a member, then stalled...        const fetchedAt = new Date(T1);        const listed = [workosMembership(userId, org)];        // ...WorkOS revoked the membership before the mirror was ever        // backfilled, and the backfill then scanned the org at T2 without        // it: no tombstone, the row was never there — and the revocation        // predates the events replay boundary, so no event will land it.        yield* mirror.applyOrganizationScan({          organizationId: org,          listedAt: new Date(T2),          members: [],        });        // The stalled login resumes and writes what it holds.        yield* mirrorSignIn(workosUser(userId), listed, fetchedAt);        const membership = yield* readMembership(userId, org);        // A login after the scan, carrying a membership WorkOS created        // since (stamped past the scan), is recorded.        const rejoinedAt = "2026-01-03T00:00:00.000Z";        yield* mirrorSignIn(          workosUser(userId),          [            workosMembership(userId, org, {              id: `om_${userId}_${org}_2`,              updatedAt: rejoinedAt,            }),          ],          new Date(rejoinedAt),

Lines 1–500 of 2021.