1import { expect } from "alchemy-test";
2import * as Effect from "effect/Effect";
3import * as Schedule from "effect/Schedule";
4import * as HttpClient from "effect/http/HttpClient";
5
6const readinessSchedule = Schedule.min([
7 Schedule.exponential("500 millis"),
8 Schedule.spaced("3 seconds"),
9]);
10
11/**
12 * Drive the sqlreach probe image: DATABASE_URL is a single rewritten-or-cloud
13 * value, and a TCP connect from inside the container succeeds.
14 */
15export const expectDatabaseReachable = (
16 baseUrl: string,
17 assertHost: (hostname: string, url: URL) => void,
18) =>
19 Effect.gen(function* () {
20 const client = yield* HttpClient.HttpClient;
21 const get = (path: string) =>
22 client.get(new URL(path, baseUrl)).pipe(
23 Effect.flatMap((r) =>
24 r.status !== 200
25 ? Effect.fail(new Error(`not ready: ${r.status}`))
26 : r.text,
27 ),
28 Effect.timeout("30 seconds"),
29 Effect.retry({ schedule: readinessSchedule, times: 30 }),
30 );
31
32 const env = JSON.parse(yield* get("/env")) as {
33 DATABASE_URL: string;
34 databaseUrlCount?: number;
35 };
36 const databaseUrl = new URL(env.DATABASE_URL);
37 assertHost(databaseUrl.hostname, databaseUrl);
38 if (env.databaseUrlCount !== undefined) {
39 expect(env.databaseUrlCount).toBe(1);
40 }
41
42 const probe = JSON.parse(yield* get("/probe")) as {
43 ok?: boolean;
44 host?: string;
45 error?: string;
46 };
47 expect(probe.error).toBeUndefined();
48 expect(probe.ok).toBe(true);
49 expect(probe.host).toBe(databaseUrl.hostname);
50 });