t3-code-android-nightly/.repos/alchemy-effect/packages/alchemy/test/Prisma/Deployment.test.ts
Julius Marminge 6f9cea00ae
chore(refs): sync Effect and Alchemy references to 4.0.1 and beta.80 (#16170)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:22:30 -07:00

2286 lines
79 KiB
TypeScript

import * as Provider from "@/Provider";
import {
Deployment as PrismaDeployment,
DeploymentProvider,
MAX_DEPLOYMENT_ARTIFACT_BYTES,
readUploadArtifact,
uploadArtifact,
validateDeploymentArtifactBytes,
} from "@/Prisma/Deployment";
import * as Output from "@/Output";
import {
PrismaApiError,
PrismaClient,
type PrismaManagementClient,
} from "@/Prisma/Client";
import { executeArtifactUpload } from "@/Prisma/Internal/ArtifactUpload";
import {
PrismaHttpClientLive,
PrismaUploadClient,
} from "@/Prisma/Internal/HttpClient";
import { PlatformServices } from "@/Util/PlatformServices";
import { sha256, sha256Object } from "@/Util/sha256";
import { describe, expect, it } from "alchemy-test";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Layer from "effect/Layer";
import * as Path from "effect/Path";
import * as Redacted from "effect/Redacted";
import * as Stream from "effect/Stream";
import * as FetchHttpClient from "effect/http/FetchHttpClient";
import * as HttpBody from "effect/http/HttpBody";
import * as HttpClient from "effect/http/HttpClient";
import * as HttpClientError from "effect/http/HttpClientError";
import * as HttpClientResponse from "effect/http/HttpClientResponse";
import {
dispatchTo,
failure,
makeFakeManagementApi,
notFound,
unhandled,
} from "./fixtures/FakeManagementApi.ts";
import {
createServer as createHttpServer,
type RequestListener,
type Server as NodeHttpServer,
} from "node:http";
import { WebSocketServer } from "ws";
import { AlchemyContext } from "@/AlchemyContext";
import { Credentials } from "@/Prisma/Credentials";
import { encodeState } from "@/State/StateEncoding";
const currentClient = <T extends object>(client: T): PrismaManagementClient => {
return client as unknown as PrismaManagementClient;
};
const fixtureArtifactPath = `${import.meta.dirname}/fixtures/artifact-archive.bin`;
const sameArtifactPath = `${import.meta.dirname}/fixtures/artifact-same-version.bin`;
const liveProviderContext = Layer.succeed(AlchemyContext, {
dotAlchemy: ".alchemy-test",
dev: false,
adopt: false,
});
const deploymentProviderLive = () =>
DeploymentProvider().pipe(Layer.provide(liveProviderContext));
/**
* Serve the Management API from the same hermetic client-shaped handlers this
* suite declares, for the routes Deployment now reaches through distilled
* operations (list/create under /v1/services/{serviceId}/deployments). The handlers
* are synchronous, so the fake runs them directly: an injected
* `PrismaApiError` becomes its real status, `undefined` becomes a 404,
* everything else a `{ data }` (or `{ data, pagination }`) envelope. The
* observe/start/promote/delete paths still resolve the hand-rolled client
* (their helpers are D3), so the `PrismaClient` layer stays provided
* alongside; tests that stub uploads provide `PrismaUploadClient` because
* the ambient `HttpClient` is now this management fake.
*/
const clientBackedApi = (client: any) =>
makeFakeManagementApi((request) => {
// segments[0] is the "v1" prefix.
const [head, id, tail] = request.pathname
.split("/")
.filter((segment) => segment.length > 0)
.slice(1);
const body = request.bodyJson as any;
const query = Object.fromEntries(new URLSearchParams(request.search));
const { call, callVoid, list } = dispatchTo(request);
if (head === "services" && id !== undefined) {
if (tail === "deployments") {
return request.method === "GET"
? call(client.listAppDeployments, [id, query], list)
: call(client.createAppDeployment, [id, body]);
}
if (tail === "promote") return call(client.promoteApp, [id, body]);
if (tail === "rollback") return call(client.rollbackApp, [id, body]);
if (tail === undefined && request.method === "GET") {
return call(client.getApp, [id]);
}
}
if (head === "deployments" && id !== undefined) {
if (tail === "start") return call(client.startDeployment, [id]);
if (tail === "stop") return callVoid(client.stopDeployment, [id]);
if (request.method === "GET") return call(client.getDeployment, [id]);
if (request.method === "DELETE") {
return callVoid(client.deleteDeployment, [id]);
}
}
return unhandled(request);
});
describe(
"Prisma Deployment",
{ tags: ["unit", "provider:prisma", "provider:prisma:deployment", "local"] },
() => {
it.effect("redacts signed upload URLs from transport failures", () => {
const secret = "SIGNED_QUERY_SECRET_SENTINEL";
const http = HttpClient.make((request) =>
Effect.fail(new Error(`transport failed for ${request.url}`) as never),
);
return Effect.gen(function* () {
const error = yield* uploadArtifact(
`https://upload.prisma.test/artifact?signature=${secret}`,
new Uint8Array([1]),
"application/octet-stream",
).pipe(Effect.flip);
expect(error.message).toContain("transport failed");
expect(String(error)).not.toContain(secret);
expect(String(error)).not.toContain("upload.prisma.test");
expect(JSON.stringify(error)).not.toContain(secret);
}).pipe(Effect.provide(Layer.succeed(HttpClient.HttpClient, http)));
});
it.effect("rejects final artifacts above the upload byte limit", () =>
Effect.gen(function* () {
const error = yield* validateDeploymentArtifactBytes(
new Uint8Array(9),
8,
).pipe(Effect.flip);
expect(error.message).toContain(
"exceeds the 8 byte upload safety limit",
);
}),
);
it.effect("does not allow callers to raise the upload hard limit", () =>
Effect.gen(function* () {
const error = yield* validateDeploymentArtifactBytes(
new Uint8Array(1),
MAX_DEPLOYMENT_ARTIFACT_BYTES + 1,
).pipe(Effect.flip);
expect(error.message).toContain("hard limit");
}),
);
it.effect("rejects symbolic-link artifact paths", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-prisma-artifact-symlink-",
});
const target = path.join(root, "artifact.tar.gz");
const link = path.join(root, "artifact-link.tar.gz");
yield* fs.writeFileString(target, "archive");
yield* fs.symlink(target, link);
const error = yield* readUploadArtifact({
artifactPath: link,
output: "file",
}).pipe(Effect.flip);
expect(error.message).toContain("symbolic link");
}).pipe(Effect.provide(PlatformServices)),
);
it.effect("refuses to upload a file changed after validation", () => {
const http = HttpClient.make((request) => {
const body = request.body as HttpBody.HttpBody;
const consume =
body._tag === "Stream" ? Stream.runDrain(body.stream) : Effect.void;
return consume.pipe(
Effect.mapError(
(cause) =>
new HttpClientError.HttpClientError({
reason: new HttpClientError.TransportError({
request,
cause,
description: "test request body stream failed",
}),
}),
),
Effect.as(HttpClientResponse.fromWeb(request, new Response(null))),
);
});
return Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-prisma-artifact-mutation-",
});
const artifactPath = path.join(root, "artifact.tar.gz");
yield* fs.writeFileString(artifactPath, "first!!");
const artifact = yield* readUploadArtifact({
artifactPath,
output: "file",
});
yield* fs.writeFileString(artifactPath, "second!");
const error = yield* uploadArtifact(
"https://upload.prisma.test/artifact.tar.gz?signature=secret",
artifact!,
"application/gzip",
).pipe(Effect.flip);
expect(error.message).toContain("transport failed");
expect(String(error)).not.toContain("signature=secret");
}).pipe(
Effect.provide(Layer.succeed(HttpClient.HttpClient, http)),
Effect.provide(PlatformServices),
);
});
it.effect(
"streams file-backed artifacts with a fixed Content-Length",
() => {
let contentLength: string | undefined;
let uploadedBytes = 0;
return withHttpServer(
(request, response) => {
contentLength = request.headers["content-length"];
request.on("data", (chunk: Buffer) => {
uploadedBytes += chunk.byteLength;
});
request.on("end", () => {
response.statusCode = contentLength === undefined ? 411 : 204;
response.end();
});
},
(uploadUrl) =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-prisma-fixed-length-upload-",
});
const artifactPath = path.join(root, "artifact.tar.gz");
yield* fs.writeFileString(artifactPath, "fixed-length-archive");
const artifact = yield* readUploadArtifact({
artifactPath,
output: "file",
});
yield* executeArtifactUpload(
uploadUrl,
artifact!,
"application/gzip",
);
expect(contentLength).toBe(String(artifact!.size));
expect(uploadedBytes).toBe(artifact!.size);
}).pipe(
Effect.provide(PrismaHttpClientLive),
Effect.provide(PlatformServices),
Effect.scoped,
),
);
},
);
it.effect(
"fails when Prisma omits an upload URL for version artifacts",
() => {
const calls: Array<[string, unknown?]> = [];
const client = {
createAppDeployment: () => {
calls.push(["createAppDeployment"]);
return Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
});
},
getDeployment: (id: string) => {
calls.push(["getDeployment", id]);
return Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
createdAt: "2026-01-01T00:00:00Z",
});
},
deleteDeployment: (id: string) => {
calls.push(["deleteDeployment", id]);
return Effect.void;
},
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
artifactPath: fixtureArtifactPath,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error).toBeInstanceOf(Error);
expect(error.message).toContain("did not return an upload URL");
expect(calls).toContainEqual(["deleteDeployment", "version-1"]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("deletes created deployment when artifact upload fails", () => {
const calls: Array<[string, unknown?]> = [];
const client = {
createAppDeployment: () => {
calls.push(["createAppDeployment"]);
return Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: "https://upload.prisma.test/version.tar.gz",
});
},
getDeployment: (id: string) => {
calls.push(["getDeployment", id]);
return Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
createdAt: "2026-01-01T00:00:00Z",
});
},
deleteDeployment: (id: string) => {
calls.push(["deleteDeployment", id]);
return Effect.void;
},
} as unknown as PrismaManagementClient;
const http = HttpClient.make((request) =>
Effect.succeed(
HttpClientResponse.fromWeb(
request,
new Response("SIGNED_UPLOAD_SECRET_SENTINEL", { status: 500 }),
),
),
);
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
artifactPath: fixtureArtifactPath,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error).toBeInstanceOf(Error);
expect(error.message).toContain("artifact upload failed");
expect(error.message).toContain("HTTP 500");
expect(error.message).toContain("29 bytes");
expect(error.message).not.toContain("SIGNED_UPLOAD_SECRET_SENTINEL");
expect(calls).toContainEqual(["deleteDeployment", "version-1"]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(Layer.succeed(PrismaUploadClient, http)),
Effect.provide(PlatformServices),
);
});
it.effect(
"preserves reconcile and cleanup failures for an orphaned deployment",
() => {
const startError = new PrismaApiError({
method: "POST",
path: "/v1/deployments/version-1/start",
status: 400,
message: "start failed",
});
const deleteError = new PrismaApiError({
method: "DELETE",
path: "/v1/deployments/version-1",
status: 400,
message: "cleanup failed",
});
const client = {
createAppDeployment: () =>
Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
}),
getDeployment: (id: string) =>
Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
createdAt: "2026-01-01T00:00:00Z",
}),
startDeployment: () => Effect.fail(startError),
deleteDeployment: () => Effect.fail(deleteError),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
skipCodeUpload: true,
start: true,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error).toBeInstanceOf(AggregateError);
expect((error as AggregateError).message).toContain("version-1");
expect((error as AggregateError).message).toContain(
"DELETE /v1/deployments/version-1",
);
expect((error as AggregateError).errors).toHaveLength(2);
// Over the wire the injected failures decode into the typed errors.
expect(
((error as AggregateError).errors[0] as Error).message,
).toContain("start failed");
expect(
((error as AggregateError).errors[1] as Error).message,
).toContain("cleanup failed");
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("deletes created deployment when start fails", () => {
const calls: Array<[string, unknown?]> = [];
const client = {
createAppDeployment: () => {
calls.push(["createAppDeployment"]);
return Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
});
},
getDeployment: (id: string) => {
calls.push(["getDeployment", id]);
return Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
createdAt: "2026-01-01T00:00:00Z",
});
},
startDeployment: (id: string) => {
calls.push(["startDeployment", id]);
return Effect.fail(
new PrismaApiError({
method: "POST",
path: `/v1/deployments/${id}/start`,
status: 400,
message: "start failed",
}),
);
},
deleteDeployment: (id: string) => {
calls.push(["deleteDeployment", id]);
return Effect.void;
},
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
skipCodeUpload: true,
start: true,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
// Over the wire the injected failure decodes into the typed error.
expect(error._tag).toBe("BadRequest");
expect(error.message).toBe("start failed");
expect(calls).toContainEqual(["deleteDeployment", "version-1"]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"preserves a deployment when promotion commit state is ambiguous",
() => {
const calls: Array<[string, unknown?]> = [];
let status = "new";
const client = {
createAppDeployment: () => {
calls.push(["createAppDeployment"]);
return Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
});
},
getDeployment: (id: string) => {
calls.push(["getDeployment", id]);
return Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status,
previewDomain: "version-1.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
});
},
listAppDeployments: () =>
Effect.succeed([
{
id: "version-1",
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:00Z",
},
]),
startDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["startDeployment", id]);
status = "running";
return { previewDomain: "version-1.preview.prisma.build" };
}),
getApp: (id: string) => {
calls.push(["getApp", id]);
return Effect.succeed({
id,
type: "app" as const,
url: `https://api.prisma.test/v1/services/${id}`,
name: "api",
region: { id: "us-east-1", name: "US East" },
projectId: "project-1",
branchId: null,
latestDeploymentId: null,
appEndpointDomain: "api.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
});
},
promoteApp: (
appId: string,
{ deploymentId }: { deploymentId: string },
) => {
calls.push(["promoteApp", { appId, deploymentId }]);
return Effect.fail(
new PrismaApiError({
method: "POST",
path: `/v1/services/${appId}/promote`,
status: 400,
message: "promote failed",
}),
);
},
rollbackApp: () =>
Effect.fail(
new PrismaApiError({
method: "POST",
path: "/v1/services/service-1/rollback",
status: 400,
message: "promotion recovery failed",
}),
),
stopDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["stopDeployment", id]);
status = "stopped";
}),
deleteDeployment: (id: string) => {
calls.push(["deleteDeployment", id]);
return Effect.void;
},
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
skipCodeUpload: true,
promote: true,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error).toBeInstanceOf(AggregateError);
expect((error as AggregateError).message).toContain("commit state");
expect(calls).not.toContainEqual(["deleteDeployment", "version-1"]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("uploads version artifact bytes from artifactPath", () => {
let uploaded:
| { url: string; contentType: string | undefined; bytes: Uint8Array }
| undefined;
const client = {
createAppDeployment: (_appId: string, input: unknown) =>
Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: "https://upload.prisma.test/version.tar.gz",
input,
}),
getDeployment: (id: string) =>
Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: "version-1.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
}),
} as unknown as PrismaManagementClient;
const http = HttpClient.make((request) =>
Effect.gen(function* () {
const body = request.body as HttpBody.HttpBody;
const bytes =
body._tag === "Uint8Array"
? body.body
: body._tag === "Stream"
? yield* Stream.runCollect(body.stream).pipe(
Effect.orDie,
Effect.map((chunks) => {
const output = new Uint8Array(
chunks.reduce(
(total, chunk) => total + chunk.byteLength,
0,
),
);
let offset = 0;
for (const chunk of chunks) {
output.set(chunk, offset);
offset += chunk.byteLength;
}
return output;
}),
)
: new Uint8Array();
uploaded = {
url: request.url,
contentType:
body._tag === "Uint8Array" || body._tag === "Stream"
? body.contentType
: undefined,
bytes,
};
return HttpClientResponse.fromWeb(request, new Response(null));
}),
);
return Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-prisma-version-artifact-",
});
const artifactPath = path.join(root, "version.tar.gz");
yield* fs.writeFileString(artifactPath, "version-archive");
const provider = yield* PrismaDeployment.Provider;
const output = yield* provider.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
artifactPath,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
});
expect(output.deploymentId).toBe("version-1");
expect(output.artifactHash).toBeDefined();
expect(uploaded?.url).toBe("https://upload.prisma.test/version.tar.gz");
expect(uploaded?.contentType).toBe("application/octet-stream");
expect(new TextDecoder().decode(uploaded?.bytes)).toBe(
"version-archive",
);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(Layer.succeed(PrismaUploadClient, http)),
Effect.provide(PlatformServices),
);
});
it.effect("reads a saved deployment through the canonical route", () => {
const calls: Array<[string, unknown?]> = [];
const client = {
listAppDeployments: (appId: string) =>
Effect.sync(() => {
calls.push(["listAppDeployments", appId]);
return [
{
id: "version-1",
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:00Z",
},
];
}),
getDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["getDeployment", id]);
return {
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "stopped",
previewDomain: "version-1.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
};
}),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* Provider.findProvider(PrismaDeployment);
const output = yield* provider.read!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: {
app: "service-1",
},
output: {
deploymentId: "version-1",
appId: "service-1",
foundryVersionId: "foundry-1",
status: "running",
previewDomain: undefined,
artifactHash: undefined,
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
});
expect(output?.deploymentId).toBe("version-1");
expect(output?.status).toBe("stopped");
expect(calls).toEqual([
["getDeployment", "version-1"],
["listAppDeployments", "service-1"],
]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"does not adopt an unrelated latest deployment on a cold read",
() => {
const client = {
listAppDeployments: () =>
Effect.die("cold reads must not enumerate or adopt deployments"),
getDeployment: () =>
Effect.die("cold reads have no persisted deployment identity"),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* Provider.findProvider(PrismaDeployment);
const output = yield* provider.read!({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
olds: { app: "app-1" },
output: undefined,
});
expect(output).toBeUndefined();
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, client)),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("uses the output App ID when reading a saved deployment", () => {
const calls: Array<[string, unknown?]> = [];
const client = {
listAppDeployments: (appId: string) =>
Effect.sync(() => {
calls.push(["listAppDeployments", appId]);
return [
{
id: "version-1",
type: "deployment" as const,
serviceId: "service-from-output",
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:00Z",
},
];
}),
getDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["getDeployment", id]);
return {
id,
type: "deployment" as const,
serviceId: "service-from-output",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "running",
previewDomain: "version-1.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
};
}),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* Provider.findProvider(PrismaDeployment);
const output = yield* provider.read!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: {
app: "service-from-olds",
},
output: {
deploymentId: "version-1",
appId: "service-from-output",
foundryVersionId: "foundry-1",
status: "stopped",
previewDomain: undefined,
artifactHash: undefined,
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
});
expect(output?.appId).toBe("service-from-output");
expect(output?.status).toBe("running");
expect(calls).toEqual([
["getDeployment", "version-1"],
["listAppDeployments", "service-from-output"],
]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"falls back to the foundry version ID when the saved deployment is gone",
() => {
const calls: Array<[string, unknown?]> = [];
const notFound = (path: string) =>
new PrismaApiError({
method: "GET",
path,
status: 404,
message: "not found",
});
const client = {
listAppDeployments: (appId: string, query: unknown) =>
Effect.sync(() => {
calls.push(["listAppDeployments", { appId, query }]);
return [
{
id: "version-new",
type: "deployment" as const,
serviceId: "service-from-output",
url: "https://api.prisma.test/v1/deployments/version-new",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:00Z",
},
];
}),
getDeployment: (id: string) =>
Effect.gen(function* () {
calls.push(["getDeployment", id]);
if (id === "version-old") {
return yield* Effect.fail(notFound(`/v1/deployments/${id}`));
}
return {
id,
type: "deployment" as const,
serviceId: "service-from-output",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status: "running",
previewDomain: "version-new.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
};
}),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* Provider.findProvider(PrismaDeployment);
const output = yield* provider.read!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: {
app: "service-from-olds",
},
output: {
deploymentId: "version-old",
appId: "service-from-output",
foundryVersionId: "foundry-1",
status: "stopped",
previewDomain: undefined,
artifactHash: undefined,
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
});
expect(output?.deploymentId).toBe("version-new");
expect(output?.appId).toBe("service-from-output");
expect(calls).toEqual([
["getDeployment", "version-old"],
[
"listAppDeployments",
// Over the wire, query params arrive as strings.
{ appId: "service-from-output", query: { limit: "100" } },
],
["getDeployment", "version-new"],
]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("rejects ambiguous Foundry-version recovery matches", () => {
const client = {
getDeployment: (id: string) =>
Effect.fail(
new PrismaApiError({
method: "GET",
path: `/v1/deployments/${id}`,
status: 404,
message: "not found",
}),
),
listAppDeployments: () =>
Effect.succeed([
{
id: "deployment-a",
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/deployment-a",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:00Z",
},
{
id: "deployment-b",
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/deployment-b",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:01Z",
},
]),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* Provider.findProvider(PrismaDeployment);
const error = yield* provider.read!({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
olds: { app: "app-1" },
output: {
deploymentId: "deployment-missing",
appId: "app-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
appEndpointDomain: undefined,
createdAt: undefined,
},
}).pipe(Effect.flip);
expect(error.message).toContain("ambiguous recovery match");
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("replaces when artifactPath contents change", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-prisma-version-diff-",
});
const artifactPath = path.join(root, "version.tar.gz");
yield* fs.writeFileString(artifactPath, "new-version");
const provider = yield* PrismaDeployment.Provider;
const diff = yield* provider.diff!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: {
app: "service-1",
artifactPath,
},
news: {
app: "service-1",
artifactPath,
},
oldBindings: [],
newBindings: [],
output: {
deploymentId: "version-1",
appId: "service-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
artifactHash: "old-hash",
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
} as never);
expect(diff).toEqual({ action: "replace" });
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("replaces changed artifacts even when app is unresolved", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const root = yield* fs.makeTempDirectory({
prefix: "alchemy-prisma-version-unresolved-diff-",
});
const artifactPath = path.join(root, "version.tar.gz");
yield* fs.writeFileString(artifactPath, "new-version");
const provider = yield* PrismaDeployment.Provider;
const diff = yield* provider.diff!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: {
app: "service-1",
artifactPath,
},
news: {
app: Output.asOutput("service-1"),
artifactPath,
},
oldBindings: [],
newBindings: [],
output: {
deploymentId: "version-1",
appId: "service-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
artifactHash: "old-hash",
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
} as never);
expect(diff).toEqual({ action: "replace" });
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"replaces when the persisted App changes from an unresolved old prop",
() => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const artifact = new TextEncoder().encode("same-version\n");
const artifactHash = yield* sha256Object({
artifact: yield* sha256(artifact),
contentType: "application/octet-stream",
});
const provider = yield* PrismaDeployment.Provider;
const diff = yield* provider.diff!({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
olds: {
app: Output.asOutput("app-old"),
artifactPath: sameArtifactPath,
},
news: {
app: "app-new",
artifactPath: sameArtifactPath,
},
oldBindings: [],
newBindings: [],
output: {
deploymentId: "deployment-1",
appId: "app-old",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
artifactHash,
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
} as never);
expect(diff).toEqual({ action: "replace" });
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("does not replace when artifact bytes are unchanged", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const artifact = new TextEncoder().encode("same-version\n");
const artifactHash = yield* sha256Object({
artifact: yield* sha256(artifact),
contentType: "application/octet-stream",
});
const provider = yield* PrismaDeployment.Provider;
const diff = yield* provider.diff!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: {
app: "service-1",
artifactPath: sameArtifactPath,
},
news: {
app: "service-1",
artifactPath: sameArtifactPath,
},
oldBindings: [],
newBindings: [],
output: {
deploymentId: "version-1",
appId: "service-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
artifactHash,
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
} as never);
expect(diff).toBeUndefined();
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("replays promotion to repair Compute endpoint drift", () => {
const calls: Array<[string, unknown?]> = [];
let latestDeploymentId: string | null = null;
let status = "new";
const service = () => ({
id: "service-1",
type: "app" as const,
url: "https://api.prisma.test/v1/services/service-1",
name: "api",
region: { id: "us-east-1", name: "US East" },
projectId: "project-1",
branchId: null,
latestDeploymentId,
appEndpointDomain: "api.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
});
const client = {
createAppDeployment: (appId: string, input: unknown) => {
calls.push(["createAppDeployment", { appId, input }]);
return Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
});
},
getDeployment: (id: string) => {
calls.push(["getDeployment", id]);
return Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status,
previewDomain: "version-1.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
});
},
listAppDeployments: () =>
Effect.succeed([
{
id: "version-1",
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
createdAt: "2026-01-01T00:00:00Z",
},
]),
startDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["startDeployment", id]);
status = "running";
return { previewDomain: "version-1.preview.prisma.build" };
}),
getApp: (id: string) => {
calls.push(["getApp", id]);
return Effect.succeed(service());
},
promoteApp: (
appId: string,
{ deploymentId }: { deploymentId: string },
) =>
Effect.sync(() => {
calls.push(["promoteApp", { appId, deploymentId }]);
latestDeploymentId = deploymentId;
return { appEndpointDomain: "api.prisma.build" };
}),
} as unknown as PrismaManagementClient;
const news = {
app: "service-1",
skipCodeUpload: true,
promote: true,
};
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const first = yield* provider.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news,
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
});
// Simulate an out-of-band stop between deployments. Reconcile must
// restore both running state and App routing even though props match.
status = "stopped";
const second = yield* provider.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news,
olds: news,
output: first,
session: undefined as never,
bindings: [],
});
expect(first.deploymentId).toBe("version-1");
expect(second.deploymentId).toBe("version-1");
expect(second.appEndpointDomain).toBe("api.prisma.build");
expect(calls.filter(([name]) => name === "startDeployment")).toEqual([
["startDeployment", "version-1"],
["startDeployment", "version-1"],
]);
expect(calls.filter(([name]) => name === "promoteApp")).toEqual([
["promoteApp", { appId: "service-1", deploymentId: "version-1" }],
["promoteApp", { appId: "service-1", deploymentId: "version-1" }],
]);
expect(
calls.filter(([name]) => name === "createAppDeployment"),
).toHaveLength(1);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"replaces a terminal failed deployment before deleting the failed generation",
() => {
const calls: Array<[string, unknown?]> = [];
let latestDeploymentId: string | null = "version-failed";
const deployments = new Map<
string,
{
id: string;
type: "deployment";
serviceId: string;
url: string;
foundryVersionId: string;
status: string;
previewDomain: string | null;
createdAt: string;
}
>([
[
"version-failed",
{
id: "version-failed",
type: "deployment",
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/version-failed",
foundryVersionId: "foundry-failed",
status: "failed",
previewDomain: "version-failed.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
},
],
]);
const client = {
createAppDeployment: (appId: string, input: unknown) =>
Effect.sync(() => {
calls.push(["createAppDeployment", { appId, input }]);
deployments.set("version-replacement", {
id: "version-replacement",
type: "deployment",
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/version-replacement",
foundryVersionId: "foundry-replacement",
status: "new",
previewDomain: "version-replacement.preview.prisma.build",
createdAt: "2026-01-02T00:00:00Z",
});
return {
id: "version-replacement",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-replacement",
foundryVersionId: "foundry-replacement",
uploadUrl: null,
};
}),
getDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["getDeployment", id]);
return deployments.get(id)!;
}),
listAppDeployments: () =>
Effect.succeed(
Array.from(deployments.values()).map(
({
id,
type,
serviceId,
url,
foundryVersionId,
createdAt,
}) => ({
id,
type,
serviceId,
url,
foundryVersionId,
createdAt,
}),
),
),
startDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["startDeployment", id]);
deployments.get(id)!.status = "running";
return {
previewDomain: deployments.get(id)!.previewDomain!,
};
}),
promoteApp: (
appId: string,
{ deploymentId }: { deploymentId: string },
) =>
Effect.sync(() => {
calls.push(["promoteApp", { appId, deploymentId }]);
expect(deployments.has("version-failed")).toBe(true);
latestDeploymentId = deploymentId;
return {
appEndpointDomain: "api.prisma.build",
reassignedDomains: 0,
};
}),
getApp: (appId: string) =>
Effect.succeed({
id: appId,
type: "app" as const,
url: `https://api.prisma.test/v1/services/${appId}`,
name: "api",
region: { id: "us-east-1", name: "US East" },
projectId: "project-1",
branchId: "branch-main",
latestDeploymentId,
appEndpointDomain: "api.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
}),
deleteDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["deleteDeployment", id]);
deployments.delete(id);
}),
} as unknown as PrismaManagementClient;
const news = {
app: "service-1",
skipCodeUpload: true,
promote: true,
} as const;
const failedOutput: PrismaDeployment["Attributes"] = {
deploymentId: "version-failed",
appId: "service-1",
foundryVersionId: "foundry-failed",
status: "failed",
previewDomain: "version-failed.preview.prisma.build",
artifactHash: undefined,
appEndpointDomain: "api.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
};
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const action = yield* provider.diff!({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: news,
news,
oldBindings: [],
newBindings: [],
output: failedOutput,
} as never);
expect(action).toEqual({ action: "replace" });
// The engine executes replacement create-before-delete. Reconcile the
// replacement to its requested running/promoted state before invoking
// delete for the failed generation.
const replacement = yield* provider.reconcile({
id: "Version",
fqn: "Version",
instanceId: "11111111111111111111111111111111",
news,
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
});
expect(replacement.deploymentId).toBe("version-replacement");
expect(replacement.status).toBe("running");
expect(deployments.has("version-failed")).toBe(true);
yield* provider.delete({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
olds: news,
output: failedOutput,
session: undefined as never,
bindings: [],
});
const createIndex = calls.findIndex(
([operation]) => operation === "createAppDeployment",
);
const promoteIndex = calls.findIndex(
([operation]) => operation === "promoteApp",
);
const deleteIndex = calls.findIndex(
([operation, id]) =>
operation === "deleteDeployment" && id === "version-failed",
);
expect(createIndex).toBeGreaterThanOrEqual(0);
expect(createIndex).toBeLessThan(promoteIndex);
expect(promoteIndex).toBeLessThan(deleteIndex);
expect(deployments.has("version-failed")).toBe(false);
expect(deployments.get("version-replacement")?.status).toBe(
"running",
);
expect(calls).not.toContainEqual([
"startDeployment",
"version-failed",
]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("rejects promotion when start is explicitly disabled", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
skipCodeUpload: true,
start: false,
promote: true,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error).toBeInstanceOf(Error);
expect(error.message).toContain(
"promote cannot be combined with start: false",
);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("rejects invalid deployment HTTP ports", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
for (const http of [0, 65_536, 1.5]) {
const error = yield* provider
.reconcile({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
news: {
app: "app-1",
skipCodeUpload: true,
portMapping: { http },
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error.message).toContain(
"portMapping.http must be an integer between 1 and 65535",
);
}
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"allows null HTTP port to request the Foundry 8080 default",
() => {
const calls: Array<[string, unknown?]> = [];
const client = {
createAppDeployment: (appId: string, input: unknown) =>
Effect.sync(() => {
calls.push(["createAppDeployment", { appId, input }]);
return {
id: "deployment-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/deployment-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
};
}),
getDeployment: () =>
Effect.succeed({
id: "deployment-1",
type: "deployment" as const,
serviceId: "service-1",
url: "https://api.prisma.test/v1/deployments/deployment-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
createdAt: "2026-01-01T00:00:00Z",
}),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const output = yield* provider.reconcile({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
news: {
app: "app-1",
skipCodeUpload: true,
portMapping: { http: null },
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
});
expect(output.deploymentId).toBe("deployment-1");
expect(calls).toEqual([
[
"createAppDeployment",
{
appId: "app-1",
input: { portMapping: { http: null }, skipCodeUpload: true },
},
],
]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("forces reconcile while start or promotion is asserted", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const base = {
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
oldBindings: [],
newBindings: [],
output: {
deploymentId: "deployment-1",
appId: "app-1",
foundryVersionId: "foundry-1",
status: "running",
previewDomain: null,
appEndpointDomain: undefined,
createdAt: undefined,
},
};
const start = yield* provider.diff!({
...base,
olds: { app: "app-1", skipCodeUpload: true, start: true },
news: { app: "app-1", skipCodeUpload: true, start: true },
} as never);
const promote = yield* provider.diff!({
...base,
olds: { app: "app-1", skipCodeUpload: true, promote: true },
news: { app: "app-1", skipCodeUpload: true, promote: true },
} as never);
expect(start).toEqual({ action: "update" });
expect(promote).toEqual({ action: "update" });
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("rejects a deployment with no artifact source", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const error = yield* provider
.reconcile({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
news: { app: "app-1" },
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
})
.pipe(Effect.flip);
expect(error.message).toContain(
"requires artifactPath or skipCodeUpload: true",
);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("starts a deployment through the canonical route", () => {
const calls: Array<[string, unknown?]> = [];
let status = "new";
const client = {
createAppDeployment: (appId: string, input: unknown) => {
calls.push(["createAppDeployment", { appId, input }]);
return Effect.succeed({
id: "version-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/version-1",
foundryVersionId: "foundry-1",
uploadUrl: null,
});
},
getDeployment: (id: string) => {
calls.push(["getDeployment", id]);
return Effect.succeed({
id,
type: "deployment" as const,
serviceId: "service-1",
url: `https://api.prisma.test/v1/deployments/${id}`,
foundryVersionId: "foundry-1",
status,
previewDomain: "version-1.preview.prisma.build",
createdAt: "2026-01-01T00:00:00Z",
});
},
startDeployment: (id: string) =>
Effect.sync(() => {
calls.push(["startDeployment", id]);
status = "running";
return { previewDomain: "version-1.preview.prisma.build" };
}),
} as unknown as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const output = yield* provider.reconcile({
id: "Version",
fqn: "Version",
instanceId: "00000000000000000000000000000000",
news: {
app: "service-1",
skipCodeUpload: true,
start: true,
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
});
expect(output.status).toBe("running");
expect(calls).toEqual([
[
"createAppDeployment",
{
appId: "service-1",
input: { portMapping: undefined, skipCodeUpload: true },
},
],
["getDeployment", "version-1"],
["startDeployment", "version-1"],
["getDeployment", "version-1"],
]);
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect("tails Deployment logs through the provider", () =>
withWebSocketServer((server) =>
Effect.gen(function* () {
const url = yield* listenUrl(server);
let authorization: string | undefined;
let requestUrl: string | undefined;
server.on("connection", (socket, request) => {
authorization = request.headers.authorization;
requestUrl = request.url;
socket.send(
JSON.stringify({
type: "log",
text: "direct version log",
byteStart: 0,
byteEnd: 18,
}),
);
socket.send(
JSON.stringify({
type: "terminal",
kind: "end",
code: "vm_stopped",
message: "done",
retryable: false,
cursor: null,
}),
);
});
const provider = yield* Provider.findProvider(PrismaDeployment).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(makeFakeManagementApi(unhandled).layer),
Effect.provide(PlatformServices),
);
const lines = yield* provider.tail!({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
props: {
app: "service-1",
},
output: {
deploymentId: "version-1",
appId: "service-1",
foundryVersionId: "foundry-1",
status: "running",
previewDomain: "version-1.preview.prisma.build",
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
}).pipe(
// The carved-out logs client resolves the distilled Credentials
// service directly: the test WebSocket server is the API base.
Stream.provideService(
Credentials,
Effect.succeed({
apiToken: Redacted.make("version-token"),
apiBaseUrl: url,
}),
),
Stream.runCollect,
);
expect(lines.map((line) => line.message)).toEqual([
"direct version log",
]);
expect(authorization).toBe("Bearer version-token");
expect(requestUrl).toBe("/v1/deployments/version-1/logs");
}).pipe(Effect.provide(FetchHttpClient.layer)),
),
);
it.effect(
"records a redacted triggers fingerprint and replaces when a value changes",
() => {
const secret = "REDEPLOY_SECRET_SENTINEL";
const client = redeployClient();
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const output = yield* provider.reconcile({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Redacted.make(secret) },
},
olds: undefined,
output: undefined,
session: undefined as never,
bindings: [],
});
expect(Redacted.isRedacted(output.triggersHash!)).toBe(true);
const fingerprint = Redacted.value(output.triggersHash!);
expect(fingerprint).not.toBe(secret);
expect(fingerprint).not.toContain(secret);
// encodeState unwraps Redacted, so this is what a state store writes.
expect(JSON.stringify(encodeState(output))).not.toContain(secret);
const unchanged = yield* provider.diff!({
...triggersDiffBase(output.triggersHash),
olds: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Redacted.make(secret) },
},
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Redacted.make(secret) },
},
} as never);
const changed = yield* provider.diff!({
...triggersDiffBase(output.triggersHash),
olds: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Redacted.make(secret) },
},
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Redacted.make(`${secret}-rotated`) },
},
} as never);
expect(unchanged).toBeUndefined();
expect(changed).toEqual({ action: "replace" });
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect("replaces when a plain triggers value changes", () => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const diff = yield* provider.diff!({
...triggersDiffBase(Redacted.make("recorded-fingerprint")),
olds: {
app: "app-1",
skipCodeUpload: true,
triggers: { FEATURE_FLAG: "off" },
},
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { FEATURE_FLAG: "on" },
},
} as never);
expect(diff).toEqual({ action: "replace" });
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
});
it.effect(
"keeps a deployment recorded before triggers existed when nothing changed",
() => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const diff = yield* provider.diff!({
...triggersDiffBase(undefined),
olds: {
app: "app-1",
skipCodeUpload: true,
triggers: { FEATURE_FLAG: "on" },
},
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { FEATURE_FLAG: "on" },
},
} as never);
expect(diff).toBeUndefined();
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
it.effect(
"replaces when triggers cannot be resolved while planning",
() => {
const client = {} as PrismaManagementClient;
return Effect.gen(function* () {
const provider = yield* PrismaDeployment.Provider;
const recorded = yield* provider.diff!({
...triggersDiffBase(Redacted.make("recorded-fingerprint")),
olds: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: "postgres://old" },
},
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Output.asOutput("postgres://new") },
},
} as never);
const neverRecorded = yield* provider.diff!({
...triggersDiffBase(undefined),
olds: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: "postgres://old" },
},
news: {
app: "app-1",
skipCodeUpload: true,
triggers: { DATABASE_URL: Output.asOutput("postgres://new") },
},
} as never);
expect(recorded).toEqual({ action: "replace" });
expect(neverRecorded).toBeUndefined();
}).pipe(
Effect.provide(deploymentProviderLive()),
Effect.provide(Layer.succeed(PrismaClient, currentClient(client))),
Effect.provide(clientBackedApi(client).layer),
Effect.provide(PlatformServices),
);
},
);
},
);
const redeployClient = () =>
({
createAppDeployment: () =>
Effect.succeed({
id: "deployment-1",
type: "deployment" as const,
url: "https://api.prisma.test/v1/deployments/deployment-1",
foundryVersionId: "foundry-1",
}),
getDeployment: (id: string) =>
Effect.succeed({
id,
type: "deployment" as const,
serviceId: "app-1",
url: "https://api.prisma.test/v1/deployments/deployment-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
createdAt: "2026-01-01T00:00:00Z",
}),
}) as unknown as PrismaManagementClient;
const triggersDiffBase = (
triggersHash: Redacted.Redacted<string> | undefined,
) => ({
id: "Deployment",
fqn: "Deployment",
instanceId: "00000000000000000000000000000000",
oldBindings: [],
newBindings: [],
output: {
deploymentId: "deployment-1",
appId: "app-1",
foundryVersionId: "foundry-1",
status: "new",
previewDomain: null,
artifactHash: undefined,
triggersHash,
appEndpointDomain: undefined,
createdAt: "2026-01-01T00:00:00Z",
},
});
const withWebSocketServer = <A, E, R>(
f: (server: WebSocketServer) => Effect.Effect<A, E, R>,
) =>
Effect.acquireUseRelease(
Effect.sync(() => new WebSocketServer({ host: "127.0.0.1", port: 0 })),
f,
(server) =>
Effect.callback<void>((resume) => {
server.close(() => resume(Effect.void));
}).pipe(Effect.ignore),
);
const listenUrl = (server: WebSocketServer) =>
Effect.callback<string, Error>((resume) => {
const complete = () => {
cleanup();
const address = server.address();
if (address && typeof address === "object") {
resume(Effect.succeed(`http://127.0.0.1:${address.port}`));
} else {
resume(Effect.fail(new Error("WebSocket server has no TCP address")));
}
};
const fail = (cause: unknown) => {
cleanup();
resume(
Effect.fail(cause instanceof Error ? cause : new Error(String(cause))),
);
};
const cleanup = () => {
server.off("listening", complete);
server.off("error", fail);
};
if (server.address()) {
complete();
return;
}
server.once("listening", complete);
server.once("error", fail);
return Effect.sync(cleanup);
});
const withHttpServer = <A, E, R>(
handler: RequestListener,
f: (url: string) => Effect.Effect<A, E, R>,
) =>
Effect.acquireUseRelease(
Effect.sync(() => createHttpServer(handler)),
(server) => Effect.flatMap(listenHttpServerUrl(server), f),
(server) =>
Effect.callback<void>((resume) => {
server.close(() => resume(Effect.void));
}).pipe(Effect.ignore),
);
const listenHttpServerUrl = (server: NodeHttpServer) =>
Effect.callback<string, Error>((resume) => {
const complete = () => {
cleanup();
const address = server.address();
if (address && typeof address === "object") {
resume(Effect.succeed(`http://127.0.0.1:${address.port}`));
} else {
resume(Effect.fail(new Error("HTTP server has no TCP address")));
}
};
const fail = (cause: unknown) => {
cleanup();
resume(
Effect.fail(cause instanceof Error ? cause : new Error(String(cause))),
);
};
const cleanup = () => {
server.off("listening", complete);
server.off("error", fail);
};
server.once("listening", complete);
server.once("error", fail);
server.listen(0, "127.0.0.1");
return Effect.sync(cleanup);
});