1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as cloudidentity from "@distilled.cloud/gcp/cloudidentity_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import { customer, logLevel, runLifecycle } from "./common.ts";
8
9const { test } = Test.make({ providers: GCP.providers() });
10
11const waitUntilGone = (name: string) =>
12 cloudidentity.getInboundOidcSsoProfiles({ name }).pipe(
13 Effect.as("found" as const),
14 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
15 Effect.repeat({
16 schedule: Schedule.spaced("1 second"),
17 until: (status) => status === "gone",
18 times: 10,
19 }),
20 );
21
22test.provider(
23 "getInboundOidcSsoProfiles on a missing profile fails with a typed tag",
24 (stack) =>
25 Effect.gen(function* () {
26 yield* stack.destroy();
27
28 const error = yield* Effect.flip(
29 cloudidentity.getInboundOidcSsoProfiles({
30 name: "inboundOidcSsoProfiles/alchemy-missing",
31 }),
32 );
33 expect(error._tag).toEqual("ServiceDisabled");
34
35 yield* stack.destroy();
36 }).pipe(logLevel),
37 {
38 tags: ["provider:gcp", "provider:gcp:cloudidentity", "live"],
39 timeout: 90_000,
40 },
41);
42
43test.provider.skipIf(!!process.env.GCP_TEST_CLOUDIDENTITY)(
44 "createInboundOidcSsoProfiles without Cloud Identity access fails with a typed entitlement error",
45 (stack) =>
46 Effect.gen(function* () {
47 yield* stack.destroy();
48
49 const error = yield* Effect.flip(
50 cloudidentity.createInboundOidcSsoProfiles({
51 body: {
52 customer,
53 displayName: "Alchemy OIDC Probe",
54 idpConfig: { issuerUri: "https://example.com" },
55 },
56 }),
57 );
58 expect(error._tag).toEqual("ServiceDisabled");
59
60 yield* stack.destroy();
61 }).pipe(logLevel),
62 {
63 tags: ["provider:gcp", "provider:gcp:cloudidentity", "live"],
64 timeout: 90_000,
65 },
66);
67
68test.provider.skipIf(!runLifecycle)(
69 "create, update, and delete an OIDC SSO profile",
70 (stack) =>
71 Effect.gen(function* () {
72 yield* stack.destroy();
73
74 const created = yield* stack.deploy(
75 Effect.gen(function* () {
76 return yield* GCP.CloudIdentity.InboundOidcSsoProfile("Okta", {
77 customer,
78 displayName: "Okta",
79 idpConfig: { issuerUri: "https://idp.example.com" },
80 rpConfig: { clientId: "google-rp", clientSecret: "secret" },
81 });
82 }),
83 );
84
85 expect(created.name.startsWith("inboundOidcSsoProfiles/")).toEqual(true);
86 expect(created.displayName).toEqual("Okta");
87
88 const fetched = yield* cloudidentity.getInboundOidcSsoProfiles({
89 name: created.name,
90 });
91 expect(fetched.name).toEqual(created.name);
92 expect(fetched.displayName).toContain("[alchemy ");
93
94 const updated = yield* stack.deploy(
95 Effect.gen(function* () {
96 return yield* GCP.CloudIdentity.InboundOidcSsoProfile("Okta", {
97 customer,
98 displayName: "Okta Prod",
99 idpConfig: { issuerUri: "https://idp.example.com" },
100 rpConfig: { clientId: "google-rp" },
101 });
102 }),
103 );
104
105 expect(updated.name).toEqual(created.name);
106 expect(updated.displayName).toEqual("Okta Prod");
107
108 yield* stack.destroy();
109
110 const gone = yield* waitUntilGone(created.name);
111 expect(gone).toEqual("gone");
112 }).pipe(logLevel),
113 {
114 tags: ["provider:gcp", "provider:gcp:cloudidentity", "live"],
115 timeout: 90_000,
116 },
117);