1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as Region from "@distilled.cloud/gcp/Region";
4import * as secretmanager from "@distilled.cloud/gcp/secretmanager_v1";
5import { expect } from "alchemy-test";
6import * as Effect from "effect/Effect";
7import * as Layer from "effect/Layer";
8import * as Redacted from "effect/Redacted";
9import { Credentials } from "@distilled.cloud/gcp/Credentials";
10import { fromCredentials, GcpEnvironment } from "@/GCP/Environment";
11import { MinimumLogLevel } from "effect/References";
12
13const { test } = Test.make({
14 providers: GCP.providers().pipe(Layer.provideMerge(GCP.Region("us-east4"))),
15});
16
17const logLevel = Effect.provideService(
18 MinimumLogLevel,
19 process.env.DEBUG ? "Debug" : "Info",
20);
21
22test(
23 "regional endpoint routing",
24 Effect.sync(() => {
25 const base = "https://secretmanager.googleapis.com/";
26 expect(
27 Region.endpointFor(
28 base,
29 "v1/projects/p/locations/us-east1/secrets/s",
30 "required",
31 ),
32 ).toEqual("https://secretmanager.us-east1.rep.googleapis.com/");
33 expect(
34 Region.endpointFor(base, "v1/projects/p/secrets/s", "required"),
35 ).toEqual(base);
36 expect(
37 Region.endpointFor(
38 "https://run.googleapis.com/",
39 "v2/projects/p/locations/us-east1/services/s",
40 "required",
41 ),
42 ).toEqual("https://run.googleapis.com/");
43 expect(
44 Region.endpointFor(
45 "https://run.googleapis.com/",
46 "v2/projects/p/locations/us-east1/services/s",
47 "prefer",
48 ),
49 ).toEqual("https://run.us-east1.rep.googleapis.com/");
50 }),
51 { tags: ["unit", "provider:gcp", "provider:gcp:region", "local"] },
52);
53
54const credentialWithRegion = (region: string | undefined) =>
55 Layer.succeed(
56 Credentials,
57 Effect.succeed({
58 accessToken: Redacted.make("token"),
59 project: "p",
60 region,
61 }),
62 );
63
64const resolvedRegion = (layer: Layer.Layer<GcpEnvironment>) =>
65 GcpEnvironment.use((env) => Effect.map(env, (e) => e.region)).pipe(
66 Effect.provide(layer),
67 );
68
69test(
70 "the credential's region is the default; GCP.Region overrides it",
71 Effect.gen(function* () {
72 expect(
73 yield* resolvedRegion(
74 fromCredentials().pipe(
75 Layer.provide(credentialWithRegion("asia-east1")),
76 ),
77 ),
78 ).toEqual("asia-east1");
79 expect(
80 yield* resolvedRegion(
81 fromCredentials().pipe(Layer.provide(credentialWithRegion(undefined))),
82 ),
83 ).toEqual("us-central1");
84 expect(
85 yield* resolvedRegion(
86 fromCredentials().pipe(
87 Layer.provide(credentialWithRegion("asia-east1")),
88 Layer.provide(GCP.Region("europe-west1")),
89 ),
90 ),
91 ).toEqual("europe-west1");
92 }),
93 { tags: ["unit", "provider:gcp", "provider:gcp:region", "local"] },
94);
95
96test.provider(
97 "a stack-level GCP.Region sets the default region; regional secrets reach their endpoint",
98 (stack) =>
99 Effect.gen(function* () {
100 yield* stack.destroy();
101
102 const out = yield* stack.deploy(
103 Effect.gen(function* () {
104 // No `location`: placed in the scope's default region.
105 const queue = yield* GCP.CloudTasks.Queue("RegionQueue", {});
106 // A regional secret, also in the default region.
107 const secret = yield* GCP.SecretManager.LocationsSecret(
108 "RegionalSecret",
109 {},
110 );
111 return { queue: queue.name, secret: secret.name };
112 }),
113 );
114
115 expect(out.queue).toContain("/locations/us-east4/");
116 expect(out.secret).toContain("/locations/us-east4/");
117
118 // Out-of-band read with plain fetch: distilled routes the regional
119 // secret to secretmanager.us-east4.rep.googleapis.com.
120 const live = yield* secretmanager.getProjectsLocationsSecrets({
121 name: out.secret,
122 });
123 expect(live.name).toEqual(out.secret);
124
125 yield* stack.destroy();
126 }).pipe(logLevel),
127 {
128 tags: [
129 "provider:gcp",
130 "provider:gcp:cloudtasks",
131 "provider:gcp:secretmanager",
132 "live",
133 ],
134 timeout: 240_000,
135 },
136);
137
138const withoutOverride = Test.make({ providers: GCP.providers() });
139
140withoutOverride.test.provider(
141 "without GCP.Region, the credential's region is the default",
142 (stack) =>
143 Effect.gen(function* () {
144 yield* stack.destroy();
145 const out = yield* stack.deploy(
146 Effect.gen(function* () {
147 const queue = yield* GCP.CloudTasks.Queue(
148 "CredentialsRegionQueue",
149 {},
150 );
151 return { queue: queue.name, region: yield* GCP.currentRegion };
152 }),
153 );
154 // The testing credential sets no region, so the default applies.
155 expect(out.region).toEqual("us-central1");
156 expect(out.queue).toContain("/locations/us-central1/");
157 yield* stack.destroy();
158 }).pipe(logLevel),
159 {
160 tags: ["provider:gcp", "provider:gcp:cloudtasks", "live"],
161 timeout: 120_000,
162 },
163);