MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-392

6.10.7 Vitest — org model + membership gating + backfill

Done
Description

Estimate: 50m · Depends on: 6.10.4

Lock the org tier with tests over a real Postgres (the project convention; the only allowed vi.mock is getSession()). Exercise the model, the gating, and the backfill for real.

The model + repositories (6.10.3):

  • An org + an OrganizationMembership create/read round-trips; the (organizationId, userId) uniqueness holds; the Workspace.organizationId relation resolves both ways.
  • Write repo methods require tx (a compile-time guarantee; assert the create runs inside a transaction).

The access gating (6.10.4):

  • A user who is a member of a WORKSPACE but NOT of its ORG is DENIED access (org membership gates workspace access) and sees 404-not-403 cross-tenant.
  • An org OWNER/ADMIN is granted admin-equivalent access to EVERY workspace under the org (the role composes above the 6.4 MemberRole); an org MEMBER falls back to their per-workspace role.
  • Membership direction (6.10.2 §5): adding a user to a WORKSPACE auto-creates their org membership (assert the OrganizationMembership row appears); adding a user to the ORG creates NO workspace membership (assert an org-only member reaches zero workspaces until explicitly added); removing from the org revokes all workspace access while removing from a workspace leaves the org membership intact.
  • The cross-workspace member listing returns members across the org’s workspaces and PAGINATES (assert a page boundary, not a full-table load — the at-scale rule).
  • A role change / removal is one transaction; a concurrent membership change serializes via the row lock (no lost update).

The backfill (6.10.3):

  • Seeding pre-org workspaces then running the backfill creates exactly ONE default org per workspace, points each at it, and makes an owner membership; NO workspace is left with a null organizationId; re-running the backfill is idempotent (no duplicates).

Acceptance criteria

  • The above cases pass over a real Postgres (only getSession() mocked); the gating + backfill + pagination are exercised for real, not asserted on mocks.
  • The member-of-workspace-but-not-org denial, the org-owner-spans-all-workspaces grant, and the one-default-org-per-workspace backfill each have a direct test.
  • New service/repo code respects the per-file coverage gate (motir-core/CLAUDE.md § coverage); the empty-input / no-membership / idempotent-backfill guards each have a direct test (a new repo method’s empty-input branch needs its own assertion or the gate fails).

Context refs

  • 6.10.3 / 6.10.4 (the schema + gating under test).
  • motir-core/CLAUDE.md § tests-use-real-Postgres + § coverage gate.
  • motir-core/tests/helpers/db.ts — the per-test truncation helper the suite runs over.