1import { providers } from "@/Neon/Providers.ts";
2import * as Test from "@/Test/Alchemy.ts";
3import { describe, expect } from "alchemy-test";
4import * as Effect from "effect/Effect";
5import * as HttpClient from "effect/http/HttpClient";
6import * as FetchHttpClient from "effect/http/FetchHttpClient";
7import { bodyContaining, exampleRoot } from "./Fixture.ts";
8import { frameworks } from "./Frameworks.ts";
9import { browserRoundtrip } from "./Browser.ts";
10
11const { test } = Test.make({ providers: providers(), dev: true });
12
13describe.sequential(
14 "Neon Website native frameworks",
15 { tags: ["provider:neon", "provider:neon:website", "local"] },
16 () => {
17 for (const { slug, name, website } of frameworks) {
18 // Each dev server spawns 1-3 GiB toolchain children; the matrix cannot
19 // share a 4 GiB budget with a long-lived suite process.
20 test.provider.skipIf(!!process.env.FAST)(
21 slug,
22 (stack) =>
23 Effect.gen(function* () {
24 yield* stack.destroy();
25 const rootDir = yield* exampleRoot(slug);
26 const site = yield* stack.deploy(
27 Effect.gen(function* () {
28 return yield* website("Web", {
29 rootDir,
30 env: { GREETING: `Hello from ${name} on Neon!` },
31 });
32 }),
33 );
34 expect(site.url).toMatch(/^http:\/\/(localhost|127\.0\.0\.1):\d+/);
35 expect(site.function).toBeUndefined();
36 expect(site.project).toBeUndefined();
37 expect(site.branch).toBeUndefined();
38 expect(site.domain).toBeUndefined();
39 yield* bodyContaining(`${site.url}/`, name);
40 const asset = yield* HttpClient.get(`${site.url}/example.json`);
41 expect(asset.status).toBe(200);
42 expect(yield* asset.json).toHaveProperty("framework");
43 if (slug === "nextjs") {
44 const stream = yield* HttpClient.get(`${site.url}/api/stream`);
45 expect(yield* stream.text).toBe(
46 "data: first\n\ndata: second\n\n",
47 );
48 const image = yield* HttpClient.get(`${site.url}/logo.svg`);
49 expect(image.headers["content-type"]).toContain("image/svg+xml");
50 const redirect = yield* HttpClient.get(
51 `${site.url}/redirect`,
52 ).pipe(
53 Effect.provideService(FetchHttpClient.RequestInit, {
54 redirect: "manual",
55 }),
56 );
57 expect(redirect.status).toBe(307);
58 const location = new URL(redirect.headers.location!);
59 expect(["localhost", "127.0.0.1"]).toContain(location.hostname);
60 expect(location.port).toBe(new URL(String(site.url)).port);
61 expect(location.pathname + location.search).toBe(
62 "/?redirected=yes",
63 );
64 expect(
65 (yield* HttpClient.get(`${site.url}/not-a-real-page`)).status,
66 ).toBe(404);
67 }
68 yield* browserRoundtrip(String(site.url), slug);
69 yield* stack.destroy();
70 }),
71 { timeout: 120_000 },
72 );
73 }
74 },
75);