1import * as AWS from "@/AWS";
2import { expect } from "alchemy-test";
3import * as Effect from "effect/Effect";
4import * as Schedule from "effect/Schedule";
5import * as HttpClient from "effect/http/HttpClient";
6import {
7 materializeIsolatedProject,
8 removeIsolatedProject,
9} from "../../IsolatedProject.ts";
10import IsolatedProjectInstance, {
11 project,
12} from "./fixtures/isolated-project-instance.ts";
13import { assertInstanceTerminated } from "./Gone.ts";
14import * as Test from "./VpcTest.ts";
15
16const { test } = Test.make({ providers: AWS.providers() });
17
18// Live proof that the EC2 hosted-program bootstrap boots when `main` lives
19// in an isolated project (see test/IsolatedProject.ts) — the bundle `cwd`
20// resolves none of alchemy's dependencies, so the bootstrap's
21// `@distilled.cloud/aws/*` / `@effect/platform-bun` imports must be bundled
22// by the virtual-entry plugin rather than found from the project root. With
23// them left external bun dies at module load under systemd and the app port
24// never answers.
25//
26// Heavy (instance boot + bun install + S3 sync + systemd), so gated behind
27// AWS_TEST_SLOW=1 alongside the other isolated-project e2e tests.
28test.provider.skipIf(!process.env.AWS_TEST_SLOW || !!process.env.FAST)(
29 "instance program bundled from an isolated project serves HTTP",
30 (stack) =>
31 Effect.gen(function* () {
32 yield* materializeIsolatedProject(project);
33 yield* stack.destroy();
34
35 try {
36 const { instanceId, publicIpAddress } = yield* stack.deploy(
37 Effect.gen(function* () {
38 const instance = yield* IsolatedProjectInstance;
39 return {
40 instanceId: instance.instanceId,
41 publicIpAddress: instance.publicIpAddress,
42 };
43 }),
44 );
45 expect(publicIpAddress).toBeTruthy();
46
47 // Poll until the instance boots, installs bun, syncs the bundle from
48 // S3, and the systemd unit serves 200 on :3000. Connection errors
49 // before the server binds are normalised to "not ready".
50 const base = `http://${publicIpAddress}:3000`;
51 const served = yield* HttpClient.get(`${base}/health`).pipe(
52 Effect.map((res) => res.status === 200),
53 Effect.catch(() => Effect.succeed(false)),
54 Effect.repeat({
55 schedule: Schedule.spaced("8 seconds"),
56 until: (ok) => ok,
57 times: 75,
58 }),
59 );
60 expect(served).toBe(true);
61
62 yield* stack.destroy();
63 yield* assertInstanceTerminated(instanceId);
64 } finally {
65 yield* removeIsolatedProject(project);
66 }
67 }),
68 { tags: ["provider:aws", "provider:aws:ec2", "live"], timeout: 1_200_000 },
69);