History

Replace branded ID casts with schema constructors

Rhys Sullivan committed 8 months ago

Commit 93ee28f1 parent 8c9d74211 files · +114 −86Browse files

Unified · Split

11 files
  1. packages/control-plane/src/api/auth/workspace-membership.test.ts +20 −10
  2. packages/control-plane/src/api/executions/http.ts +5 −7
  3. packages/control-plane/src/api/memberships/http.ts +5 −4
  4. packages/control-plane/src/api/organizations/http.ts +3 −2
  5. packages/control-plane/src/api/policies/http.ts +7 −9
  6. packages/control-plane/src/api/sources/http.ts +7 −9
  7. packages/control-plane/src/api/workspaces/http.ts +11 −13
  8. packages/control-plane/src/domain/auth/actor.test.ts +15 −8
  9. packages/control-plane/src/domain/auth/policy.test.ts +6 −2
  10. packages/control-plane/src/persistence/control-plane-rows.test.ts +33 −20
  11. packages/control-plane/src/runtime/control-plane-runtime.test.ts +2 −2
packages/control-plane/src/api/auth/workspace-membership.test.ts+20−10
@@ -1,15 +1,25 @@
1import { describe, expect, it } from "@effect/vitest";1import { describe, expect, it } from "@effect/vitest";
2import {
3 AccountIdSchema,
4 OrganizationIdSchema,
5 OrganizationMemberIdSchema,
6 WorkspaceIdSchema,
7} from "#schema";
2 8
3import { deriveWorkspaceMembershipsForPrincipal } from "./workspace-membership";9import { deriveWorkspaceMembershipsForPrincipal } from "./workspace-membership";
4 10
5describe("workspace membership derivation", () => {11describe("workspace membership derivation", () => {
6 it("prefers highest active role in matching organization", () => {12 it("prefers highest active role in matching organization", () => {
13 const principalAccountId = AccountIdSchema.make("acc_1");
14 const workspaceId = WorkspaceIdSchema.make("ws_1");
15 const organizationId = OrganizationIdSchema.make("org_1");
16
7 const memberships = deriveWorkspaceMembershipsForPrincipal({17 const memberships = deriveWorkspaceMembershipsForPrincipal({
8 principalAccountId: "acc_1" as never,18 principalAccountId,
9 workspaceId: "ws_1" as never,19 workspaceId,
10 workspace: {20 workspace: {
11 id: "ws_1" as never,21 id: workspaceId,
12 organizationId: "org_1" as never,22 organizationId,
13 name: "Main",23 name: "Main",
14 createdByAccountId: null,24 createdByAccountId: null,
15 createdAt: 1,25 createdAt: 1,
@@ -17,9 +27,9 @@ describe("workspace membership derivation", () => { · 1 unmodified line
17 },27 },
18 organizationMemberships: [28 organizationMemberships: [
19 {29 {
20 id: "mem_1" as never,30 id: OrganizationMemberIdSchema.make("mem_1"),
21 organizationId: "org_1" as never,31 organizationId,
22 accountId: "acc_1" as never,32 accountId: principalAccountId,
23 role: "viewer",33 role: "viewer",
24 status: "active",34 status: "active",
25 billable: true,35 billable: true,
@@ -29,9 +39,9 @@ describe("workspace membership derivation", () => { · 3 unmodified lines
29 updatedAt: 1,39 updatedAt: 1,
30 },40 },
31 {41 {
32 id: "mem_2" as never,42 id: OrganizationMemberIdSchema.make("mem_2"),
33 organizationId: "org_1" as never,43 organizationId,
34 accountId: "acc_1" as never,44 accountId: principalAccountId,
35 role: "admin",45 role: "admin",
36 status: "active",46 status: "active",
37 billable: true,47 billable: true,
packages/control-plane/src/api/executions/http.ts+5−7
@@ -1,5 +1,6 @@
1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import type { WorkspaceId } from "#schema";
3 4
4import {5import {
5 Actor,6 Actor,
@@ -37,21 +38,18 @@ const toUnauthorizedError = ( · 31 unmodified lines
37 details: "Authentication required",38 details: "Authentication required",
38 });39 });
39 40
40const resolveWorkspaceActor = (workspaceId: string) =>41const resolveWorkspaceActor = (workspaceId: WorkspaceId) =>
41 Effect.gen(function* () {42 Effect.gen(function* () {
42 const actorResolver = yield* ControlPlaneActorResolver;43 const actorResolver = yield* ControlPlaneActorResolver;
43 const request = yield* HttpServerRequest.HttpServerRequest;44 const request = yield* HttpServerRequest.HttpServerRequest;
44 45
45 return yield* actorResolver.resolveWorkspaceActor({46 return yield* actorResolver.resolveWorkspaceActor({ workspaceId, headers: request.headers });
46 workspaceId: workspaceId as never,
47 headers: request.headers,
48 });
49 });47 });
50 48
51const requireExecuteWorkspace = (workspaceId: string) =>49const requireExecuteWorkspace = (workspaceId: WorkspaceId) =>
52 requirePermission({50 requirePermission({
53 permission: "workspace:read",51 permission: "workspace:read",
54 workspaceId: workspaceId as never,52 workspaceId,
55 });53 });
56 54
57export const ControlPlaneExecutionsLive = HttpApiBuilder.group(55export const ControlPlaneExecutionsLive = HttpApiBuilder.group(
packages/control-plane/src/api/memberships/http.ts+5−4
@@ -1,5 +1,6 @@
1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import type { OrganizationId } from "#schema";
3 4
4import {5import {
5 Actor,6 Actor,
@@ -43,16 +44,16 @@ const resolveActor = Effect.gen(function* () { · 37 unmodified lines
43 return yield* actorResolver.resolveActor({ headers: request.headers });44 return yield* actorResolver.resolveActor({ headers: request.headers });
44});45});
45 46
46const requireReadMemberships = (organizationId: string) =>47const requireReadMemberships = (organizationId: OrganizationId) =>
47 requirePermission({48 requirePermission({
48 permission: "memberships:read",49 permission: "memberships:read",
49 organizationId: organizationId as never,50 organizationId,
50 });51 });
51 52
52const requireWriteMemberships = (organizationId: string) =>53const requireWriteMemberships = (organizationId: OrganizationId) =>
53 requirePermission({54 requirePermission({
54 permission: "memberships:write",55 permission: "memberships:write",
55 organizationId: organizationId as never,56 organizationId,
56 });57 });
57 58
58export const ControlPlaneMembershipsLive = HttpApiBuilder.group(59export const ControlPlaneMembershipsLive = HttpApiBuilder.group(
packages/control-plane/src/api/organizations/http.ts+3−2
@@ -1,5 +1,6 @@
1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import type { OrganizationId } from "#schema";
3 4
4import {5import {
5 Actor,6 Actor,
@@ -47,10 +48,10 @@ const requireReadOrganizations = requirePermission({ · 41 unmodified lines
47 permission: "organizations:read",48 permission: "organizations:read",
48});49});
49 50
50const requireManageOrganizations = (organizationId: string) =>51const requireManageOrganizations = (organizationId: OrganizationId) =>
51 requirePermission({52 requirePermission({
52 permission: "organizations:manage",53 permission: "organizations:manage",
53 organizationId: organizationId as never,54 organizationId,
54 });55 });
55 56
56export const ControlPlaneOrganizationsLive = HttpApiBuilder.group(57export const ControlPlaneOrganizationsLive = HttpApiBuilder.group(
packages/control-plane/src/api/policies/http.ts+7−9
@@ -1,5 +1,6 @@
1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import type { WorkspaceId } from "#schema";
3 4
4import {5import {
5 Actor,6 Actor,
@@ -37,27 +38,24 @@ const toUnauthorizedError = ( · 31 unmodified lines
37 details: "Authentication required",38 details: "Authentication required",
38 });39 });
39 40
40const resolveWorkspaceActor = (workspaceId: string) =>41const resolveWorkspaceActor = (workspaceId: WorkspaceId) =>
41 Effect.gen(function* () {42 Effect.gen(function* () {
42 const actorResolver = yield* ControlPlaneActorResolver;43 const actorResolver = yield* ControlPlaneActorResolver;
43 const request = yield* HttpServerRequest.HttpServerRequest;44 const request = yield* HttpServerRequest.HttpServerRequest;
44 45
45 return yield* actorResolver.resolveWorkspaceActor({46 return yield* actorResolver.resolveWorkspaceActor({ workspaceId, headers: request.headers });
46 workspaceId: workspaceId as never,
47 headers: request.headers,
48 });
49 });47 });
50 48
51const requireReadPolicies = (workspaceId: string) =>49const requireReadPolicies = (workspaceId: WorkspaceId) =>
52 requirePermission({50 requirePermission({
53 permission: "policies:read",51 permission: "policies:read",
54 workspaceId: workspaceId as never,52 workspaceId,
55 });53 });
56 54
57const requireWritePolicies = (workspaceId: string) =>55const requireWritePolicies = (workspaceId: WorkspaceId) =>
58 requirePermission({56 requirePermission({
59 permission: "policies:write",57 permission: "policies:write",
60 workspaceId: workspaceId as never,58 workspaceId,
61 });59 });
62 60
63export const ControlPlanePoliciesLive = HttpApiBuilder.group(61export const ControlPlanePoliciesLive = HttpApiBuilder.group(
packages/control-plane/src/api/sources/http.ts+7−9
@@ -1,5 +1,6 @@
1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import type { WorkspaceId } from "#schema";
3 4
4import {5import {
5 Actor,6 Actor,
@@ -37,27 +38,24 @@ const toUnauthorizedError = ( · 31 unmodified lines
37 details: "Authentication required",38 details: "Authentication required",
38 });39 });
39 40
40const resolveWorkspaceActor = (workspaceId: string) =>41const resolveWorkspaceActor = (workspaceId: WorkspaceId) =>
41 Effect.gen(function* () {42 Effect.gen(function* () {
42 const actorResolver = yield* ControlPlaneActorResolver;43 const actorResolver = yield* ControlPlaneActorResolver;
43 const request = yield* HttpServerRequest.HttpServerRequest;44 const request = yield* HttpServerRequest.HttpServerRequest;
44 45
45 return yield* actorResolver.resolveWorkspaceActor({46 return yield* actorResolver.resolveWorkspaceActor({ workspaceId, headers: request.headers });
46 workspaceId: workspaceId as never,
47 headers: request.headers,
48 });
49 });47 });
50 48
51const requireReadSources = (workspaceId: string) =>49const requireReadSources = (workspaceId: WorkspaceId) =>
52 requirePermission({50 requirePermission({
53 permission: "sources:read",51 permission: "sources:read",
54 workspaceId: workspaceId as never,52 workspaceId,
55 });53 });
56 54
57const requireWriteSources = (workspaceId: string) =>55const requireWriteSources = (workspaceId: WorkspaceId) =>
58 requirePermission({56 requirePermission({
59 permission: "sources:write",57 permission: "sources:write",
60 workspaceId: workspaceId as never,58 workspaceId,
61 });59 });
62 60
63export const ControlPlaneSourcesLive = HttpApiBuilder.group(61export const ControlPlaneSourcesLive = HttpApiBuilder.group(
packages/control-plane/src/api/workspaces/http.ts+11−13
@@ -1,5 +1,6 @@
1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";1import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3import type { OrganizationId, WorkspaceId } from "#schema";
3 4
4import {5import {
5 Actor,6 Actor,
@@ -43,39 +44,36 @@ const resolveActor = Effect.gen(function* () { · 37 unmodified lines
43 return yield* actorResolver.resolveActor({ headers: request.headers });44 return yield* actorResolver.resolveActor({ headers: request.headers });
44});45});
45 46
46const resolveWorkspaceActor = (workspaceId: string) =>47const resolveWorkspaceActor = (workspaceId: WorkspaceId) =>
47 Effect.gen(function* () {48 Effect.gen(function* () {
48 const actorResolver = yield* ControlPlaneActorResolver;49 const actorResolver = yield* ControlPlaneActorResolver;
49 const request = yield* HttpServerRequest.HttpServerRequest;50 const request = yield* HttpServerRequest.HttpServerRequest;
50 51
51 return yield* actorResolver.resolveWorkspaceActor({52 return yield* actorResolver.resolveWorkspaceActor({ workspaceId, headers: request.headers });
52 workspaceId: workspaceId as never,
53 headers: request.headers,
54 });
55 });53 });
56 54
57const requireReadWorkspace = (workspaceId: string) =>55const requireReadWorkspace = (workspaceId: WorkspaceId) =>
58 requirePermission({56 requirePermission({
59 permission: "workspace:read",57 permission: "workspace:read",
60 workspaceId: workspaceId as never,58 workspaceId,
61 });59 });
62 60
63const requireManageWorkspace = (workspaceId: string) =>61const requireManageWorkspace = (workspaceId: WorkspaceId) =>
64 requirePermission({62 requirePermission({
65 permission: "workspace:manage",63 permission: "workspace:manage",
66 workspaceId: workspaceId as never,64 workspaceId,
67 });65 });
68 66
69const requireOrganizationWorkspaceRead = (organizationId: string) =>67const requireOrganizationWorkspaceRead = (organizationId: OrganizationId) =>
70 requirePermission({68 requirePermission({
71 permission: "workspace:read",69 permission: "workspace:read",
72 organizationId: organizationId as never,70 organizationId,
73 });71 });
74 72
75const requireOrganizationWorkspaceManage = (organizationId: string) =>73const requireOrganizationWorkspaceManage = (organizationId: OrganizationId) =>
76 requirePermission({74 requirePermission({
77 permission: "workspace:manage",75 permission: "workspace:manage",
78 organizationId: organizationId as never,76 organizationId,
79 });77 });
80 78
81export const ControlPlaneWorkspacesLive = HttpApiBuilder.group(79export const ControlPlaneWorkspacesLive = HttpApiBuilder.group(
packages/control-plane/src/domain/auth/actor.test.ts+15−8
@@ -1,4 +1,5 @@
1import { describe, expect, it } from "@effect/vitest";1import { describe, expect, it } from "@effect/vitest";
2import { AccountIdSchema, WorkspaceIdSchema } from "#schema";
2import * as Effect from "effect/Effect";3import * as Effect from "effect/Effect";
3 4
4import {5import {
@@ -10,9 +11,12 @@ import { · 5 unmodified lines
10describe("control-plane-domain actor", () => {11describe("control-plane-domain actor", () => {
11 it.effect("allows workspace permissions from active membership", () =>12 it.effect("allows workspace permissions from active membership", () =>
12 Effect.gen(function* () {13 Effect.gen(function* () {
14 const accountId = AccountIdSchema.make("acc_1");
15 const workspaceId = WorkspaceIdSchema.make("ws_1");
16
13 const actor = yield* makeActor({17 const actor = yield* makeActor({
14 principal: {18 principal: {
15 accountId: "acc_1" as never,19 accountId,
16 provider: "local",20 provider: "local",
17 subject: "local:acc_1",21 subject: "local:acc_1",
18 email: null,22 email: null,
@@ -20,8 +24,8 @@ describe("control-plane-domain actor", () => { · 1 unmodified line
20 },24 },
21 workspaceMemberships: [25 workspaceMemberships: [
22 {26 {
23 accountId: "acc_1" as never,27 accountId,
24 workspaceId: "ws_1" as never,28 workspaceId,
25 role: "editor",29 role: "editor",
26 status: "active",30 status: "active",
27 grantedAt: 1,31 grantedAt: 1,
@@ -33,16 +37,19 @@ describe("control-plane-domain actor", () => { · 5 unmodified lines
33 37
34 yield* actor.requirePermission({38 yield* actor.requirePermission({
35 permission: "sources:write",39 permission: "sources:write",
36 workspaceId: "ws_1" as never,40 workspaceId,
37 });41 });
38 }),42 }),
39 );43 );
40 44
41 it.effect("denies permission when role is insufficient", () =>45 it.effect("denies permission when role is insufficient", () =>
42 Effect.gen(function* () {46 Effect.gen(function* () {
47 const accountId = AccountIdSchema.make("acc_1");
48 const workspaceId = WorkspaceIdSchema.make("ws_1");
49
43 const actor = yield* makeActor({50 const actor = yield* makeActor({
44 principal: {51 principal: {
45 accountId: "acc_1" as never,52 accountId,
46 provider: "local",53 provider: "local",
47 subject: "local:acc_1",54 subject: "local:acc_1",
48 email: null,55 email: null,
@@ -50,8 +57,8 @@ describe("control-plane-domain actor", () => { · 1 unmodified line
50 },57 },
51 workspaceMemberships: [58 workspaceMemberships: [
52 {59 {
53 accountId: "acc_1" as never,60 accountId,
54 workspaceId: "ws_1" as never,61 workspaceId,
55 role: "viewer",62 role: "viewer",
56 status: "active",63 status: "active",
57 grantedAt: 1,64 grantedAt: 1,
@@ -64,7 +71,7 @@ describe("control-plane-domain actor", () => { · 6 unmodified lines
64 const denied = yield* Effect.either(71 const denied = yield* Effect.either(
65 actor.requirePermission({72 actor.requirePermission({
66 permission: "sources:write",73 permission: "sources:write",
67 workspaceId: "ws_1" as never,74 workspaceId,
68 }),75 }),
69 );76 );
70 77
packages/control-plane/src/domain/auth/policy.test.ts+6−2
@@ -1,4 +1,5 @@
1import { describe, expect, it } from "@effect/vitest";1import { describe, expect, it } from "@effect/vitest";
2import { AccountIdSchema, WorkspaceIdSchema } from "#schema";
2import * as Effect from "effect/Effect";3import * as Effect from "effect/Effect";
3 4
4import { Actor, makeAllowAllActor } from "./actor";5import { Actor, makeAllowAllActor } from "./actor";
@@ -7,8 +8,11 @@ import { requirePermission, withPolicy } from "./policy"; · 2 unmodified lines
7describe("control-plane-domain policy", () => {8describe("control-plane-domain policy", () => {
8 it.effect("runs protected effect when permission is granted", () =>9 it.effect("runs protected effect when permission is granted", () =>
9 Effect.gen(function* () {10 Effect.gen(function* () {
11 const accountId = AccountIdSchema.make("acc_1");
12 const workspaceId = WorkspaceIdSchema.make("ws_1");
13
10 const principal = {14 const principal = {
11 accountId: "acc_1" as never,15 accountId,
12 provider: "local" as const,16 provider: "local" as const,
13 subject: "local:acc_1",17 subject: "local:acc_1",
14 email: null,18 email: null,
@@ -18,7 +22,7 @@ describe("control-plane-domain policy", () => { · 3 unmodified lines
18 const result = yield* withPolicy(22 const result = yield* withPolicy(
19 requirePermission({23 requirePermission({
20 permission: "workspace:read",24 permission: "workspace:read",
21 workspaceId: "ws_1" as never,25 workspaceId,
22 }),26 }),
23 )(Effect.succeed("ok")).pipe(27 )(Effect.succeed("ok")).pipe(
24 Effect.provideService(Actor, makeAllowAllActor(principal)),28 Effect.provideService(Actor, makeAllowAllActor(principal)),
packages/control-plane/src/persistence/control-plane-rows.test.ts+33−20
@@ -1,4 +1,12 @@
1import { describe, expect, it } from "@effect/vitest";1import { describe, expect, it } from "@effect/vitest";
2import {
3 AccountIdSchema,
4 OrganizationIdSchema,
5 OrganizationMemberIdSchema,
6 PolicyIdSchema,
7 SourceIdSchema,
8 WorkspaceIdSchema,
9} from "#schema";
2import * as Effect from "effect/Effect";10import * as Effect from "effect/Effect";
3import * as Option from "effect/Option";11import * as Option from "effect/Option";
4 12
@@ -20,29 +28,32 @@ describe("control-plane-persistence-drizzle", () => { · 15 unmodified lines
20 Effect.gen(function* () {28 Effect.gen(function* () {
21 const persistence = yield* makePersistence;29 const persistence = yield* makePersistence;
22 const now = Date.now();30 const now = Date.now();
31 const organizationId = OrganizationIdSchema.make("org_1");
32 const accountId = AccountIdSchema.make("acc_1");
33 const workspaceId = WorkspaceIdSchema.make("ws_1");
23 34
24 yield* persistence.rows.organizations.insert({35 yield* persistence.rows.organizations.insert({
25 id: "org_1" as never,36 id: organizationId,
26 slug: "acme",37 slug: "acme",
27 name: "Acme",38 name: "Acme",
28 status: "active",39 status: "active",
29 createdByAccountId: "acc_1" as never,40 createdByAccountId: accountId,
30 createdAt: now,41 createdAt: now,
31 updatedAt: now,42 updatedAt: now,
32 });43 });
33 44
34 yield* persistence.rows.workspaces.insert({45 yield* persistence.rows.workspaces.insert({
35 id: "ws_1" as never,46 id: workspaceId,
36 organizationId: "org_1" as never,47 organizationId,
37 name: "Main",48 name: "Main",
38 createdByAccountId: "acc_1" as never,49 createdByAccountId: accountId,
39 createdAt: now,50 createdAt: now,
40 updatedAt: now,51 updatedAt: now,
41 });52 });
42 53
43 yield* persistence.rows.sources.insert({54 yield* persistence.rows.sources.insert({
44 id: "src_1" as never,55 id: SourceIdSchema.make("src_1"),
45 workspaceId: "ws_1" as never,56 workspaceId,
46 name: "Github",57 name: "Github",
47 kind: "openapi",58 kind: "openapi",
48 endpoint: "https://api.github.com/openapi.json",59 endpoint: "https://api.github.com/openapi.json",
@@ -56,8 +67,8 @@ describe("control-plane-persistence-drizzle", () => { · 7 unmodified lines
56 });67 });
57 68
58 yield* persistence.rows.policies.insert({69 yield* persistence.rows.policies.insert({
59 id: "pol_1" as never,70 id: PolicyIdSchema.make("pol_1"),
60 workspaceId: "ws_1" as never,71 workspaceId,
61 targetAccountId: null,72 targetAccountId: null,
62 clientId: null,73 clientId: null,
63 resourceType: "tool_path",74 resourceType: "tool_path",
@@ -72,14 +83,14 @@ describe("control-plane-persistence-drizzle", () => { · 8 unmodified lines
72 updatedAt: now,83 updatedAt: now,
73 });84 });
74 85
75 const workspace = yield* persistence.rows.workspaces.getById("ws_1" as never);86 const workspace = yield* persistence.rows.workspaces.getById(workspaceId);
76 expect(Option.isSome(workspace)).toBe(true);87 expect(Option.isSome(workspace)).toBe(true);
77 88
78 const sources = yield* persistence.rows.sources.listByWorkspaceId("ws_1" as never);89 const sources = yield* persistence.rows.sources.listByWorkspaceId(workspaceId);
79 expect(sources).toHaveLength(1);90 expect(sources).toHaveLength(1);
80 expect(sources[0]?.name).toBe("Github");91 expect(sources[0]?.name).toBe("Github");
81 92
82 const policies = yield* persistence.rows.policies.listByWorkspaceId("ws_1" as never);93 const policies = yield* persistence.rows.policies.listByWorkspaceId(workspaceId);
83 expect(policies).toHaveLength(1);94 expect(policies).toHaveLength(1);
84 expect(policies[0]?.resourcePattern).toBe("source.github.*");95 expect(policies[0]?.resourcePattern).toBe("source.github.*");
85 }),96 }),
@@ -89,11 +100,13 @@ describe("control-plane-persistence-drizzle", () => { · 3 unmodified lines
89 Effect.gen(function* () {100 Effect.gen(function* () {
90 const persistence = yield* makePersistence;101 const persistence = yield* makePersistence;
91 const now = Date.now();102 const now = Date.now();
103 const organizationId = OrganizationIdSchema.make("org_1");
104 const accountId = AccountIdSchema.make("acc_1");
92 105
93 yield* persistence.rows.organizationMemberships.upsert({106 yield* persistence.rows.organizationMemberships.upsert({
94 id: "mem_1" as never,107 id: OrganizationMemberIdSchema.make("mem_1"),
95 organizationId: "org_1" as never,108 organizationId,
96 accountId: "acc_1" as never,109 accountId,
97 role: "viewer",110 role: "viewer",
98 status: "active",111 status: "active",
99 billable: true,112 billable: true,
@@ -104,9 +117,9 @@ describe("control-plane-persistence-drizzle", () => { · 4 unmodified lines
104 });117 });
105 118
106 yield* persistence.rows.organizationMemberships.upsert({119 yield* persistence.rows.organizationMemberships.upsert({
107 id: "mem_2" as never,120 id: OrganizationMemberIdSchema.make("mem_2"),
108 organizationId: "org_1" as never,121 organizationId,
109 accountId: "acc_1" as never,122 accountId,
110 role: "admin",123 role: "admin",
111 status: "active",124 status: "active",
112 billable: true,125 billable: true,
@@ -117,8 +130,8 @@ describe("control-plane-persistence-drizzle", () => { · 4 unmodified lines
117 });130 });
118 131
119 const membership = yield* persistence.rows.organizationMemberships.getByOrganizationAndAccount(132 const membership = yield* persistence.rows.organizationMemberships.getByOrganizationAndAccount(
120 "org_1" as never,133 organizationId,
121 "acc_1" as never,134 accountId,
122 );135 );
123 136
124 expect(Option.isSome(membership)).toBe(true);137 expect(Option.isSome(membership)).toBe(true);
packages/control-plane/src/runtime/control-plane-runtime.test.ts+2−2
@@ -1,7 +1,7 @@
1import { describe, expect, it } from "@effect/vitest";1import { describe, expect, it } from "@effect/vitest";
2import * as Effect from "effect/Effect";2import * as Effect from "effect/Effect";
3 3
4import type { AccountId } from "#schema";4import { WorkspaceIdSchema, type AccountId } from "#schema";
5 5
6import { makeSqlControlPlaneRuntime } from "./index";6import { makeSqlControlPlaneRuntime } from "./index";
7import { withControlPlaneClient } from "./test-http-client";7import { withControlPlaneClient } from "./test-http-client";
@@ -312,7 +312,7 @@ describe("control-plane-runtime", () => { · 304 unmodified lines
312 expect(deleteOrg.removed).toBe(true);312 expect(deleteOrg.removed).toBe(true);
313 313
314 const deletedWorkspaceLookup = yield* Effect.either(314 const deletedWorkspaceLookup = yield* Effect.either(
315 runtime.service.getWorkspace(workspace.id as never),315 runtime.service.getWorkspace(WorkspaceIdSchema.make(workspace.id)),
316 );316 );
317 expect(deletedWorkspaceLookup._tag).toBe("Left");317 expect(deletedWorkspaceLookup._tag).toBe("Left");
318 318