1import { decodeFunctionTriggerEvent } from "@/Neon/FunctionTriggerEvent";
2import { expect, test } from "alchemy-test";
3import * as Effect from "effect/Effect";
4
5const payload = {
6 version: 1,
7 invocation_id: "occurrence",
8 trigger: { type: "schedule", id: "trigger-test", name: "Nightly" },
9 data: { scheduled_at: "2026-09-17T00:00:00Z" },
10};
11const request = (
12 body: unknown,
13 headers: Record<string, string> = {
14 "x-neon-trigger-invocation-id": "occurrence",
15 },
16) =>
17 new Request("https://example.test/jobs", {
18 method: "POST",
19 headers,
20 body: JSON.stringify(body),
21 });
22
23test.effect(
24 "validates the versioned schedule envelope and attested header",
25 () =>
26 Effect.gen(function* () {
27 const event = yield* decodeFunctionTriggerEvent(
28 yield* Effect.sync(() => request(payload)),
29 );
30 expect(event).toEqual(payload);
31 }),
32 { tags: ["unit", "provider:neon", "provider:neon:function", "local"] },
33);
34
35for (const [name, body, headers, status] of [
36 ["missing attestation", payload, {}, 403],
37 [
38 "mismatched attestation",
39 payload,
40 { "x-neon-trigger-invocation-id": "other" },
41 403,
42 ],
43 [
44 "future version",
45 { ...payload, version: 2 },
46 { "x-neon-trigger-invocation-id": "occurrence" },
47 400,
48 ],
49 [
50 "unknown discriminator",
51 { ...payload, trigger: { ...payload.trigger, type: "other" } },
52 { "x-neon-trigger-invocation-id": "occurrence" },
53 400,
54 ],
55] as const)
56 test.effect(
57 name,
58 () =>
59 Effect.gen(function* () {
60 const result = yield* decodeFunctionTriggerEvent(
61 yield* Effect.sync(() => request(body, headers)),
62 ).pipe(
63 Effect.as(200),
64 Effect.catchTag("FunctionTriggerEventError", (error) =>
65 Effect.succeed(error.status),
66 ),
67 );
68 expect(result).toBe(status);
69 }),
70 { tags: ["unit", "provider:neon", "provider:neon:function", "local"] },
71 );