1import { Auth } from "@/Neon/Auth.ts";
2import { AuthOAuthProvider } from "@/Neon/AuthOAuthProvider.ts";
3import { Project } from "@/Neon/Project.ts";
4import { providers } from "@/Neon/Providers.ts";
5import * as Test from "@/Test/Alchemy";
6import * as SDK from "@distilled.cloud/neon";
7import { expect } from "alchemy-test";
8import * as Effect from "effect/Effect";
9import * as Redacted from "effect/Redacted";
10
11const { test } = Test.make({ providers: providers() });
12
13test.provider(
14 "OAuth configuration owns one provider and never publishes its secret",
15 (stack) =>
16 Effect.gen(function* () {
17 yield* stack.destroy();
18 const application = (clientId: string | undefined) =>
19 Effect.gen(function* () {
20 const project = yield* Project("OAuthProject", {
21 region: "aws-us-east-2",
22 });
23 const auth = yield* Auth("OAuthAuth", { project });
24 const provider = yield* AuthOAuthProvider("GitHub", {
25 auth,
26 provider: "github",
27 clientId,
28 clientSecret: Redacted.make(
29 "alchemy-configuration-only-not-a-real-oauth-secret",
30 ),
31 });
32 return { auth, provider };
33 });
34 const first = yield* stack.deploy(
35 application("alchemy-configuration-only"),
36 );
37 const request = {
38 project_id: first.auth.projectId,
39 branch_id: first.auth.branchId,
40 };
41 expect(
42 (yield* SDK.listBranchNeonAuthOauthProviders(request)).providers.find(
43 (item) => item.id === "github",
44 )?.client_id,
45 ).toBe("alchemy-configuration-only");
46 expect(Object.keys(first.provider)).not.toContain("clientSecret");
47 const updated = yield* stack.deploy(
48 application("alchemy-configuration-updated"),
49 );
50 expect(updated.provider.provider).toBe("github");
51 expect(
52 (yield* SDK.listBranchNeonAuthOauthProviders(request)).providers.find(
53 (item) => item.id === "github",
54 )?.client_id,
55 ).toBe("alchemy-configuration-updated");
56 const refused = yield* stack.deploy(application(undefined)).pipe(
57 Effect.as(false),
58 Effect.catchTag("InvalidManagedAuth", () => Effect.succeed(true)),
59 );
60 expect(refused).toBe(true);
61 expect(
62 (yield* SDK.listBranchNeonAuthOauthProviders(request)).providers.find(
63 (item) => item.id === "github",
64 )?.client_id,
65 ).toBe("alchemy-configuration-updated");
66 yield* stack.destroy();
67 expect(
68 yield* SDK.getNeonAuth(request).pipe(
69 Effect.as(false),
70 Effect.catchTag("NotFound", () => Effect.succeed(true)),
71 ),
72 ).toBe(true);
73 yield* stack.destroy();
74 }),
75 {
76 tags: [
77 "provider:neon",
78 "provider:neon:auth",
79 "provider:neon:authoauthprovider",
80 "provider:neon:project",
81 "live",
82 ],
83 timeout: 120_000,
84 },
85);