1import * as Alchemy from "@/index";
2import { Function } from "@/Neon/Function";
3import { Project } from "@/Neon/Project";
4import { providers } from "@/Neon/Providers";
5import * as Test from "@/Test/Alchemy";
6import * as Api from "@distilled.cloud/neon";
7import { expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import * as HttpClient from "effect/http/HttpClient";
10
11const { test } = Test.make({ providers: providers(), dev: true });
12test.provider(
13 "local Function runs behind the RPC sidecar and restarts on env changes",
14 (stack) =>
15 Effect.gen(function* () {
16 yield* stack.destroy();
17 const deploy = (value: string) =>
18 stack.deploy(
19 Function("LocalApi", {
20 branch: { projectId: "local-project", branchId: "local-branch" },
21 main: new URL("./fixtures/function-native.ts", import.meta.url)
22 .href,
23 env: { FUNCTION_TEST_VALUE: value },
24 }),
25 );
26 const first = yield* deploy("one");
27 expect(first.functionId).toMatch(/^dev:/);
28 const client = yield* HttpClient.HttpClient;
29 expect(yield* (yield* client.get(`${first.url}/env`)).json).toMatchObject(
30 { value: "one", hasAccountKey: false },
31 );
32 const second = yield* deploy("two");
33 expect(
34 yield* (yield* client.get(`${second.url}/env`)).json,
35 ).toMatchObject({ value: "two", hasAccountKey: false });
36 yield* stack.destroy();
37 expect(
38 yield* client.get(second.url).pipe(
39 Effect.timeout("5 seconds"),
40 Effect.as(false),
41 Effect.catchTag("HttpClientError", () => Effect.succeed(true)),
42 ),
43 ).toBe(true);
44 }),
45 {
46 tags: ["provider:neon", "provider:neon:function", "local"],
47 timeout: 120_000,
48 },
49);
50
51test.provider(
52 "remote Function opt-out uses the live provider and stamped cleanup",
53 (stack) =>
54 Effect.gen(function* () {
55 yield* stack.destroy();
56 const first = yield* stack.deploy(
57 Effect.gen(function* () {
58 const project = yield* Project("RemoteFunctionProject", {
59 region: "aws-us-east-2",
60 });
61 return yield* Function("RemoteApi", {
62 project,
63 main: new URL("./fixtures/function-bare.ts", import.meta.url).href,
64 }).pipe(Alchemy.remote());
65 }),
66 );
67 expect(first.functionId.startsWith("dev:")).toBe(false);
68 const client = yield* HttpClient.HttpClient;
69 expect(yield* (yield* client.get(first.url)).text).toBe("bare-v2");
70 const observed = yield* Api.getProjectBranchFunction({
71 project_id: first.projectId,
72 branch_id: first.branchId,
73 slug: first.slug,
74 });
75 expect(observed.function.id).toBe(first.functionId);
76 yield* stack.destroy();
77 expect(
78 yield* Api.getProjectBranchFunction({
79 project_id: first.projectId,
80 branch_id: first.branchId,
81 slug: first.slug,
82 }).pipe(
83 Effect.as(false),
84 Effect.catchTag("NotFound", () => Effect.succeed(true)),
85 ),
86 ).toBe(true);
87 }),
88 {
89 tags: [
90 "provider:neon",
91 "provider:neon:function",
92 "provider:neon:project",
93 "live",
94 ],
95 timeout: 120_000,
96 },
97);