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.getInboundSamlSsoProfiles({ 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 "getInboundSamlSsoProfiles 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.getInboundSamlSsoProfiles({
30 name: "inboundSamlSsoProfiles/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 "createInboundSamlSsoProfiles 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.createInboundSamlSsoProfiles({
51 body: {
52 customer,
53 displayName: "Alchemy SAML Probe",
54 idpConfig: {
55 entityId: "https://idp.example.com/metadata",
56 singleSignOnServiceUri: "https://idp.example.com/sso",
57 },
58 },
59 }),
60 );
61 expect(error._tag).toEqual("ServiceDisabled");
62
63 yield* stack.destroy();
64 }).pipe(logLevel),
65 {
66 tags: ["provider:gcp", "provider:gcp:cloudidentity", "live"],
67 timeout: 90_000,
68 },
69);
70
71test.provider.skipIf(!runLifecycle)(
72 "create, update, and delete a SAML SSO profile",
73 (stack) =>
74 Effect.gen(function* () {
75 yield* stack.destroy();
76
77 const created = yield* stack.deploy(
78 Effect.gen(function* () {
79 return yield* GCP.CloudIdentity.InboundSamlSsoProfile("Okta", {
80 customer,
81 displayName: "Okta",
82 idpConfig: {
83 entityId: "https://idp.example.com/metadata",
84 singleSignOnServiceUri: "https://idp.example.com/sso",
85 },
86 });
87 }),
88 );
89
90 expect(created.name.startsWith("inboundSamlSsoProfiles/")).toEqual(true);
91 expect(created.displayName).toEqual("Okta");
92
93 const fetched = yield* cloudidentity.getInboundSamlSsoProfiles({
94 name: created.name,
95 });
96 expect(fetched.name).toEqual(created.name);
97 expect(fetched.displayName).toContain("[alchemy ");
98
99 const updated = yield* stack.deploy(
100 Effect.gen(function* () {
101 return yield* GCP.CloudIdentity.InboundSamlSsoProfile("Okta", {
102 customer,
103 displayName: "Okta Prod",
104 idpConfig: {
105 entityId: "https://idp.example.com/metadata",
106 singleSignOnServiceUri: "https://idp.example.com/sso",
107 logoutRedirectUri: "https://idp.example.com/logout",
108 },
109 });
110 }),
111 );
112
113 expect(updated.name).toEqual(created.name);
114 expect(updated.displayName).toEqual("Okta Prod");
115
116 yield* stack.destroy();
117
118 const gone = yield* waitUntilGone(created.name);
119 expect(gone).toEqual("gone");
120 }).pipe(logLevel),
121 {
122 tags: ["provider:gcp", "provider:gcp:cloudidentity", "live"],
123 timeout: 90_000,
124 },
125);