1import * as GCP from "@/GCP";
2import * as Test from "@/Test/Alchemy";
3import * as firebasedataconnect from "@distilled.cloud/gcp/firebasedataconnect_v1";
4import { expect } from "alchemy-test";
5import * as Effect from "effect/Effect";
6import * as Schedule from "effect/Schedule";
7import {
8 connectorSource,
9 location,
10 logLevel,
11 missingConnector,
12 currentProject,
13 schemaSource,
14 unlinkedDatasources,
15} from "./common.ts";
16
17const { test } = Test.make({ providers: GCP.providers() });
18
19const waitUntilGone = (name: string) =>
20 firebasedataconnect.getProjectsLocationsServicesConnectors({ name }).pipe(
21 Effect.as("found" as const),
22 Effect.catchTag("NotFound", () => Effect.succeed("gone" as const)),
23 Effect.repeat({
24 schedule: Schedule.spaced("3 seconds"),
25 until: (status) => status === "gone",
26 times: 10,
27 }),
28 );
29
30test.provider(
31 "getProjectsLocationsServicesConnectors on a missing connector fails with a typed tag",
32 (stack) =>
33 Effect.gen(function* () {
34 const project = yield* currentProject;
35
36 yield* stack.destroy();
37
38 const missing = yield* missingConnector();
39 const error = yield* Effect.flip(
40 firebasedataconnect.getProjectsLocationsServicesConnectors({
41 name: missing,
42 }),
43 );
44 expect(error._tag).toEqual("NotFound");
45
46 const page =
47 yield* firebasedataconnect.listProjectsLocationsServicesConnectors({
48 parent: `projects/${project}/locations/${location}/services/-`,
49 pageSize: 10,
50 });
51 expect((page.connectors ?? []).map((item) => item.name)).not.toContain(
52 missing,
53 );
54
55 yield* stack.destroy();
56 }).pipe(logLevel),
57 {
58 tags: ["provider:gcp", "provider:gcp:firebasedataconnect", "live"],
59 timeout: 90_000,
60 },
61);
62
63test.provider(
64 "create, update, and delete a Data Connect connector",
65 (stack) =>
66 Effect.gen(function* () {
67 yield* stack.destroy();
68
69 const created = yield* stack.deploy(
70 Effect.gen(function* () {
71 const service = yield* GCP.FirebaseDataConnect.Service("App", {
72 displayName: "alchemy-test-connector-svc",
73 labels: { env: "test" },
74 });
75 const schema = yield* GCP.FirebaseDataConnect.ServicesSchema("Main", {
76 service: service.name,
77 source: schemaSource(),
78 datasources: unlinkedDatasources,
79 labels: { env: "test" },
80 });
81 const connector = yield* GCP.FirebaseDataConnect.ServicesConnector(
82 "Queries",
83 {
84 service: schema.service,
85 source: connectorSource,
86 displayName: "alchemy-test-connector",
87 labels: { env: "test" },
88 },
89 );
90 return { service, schema, connector };
91 }),
92 );
93
94 expect(created.connector.name).toContain("/connectors/");
95 expect(created.connector.connectorId).toEqual(expect.any(String));
96 expect(created.connector.location).toEqual(location);
97 expect(created.connector.displayName).toEqual("alchemy-test-connector");
98 expect(created.connector.labels).toMatchObject({ env: "test" });
99 expect(created.connector.service).toEqual(created.service.name);
100 expect(created.connector.reconciling).toEqual(false);
101
102 const fetched =
103 yield* firebasedataconnect.getProjectsLocationsServicesConnectors({
104 name: created.connector.name,
105 });
106 expect(fetched.name).toEqual(created.connector.name);
107 expect(fetched.displayName).toEqual("alchemy-test-connector");
108 expect(fetched.labels?.env).toEqual("test");
109
110 const updated = yield* stack.deploy(
111 Effect.gen(function* () {
112 const service = yield* GCP.FirebaseDataConnect.Service("App", {
113 serviceId: created.service.serviceId,
114 displayName: "alchemy-prod-connector-svc",
115 labels: { env: "prod" },
116 });
117 const schema = yield* GCP.FirebaseDataConnect.ServicesSchema("Main", {
118 service: service.name,
119 source: schemaSource(),
120 datasources: unlinkedDatasources,
121 labels: { env: "prod" },
122 });
123 const connector = yield* GCP.FirebaseDataConnect.ServicesConnector(
124 "Queries",
125 {
126 service: schema.service,
127 connectorId: created.connector.connectorId,
128 source: connectorSource,
129 displayName: "alchemy-prod-connector",
130 labels: { env: "prod", role: "query" },
131 },
132 );
133 return { service, schema, connector };
134 }),
135 );
136
137 expect(updated.connector.name).toEqual(created.connector.name);
138 expect(updated.connector.displayName).toEqual("alchemy-prod-connector");
139 expect(updated.connector.labels).toMatchObject({
140 env: "prod",
141 role: "query",
142 });
143
144 const refetched =
145 yield* firebasedataconnect.getProjectsLocationsServicesConnectors({
146 name: created.connector.name,
147 });
148 expect(refetched.displayName).toEqual("alchemy-prod-connector");
149 expect(refetched.labels?.env).toEqual("prod");
150
151 yield* stack.destroy();
152
153 const gone = yield* waitUntilGone(created.connector.name);
154 expect(gone).toEqual("gone");
155 }).pipe(logLevel),
156 {
157 tags: ["provider:gcp", "provider:gcp:firebasedataconnect", "live"],
158 timeout: 120_000,
159 },
160);