1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as gamesConfiguration from "@distilled.cloud/gcp/gamesConfiguration_v1configuration";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 applicationId,
9 logLevel,
10 missingTag,
11 probeApplicationId,
12} from "./common.ts";
13
14const { test } = Test.make({ providers: GCP.providers() });
15
16const waitUntilGone = (achievementId: string) =>
17 gamesConfiguration.getAchievementConfigurations({ achievementId }).pipe(
18 Effect.as("found" as const),
19 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
20 Effect.repeat({
21 schedule: Schedule.spaced("1 second"),
22 until: (status) => status === "gone",
23 times: 10,
24 }),
25 );
26
27test.provider(
28 "getAchievementConfigurations on a missing achievement fails with a typed tag",
29 (stack) =>
30 Effect.gen(function* () {
31 yield* stack.destroy();
32
33 const error = yield* Effect.flip(
34 gamesConfiguration.getAchievementConfigurations({
35 achievementId: "alchemy-missing-achievement",
36 }),
37 );
38 expect(error._tag).toEqual(missingTag);
39
40 yield* stack.destroy();
41 }).pipe(logLevel),
42 {
43 tags: ["provider:gcp", "provider:gcp:gamesconfiguration", "live"],
44 timeout: 90_000,
45 },
46);
47
48test.provider.skipIf(!!applicationId)(
49 "insertAchievementConfigurations without Play Games access fails with a typed tag",
50 (stack) =>
51 Effect.gen(function* () {
52 yield* stack.destroy();
53
54 const error = yield* Effect.flip(
55 gamesConfiguration.insertAchievementConfigurations({
56 applicationId: probeApplicationId,
57 body: {
58 achievementType: "STANDARD",
59 initialState: "REVEALED",
60 draft: {
61 name: {
62 translations: [{ locale: "en-US", value: "Alchemy Probe" }],
63 },
64 description: {
65 translations: [{ locale: "en-US", value: "probe" }],
66 },
67 pointValue: 5,
68 },
69 },
70 }),
71 );
72 expect(error._tag).toEqual("Forbidden");
73
74 yield* stack.destroy();
75 }).pipe(logLevel),
76 {
77 tags: ["provider:gcp", "provider:gcp:gamesconfiguration", "live"],
78 timeout: 90_000,
79 },
80);
81
82// Needs a Play Games Services application the credentials administer; without
83// one the API rejects inserts with Forbidden (probe above).
84// Set GCP_GAMESCONFIGURATION_APPLICATION_ID to run the lifecycle.
85test.provider.skipIf(!applicationId)(
86 "create, update, and delete an achievement configuration",
87 (stack) =>
88 Effect.gen(function* () {
89 yield* stack.destroy();
90
91 const created = yield* stack.deploy(
92 Effect.gen(function* () {
93 return yield* GCP.GamesConfiguration.AchievementConfiguration(
94 "FirstWin",
95 {
96 applicationId: applicationId!,
97 name: "First Win",
98 description: "Win your first match",
99 },
100 );
101 }),
102 );
103
104 expect(created.achievementId.length).toBeGreaterThan(0);
105 expect(created.applicationId).toEqual(applicationId);
106 expect(created.name).toEqual("First Win");
107 expect(created.description).toEqual("Win your first match");
108
109 const fetched = yield* gamesConfiguration.getAchievementConfigurations({
110 achievementId: created.achievementId,
111 });
112 expect(fetched.id).toEqual(created.achievementId);
113 expect(fetched.draft?.description?.translations?.[0]?.value).toEqual(
114 "Win your first match",
115 );
116
117 const updated = yield* stack.deploy(
118 Effect.gen(function* () {
119 return yield* GCP.GamesConfiguration.AchievementConfiguration(
120 "FirstWin",
121 {
122 applicationId: created.applicationId,
123 achievementId: created.achievementId,
124 name: "First Win",
125 description: "Win a match",
126 },
127 );
128 }),
129 );
130
131 expect(updated.achievementId).toEqual(created.achievementId);
132 expect(updated.description).toEqual("Win a match");
133
134 yield* stack.destroy();
135
136 const gone = yield* waitUntilGone(created.achievementId);
137 expect(gone).toEqual("gone");
138 }).pipe(logLevel),
139 {
140 tags: ["provider:gcp", "provider:gcp:gamesconfiguration", "live"],
141 timeout: 90_000,
142 },
143);