1import * as Prisma from "@/Prisma";
2import * as Test from "@/Test/Alchemy";
3import { getProject, getService } from "@distilled.cloud/prisma/management";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import { MinimumLogLevel } from "effect/References";
7import * as Schedule from "effect/Schedule";
8import * as HttpClient from "effect/http/HttpClient";
9import {
10 materializeIsolatedProject,
11 removeIsolatedProject,
12} from "../IsolatedProject.ts";
13import IsolatedProjectCompute, {
14 project,
15} from "./fixtures/isolated-project-compute.ts";
16
17const { test } = Test.make({ providers: Prisma.providers() });
18
19// Same live gating as Compute.live.test.ts.
20const wantsLive = process.env.ALCHEMY_RUN_LIVE_PRISMA_TESTS === "true";
21const hasLiveCredentials =
22 !!process.env.PRISMA_SERVICE_TOKEN?.trim() ||
23 !!process.env.PRISMA_API_TOKEN?.trim() ||
24 process.env.ALCHEMY_RUN_LIVE_PRISMA_WITH_PROFILE === "true";
25const runLive = wantsLive && hasLiveCredentials;
26
27const logLevel = Effect.provideService(
28 MinimumLogLevel,
29 process.env.DEBUG ? "Debug" : "Info",
30);
31
32// Live proof that the Prisma Compute bun bootstrap boots when the app's
33// `main` lives in an isolated project (see test/IsolatedProject.ts) — the
34// bundle `cwd` resolves none of alchemy's dependencies, so the bootstrap's
35// `@effect/platform-bun` / `alchemy/*` imports must be bundled by the
36// virtual-entry plugin rather than found from the project root. With them
37// left external bun dies at module load and the app never answers.
38test.provider.skipIf(!runLive)(
39 "app bundled from an isolated project serves HTTP",
40 (stack) =>
41 Effect.gen(function* () {
42 yield* materializeIsolatedProject(project);
43 yield* stack.destroy();
44
45 try {
46 const app = yield* stack.deploy(
47 Effect.gen(function* () {
48 return yield* IsolatedProjectCompute;
49 }),
50 );
51 expect(app.url).toBeTruthy();
52
53 const health = yield* HttpClient.get(new URL("/health", app.url)).pipe(
54 Effect.flatMap((res) =>
55 res.status === 200
56 ? res.json
57 : Effect.fail(new Error(`/health returned ${res.status}`)),
58 ),
59 Effect.retry({ schedule: Schedule.spaced("2 seconds"), times: 30 }),
60 );
61 expect(health).toEqual({ ok: true });
62
63 yield* stack.destroy();
64 const projectGone = yield* getProject({ id: app.projectId }).pipe(
65 Effect.as(false),
66 Effect.catchTag("NotFound", () => Effect.succeed(true)),
67 );
68 const appGone = yield* getService({ serviceId: app.appId }).pipe(
69 Effect.as(false),
70 Effect.catchTag("NotFound", () => Effect.succeed(true)),
71 );
72 expect(projectGone).toBe(true);
73 expect(appGone).toBe(true);
74 } finally {
75 yield* stack.destroy().pipe(Effect.ignore);
76 yield* removeIsolatedProject(project);
77 }
78 }).pipe(logLevel),
79 // One Prisma Compute deploy alone can take the full 600s.
80 {
81 tags: [
82 "provider:prisma",
83 "provider:prisma:compute",
84 "provider:prisma:project",
85 "live",
86 ],
87 timeout: 1_200_000,
88 },
89);