1import * as Prisma from "@/Prisma";
2import type { PrismaManagementClient } from "@/Prisma/Client";
3import { describe, expect, it } from "alchemy-test";
4import {
5 managementApiContract,
6 productionManagementApiRoutes,
7} from "./fixtures/ManagementApiContract.ts";
8
9const canonicalComputeOperations = [
10 "listApps",
11 "getApp",
12 "createApp",
13 "updateApp",
14 "deleteApp",
15 "promoteApp",
16 "rollbackApp",
17 "listAppDomains",
18 "createAppDomain",
19 "listAppDeployments",
20 "createAppDeployment",
21 "getDeployment",
22 "deleteDeployment",
23 "startDeployment",
24 "stopDeployment",
25 "getDeploymentLogsRequest",
26 "getBuildLogsRequest",
27] as const satisfies ReadonlyArray<keyof PrismaManagementClient>;
28
29const lifecycleResources = [
30 {
31 name: "Project",
32 resource: Prisma.Project,
33 routes: [
34 "GET /v1/projects",
35 "POST /v1/projects",
36 "GET /v1/projects/{id}",
37 "PATCH /v1/projects/{id}",
38 "DELETE /v1/projects/{id}",
39 ],
40 },
41 {
42 name: "Database",
43 resource: Prisma.Database,
44 routes: [
45 "GET /v1/databases",
46 "POST /v1/databases",
47 "GET /v1/projects/{projectId}/databases",
48 "POST /v1/projects/{projectId}/databases",
49 "GET /v1/databases/{databaseId}",
50 "PATCH /v1/databases/{databaseId}",
51 "DELETE /v1/databases/{databaseId}",
52 ],
53 },
54 {
55 name: "Connection",
56 resource: Prisma.Connection,
57 routes: [
58 "GET /v1/connections",
59 "POST /v1/connections",
60 "GET /v1/databases/{databaseId}/connections",
61 "POST /v1/databases/{databaseId}/connections",
62 "GET /v1/connections/{id}",
63 "DELETE /v1/connections/{id}",
64 ],
65 },
66 {
67 name: "Branch",
68 resource: Prisma.Branch,
69 routes: [
70 "GET /v1/projects/{projectId}/branches",
71 "POST /v1/projects/{projectId}/branches",
72 "GET /v1/branches/{branchId}",
73 "PATCH /v1/branches/{branchId}",
74 "DELETE /v1/branches/{branchId}",
75 ],
76 },
77 {
78 name: "Bucket",
79 resource: Prisma.Bucket,
80 routes: [
81 "GET /v1/buckets",
82 "POST /v1/buckets",
83 "GET /v1/buckets/{bucketId}",
84 "DELETE /v1/buckets/{bucketId}",
85 ],
86 },
87 {
88 name: "BucketAccessKey",
89 resource: Prisma.BucketAccessKey,
90 routes: [
91 "GET /v1/buckets/{bucketId}/keys",
92 "POST /v1/buckets/{bucketId}/keys",
93 "DELETE /v1/buckets/{bucketId}/keys/{keyId}",
94 ],
95 },
96 {
97 name: "App",
98 resource: Prisma.App,
99 routes: [
100 "GET /v1/apps",
101 "POST /v1/apps",
102 "GET /v1/apps/{appId}",
103 "PATCH /v1/apps/{appId}",
104 "DELETE /v1/apps/{appId}",
105 ],
106 },
107 {
108 name: "Deployment",
109 resource: Prisma.Deployment,
110 routes: [
111 "GET /v1/apps/{appId}/deployments",
112 "POST /v1/apps/{appId}/deployments",
113 "GET /v1/deployments/{deploymentId}",
114 "DELETE /v1/deployments/{deploymentId}",
115 "POST /v1/deployments/{deploymentId}/start",
116 "POST /v1/deployments/{deploymentId}/stop",
117 "GET /v1/deployments/{deploymentId}/logs",
118 ],
119 },
120 {
121 name: "CustomDomain",
122 resource: Prisma.CustomDomain,
123 routes: [
124 "GET /v1/apps/{appId}/domains",
125 "POST /v1/apps/{appId}/domains",
126 "GET /v1/domains/{domainId}",
127 "DELETE /v1/domains/{domainId}",
128 ],
129 },
130 {
131 name: "EnvironmentVariable",
132 resource: Prisma.EnvironmentVariable,
133 routes: [
134 "GET /v1/environment-variables",
135 "POST /v1/environment-variables",
136 "GET /v1/environment-variables/{envVarId}",
137 "PATCH /v1/environment-variables/{envVarId}",
138 "DELETE /v1/environment-variables/{envVarId}",
139 ],
140 },
141 {
142 name: "SourceRepository",
143 resource: Prisma.SourceRepository,
144 routes: [
145 "GET /v1/source-repositories",
146 "POST /v1/source-repositories",
147 "GET /v1/source-repositories/{id}",
148 "DELETE /v1/source-repositories/{id}",
149 ],
150 },
151] as const;
152
153const operationOnlyRoutes = [
154 "POST /v1/apps/{appId}/promote",
155 "POST /v1/apps/{appId}/rollback",
156 "GET /v1/builds/{buildId}/logs",
157 "POST /v1/projects/{id}/transfer",
158 "GET /v1/databases/{databaseId}/backups",
159 "POST /v1/databases/{targetDatabaseId}/restore",
160 "GET /v1/databases/{databaseId}/usage",
161 "POST /v1/connections/{id}/rotate",
162 "POST /v1/domains/{domainId}/retry",
163 "GET /v1/me",
164 "GET /v1/workspaces",
165 "GET /v1/workspaces/{id}",
166 "GET /v1/regions",
167 "GET /v1/regions/postgres",
168 "GET /v1/regions/accelerate",
169 "GET /v1/scm-installations",
170 "POST /v1/scm-installations/install-intents",
171 "GET /v1/scm-installations/{installationId}/repositories",
172 "GET /v1/integrations",
173 "GET /v1/integrations/{id}",
174 "DELETE /v1/integrations/{id}",
175 "GET /v1/workspaces/{workspaceId}/integrations",
176 "DELETE /v1/workspaces/{workspaceId}/integrations/{clientId}",
177] as const;
178
179const expectedManagementApiRoutes = [
180 ...lifecycleResources.flatMap((resource) => resource.routes),
181 ...operationOnlyRoutes,
182].sort();
183
184describe(
185 "Prisma Management API coverage",
186 { tags: ["unit", "provider:prisma", "local"] },
187 () => {
188 it(
189 "maps lifecycle route groups to Alchemy resources",
190 () => {
191 for (const { name, resource } of lifecycleResources) {
192 expect(resource.Type).toBe(`Prisma.${name}`);
193 }
194 },
195 {
196 tags: [
197 "provider:prisma:app",
198 "provider:prisma:branch",
199 "provider:prisma:bucket",
200 "provider:prisma:bucketaccesskey",
201 "provider:prisma:connection",
202 "provider:prisma:customdomain",
203 "provider:prisma:database",
204 "provider:prisma:deployment",
205 "provider:prisma:environmentvariable",
206 "provider:prisma:project",
207 "provider:prisma:sourcerepository",
208 ],
209 },
210 );
211
212 it(
213 "accounts for every route in the pinned Management API contract",
214 () => {
215 expect(managementApiContract.repository).toBe(
216 "prisma/pdp-control-plane",
217 );
218 expect(managementApiContract.commit).toMatch(/^[0-9a-f]{40}$/);
219 expect(managementApiContract.routes).toHaveLength(78);
220 expect(managementApiContract.deferredRoutes).toHaveLength(0);
221 expect(
222 managementApiContract.deferredRoutes.every((route) =>
223 managementApiContract.routes.includes(route),
224 ),
225 ).toBe(true);
226 expect(expectedManagementApiRoutes).toHaveLength(78);
227 expect(expectedManagementApiRoutes).toEqual(
228 [...productionManagementApiRoutes].sort(),
229 );
230 expect(
231 expectedManagementApiRoutes.some((route) =>
232 route.includes("/__admin"),
233 ),
234 ).toBe(false);
235 },
236 {
237 tags: [
238 "provider:prisma:app",
239 "provider:prisma:branch",
240 "provider:prisma:bucket",
241 "provider:prisma:bucketaccesskey",
242 "provider:prisma:connection",
243 "provider:prisma:customdomain",
244 "provider:prisma:database",
245 "provider:prisma:deployment",
246 "provider:prisma:environmentvariable",
247 "provider:prisma:project",
248 "provider:prisma:sourcerepository",
249 ],
250 },
251 );
252
253 it("exports the canonical app and deployment operations", () => {
254 for (const operation of canonicalComputeOperations) {
255 expect(operation in Prisma).toBe(true);
256 }
257 });
258 },
259);