1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as saasservicemgmt from "@distilled.cloud/gcp/saasservicemgmt_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import {
7 location,
8 logLevel,
9 currentProject,
10 blueprintPackage,
11 runBlueprintLifecycle,
12 runLifecycle,
13 waitUntilGone,
14} from "./common.ts";
15
16const { test } = Test.make({ providers: GCP.providers() });
17
18test.provider(
19 "getProjectsLocationsReleases on a missing release fails with a typed tag",
20 (stack) =>
21 Effect.gen(function* () {
22 const project = yield* currentProject;
23 yield* stack.destroy();
24
25 const error = yield* Effect.flip(
26 saasservicemgmt.getProjectsLocationsReleases({
27 name: `projects/${project}/locations/${location}/releases/alchemy-missing-rel`,
28 }),
29 );
30 expect(error._tag).toEqual("NotFound");
31
32 yield* stack.destroy();
33 }).pipe(logLevel),
34 {
35 tags: ["provider:gcp", "provider:gcp:saasservicemgmt", "live"],
36 timeout: 90_000,
37 },
38);
39
40test.provider.skipIf(!runLifecycle || runBlueprintLifecycle)(
41 "a release with a missing blueprint image fails with BlueprintNotFound",
42 (stack) =>
43 Effect.gen(function* () {
44 const project = yield* currentProject;
45 yield* stack.destroy();
46
47 const error = yield* Effect.flip(
48 stack.deploy(
49 Effect.gen(function* () {
50 const product = yield* GCP.SaasServiceManagement.Saa(
51 "MissingProduct",
52 {
53 location,
54 locations: [{ name: location }],
55 },
56 );
57 const kind = yield* GCP.SaasServiceManagement.UnitKind(
58 "MissingKind",
59 {
60 location,
61 saas: product.name,
62 },
63 );
64 return yield* GCP.SaasServiceManagement.Release("Missing", {
65 location,
66 unitKind: kind.name,
67 blueprint: {
68 package: `${location}-docker.pkg.dev/${project}/alchemy-missing/store:v1`,
69 },
70 });
71 }),
72 ),
73 );
74 expect(error._tag).toEqual("BlueprintNotFound");
75
76 yield* stack.destroy();
77 }).pipe(logLevel),
78 {
79 tags: ["provider:gcp", "provider:gcp:saasservicemanagement", "live"],
80 timeout: 120_000,
81 },
82);
83
84test.provider.skipIf(!runBlueprintLifecycle)(
85 "create, update, and delete a release",
86 (stack) =>
87 Effect.gen(function* () {
88 const blueprint = blueprintPackage ?? "";
89 yield* stack.destroy();
90
91 const created = yield* stack.deploy(
92 Effect.gen(function* () {
93 const product = yield* GCP.SaasServiceManagement.Saa("Inventory", {
94 location,
95 locations: [{ name: location }],
96 });
97 const kind = yield* GCP.SaasServiceManagement.UnitKind("Store", {
98 location,
99 saas: product.name,
100 });
101 const release = yield* GCP.SaasServiceManagement.Release("V1", {
102 location,
103 unitKind: kind.name,
104 blueprint: { package: blueprint },
105 labels: { env: "test" },
106 });
107 return { product, kind, release };
108 }),
109 );
110
111 expect(created.release.name).toContain("/releases/");
112 expect(created.release.unitKindId).toEqual(created.kind.unitKindId);
113 expect(created.release.blueprint?.package).toEqual(blueprint);
114 expect(created.release.labels).toMatchObject({ env: "test" });
115
116 const fetched = yield* saasservicemgmt.getProjectsLocationsReleases({
117 name: created.release.name,
118 });
119 expect(fetched.name).toEqual(created.release.name);
120 expect(fetched.labels?.env).toEqual("test");
121 expect(
122 Object.keys(fetched.labels ?? {}).some((key) =>
123 key.startsWith("alchemy-"),
124 ),
125 ).toEqual(true);
126
127 const updated = yield* stack.deploy(
128 Effect.gen(function* () {
129 const product = yield* GCP.SaasServiceManagement.Saa("Inventory", {
130 saasId: created.product.saasId,
131 location,
132 locations: [{ name: location }],
133 });
134 const kind = yield* GCP.SaasServiceManagement.UnitKind("Store", {
135 unitKindId: created.kind.unitKindId,
136 location,
137 saas: product.name,
138 });
139 const release = yield* GCP.SaasServiceManagement.Release("V1", {
140 releaseId: created.release.releaseId,
141 location,
142 unitKind: kind.name,
143 blueprint: { package: blueprint },
144 inputVariableDefaults: [
145 { variable: "region", type: "STRING", value: location },
146 ],
147 labels: { env: "prod" },
148 });
149 return { product, kind, release };
150 }),
151 );
152
153 expect(updated.release.name).toEqual(created.release.name);
154 expect(updated.release.labels).toMatchObject({ env: "prod" });
155 expect(updated.release.inputVariableDefaults[0]?.variable).toEqual(
156 "region",
157 );
158
159 yield* stack.destroy();
160 const gone = yield* waitUntilGone(
161 saasservicemgmt.getProjectsLocationsReleases({
162 name: created.release.name,
163 }),
164 );
165 expect(gone).toEqual("gone");
166 }).pipe(logLevel),
167 {
168 tags: ["provider:gcp", "provider:gcp:saasservicemgmt", "live"],
169 timeout: 90_000,
170 },
171);