1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as androidenterprise from "@distilled.cloud/gcp/androidenterprise_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 enterpriseId,
9 logLevel,
10 probeEnterpriseId,
11 runLifecycle,
12} from "./common.ts";
13
14const { test } = Test.make({ providers: GCP.providers() });
15
16const waitUntilGone = (accountId: string, pageId: string, clusterId: string) =>
17 androidenterprise
18 .getStorelayoutclusters({
19 enterpriseId: accountId,
20 pageId,
21 clusterId,
22 })
23 .pipe(
24 Effect.as("found" as const),
25 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
26 Effect.repeat({
27 schedule: Schedule.spaced("1 second"),
28 until: (status) => status === "gone",
29 times: 10,
30 }),
31 );
32
33test.provider.skipIf(!runLifecycle)(
34 "getStorelayoutclusters on a missing cluster fails with a typed tag",
35 (stack) =>
36 Effect.gen(function* () {
37 yield* stack.destroy();
38
39 const error = yield* Effect.flip(
40 androidenterprise.getStorelayoutclusters({
41 enterpriseId: probeEnterpriseId,
42 pageId: "alchemy-missing-page",
43 clusterId: "alchemy-missing-cluster",
44 }),
45 );
46 expect(error._tag).toEqual("NotFound");
47
48 yield* stack.destroy();
49 }).pipe(logLevel),
50 {
51 tags: ["provider:gcp", "provider:gcp:androidenterprise", "live"],
52 timeout: 90_000,
53 },
54);
55
56test.provider.skipIf(runLifecycle)(
57 "insertStorelayoutclusters without the EMM OAuth scope fails with InsufficientAuthenticationScopes",
58 (stack) =>
59 Effect.gen(function* () {
60 yield* stack.destroy();
61
62 const error = yield* Effect.flip(
63 androidenterprise.insertStorelayoutclusters({
64 enterpriseId: probeEnterpriseId,
65 pageId: "alchemy-missing-page",
66 body: {
67 name: [{ locale: "en-US", text: "Alchemy Probe Cluster" }],
68 },
69 }),
70 );
71 expect(error._tag).toEqual("InsufficientAuthenticationScopes");
72
73 yield* stack.destroy();
74 }).pipe(logLevel),
75 {
76 tags: ["provider:gcp", "provider:gcp:androidenterprise", "live"],
77 timeout: 90_000,
78 },
79);
80
81test.provider.skipIf(!runLifecycle)(
82 "create, update, and delete a store layout cluster",
83 (stack) =>
84 Effect.gen(function* () {
85 yield* stack.destroy();
86
87 const created = yield* stack.deploy(
88 Effect.gen(function* () {
89 const page = yield* GCP.AndroidEnterprise.Storelayoutpage("Home", {
90 enterpriseId: enterpriseId!,
91 name: [{ locale: "en-US", text: "Home" }],
92 });
93 const cluster = yield* GCP.AndroidEnterprise.Storelayoutcluster(
94 "Apps",
95 {
96 enterpriseId: page.enterpriseId,
97 pageId: page.pageId,
98 name: [{ locale: "en-US", text: "Work apps" }],
99 },
100 );
101 return { page, cluster };
102 }),
103 );
104
105 expect(created.cluster.clusterId.length).toBeGreaterThan(0);
106 expect(created.cluster.pageId).toEqual(created.page.pageId);
107 expect(created.cluster.name?.[0]?.text).toEqual("Work apps");
108
109 const fetched = yield* androidenterprise.getStorelayoutclusters({
110 enterpriseId: created.cluster.enterpriseId,
111 pageId: created.cluster.pageId,
112 clusterId: created.cluster.clusterId,
113 });
114 expect(fetched.id).toEqual(created.cluster.clusterId);
115 expect(fetched.name?.[0]?.text).toEqual("Work apps");
116
117 const updated = yield* stack.deploy(
118 Effect.gen(function* () {
119 const page = yield* GCP.AndroidEnterprise.Storelayoutpage("Home", {
120 enterpriseId: created.page.enterpriseId,
121 pageId: created.page.pageId,
122 name: [{ locale: "en-US", text: "Home" }],
123 });
124 const cluster = yield* GCP.AndroidEnterprise.Storelayoutcluster(
125 "Apps",
126 {
127 enterpriseId: page.enterpriseId,
128 pageId: page.pageId,
129 clusterId: created.cluster.clusterId,
130 name: [{ locale: "en-US", text: "Featured apps" }],
131 },
132 );
133 return { page, cluster };
134 }),
135 );
136
137 expect(updated.cluster.clusterId).toEqual(created.cluster.clusterId);
138 expect(updated.cluster.name?.[0]?.text).toEqual("Featured apps");
139
140 yield* stack.destroy();
141
142 const gone = yield* waitUntilGone(
143 created.cluster.enterpriseId,
144 created.cluster.pageId,
145 created.cluster.clusterId,
146 );
147 expect(gone).toEqual("gone");
148 }).pipe(logLevel),
149 {
150 tags: ["provider:gcp", "provider:gcp:androidenterprise", "live"],
151 timeout: 90_000,
152 },
153);