@scalekit-sdk/node 2.2.0-beta.2 → 2.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,356 @@
1
+ import ScalekitClient from "../src/scalekit";
2
+ import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
3
+ import { TestDataGenerator, TestOrganizationManager } from "./utils/test-data";
4
+ import {
5
+ AuthorizationDetails,
6
+ CreateConnectedAccount,
7
+ OauthToken,
8
+ } from "../src/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb";
9
+
10
+ declare const global: {
11
+ client: ScalekitClient;
12
+ };
13
+
14
+ describe("Actions", () => {
15
+ let client: ScalekitClient;
16
+ let testOrg: string;
17
+
18
+ // Happy-path assumptions for the test environment:
19
+ // - A GMAIL connector named "GMAIL-test" exists.
20
+ // - A connected account with identifier "default" is ACTIVE for that connector.
21
+ // - A tool named "gmail_fetch_mails" is available for that connection.
22
+ const GMAIL_CONNECTION_NAME = "GMAIL-test";
23
+ const GMAIL_IDENTIFIER = "default";
24
+ const GMAIL_TOOL_NAME = "gmail_fetch_mails";
25
+ const USER_PROFILE_PATH = "/v1/users/me/profile";
26
+ const FRESHDESK_CONNECTION_NAME = "freshdesk";
27
+
28
+ beforeEach(async () => {
29
+ client = global.client;
30
+ testOrg = await TestOrganizationManager.createTestOrganization(client);
31
+ });
32
+
33
+ afterEach(async () => {
34
+ await TestOrganizationManager.cleanupTestOrganization(client, testOrg);
35
+ });
36
+
37
+ it("should expose actions on ScalekitClient", () => {
38
+ expect(client.actions).toBeDefined();
39
+ });
40
+
41
+ it("should expose key connected-account helper methods", () => {
42
+
43
+ expect(typeof client.actions.createConnectedAccount).toBe("function");
44
+ expect(typeof client.actions.getOrCreateConnectedAccount).toBe("function");
45
+ expect(typeof client.actions.updateConnectedAccount).toBe("function");
46
+ expect(typeof client.actions.deleteConnectedAccount).toBe("function");
47
+ expect(typeof client.actions.getConnectedAccount).toBe("function");
48
+ });
49
+
50
+ describe("executeTool", () => {
51
+ it("should delegate to tools.executeTool with basic params", async () => {
52
+ try {
53
+ await client.actions.executeTool({
54
+ toolName: "test_tool",
55
+ toolInput: { key: "value" },
56
+ });
57
+ } catch (error: any) {
58
+ expect(error).toBeDefined();
59
+ expect(error.message || error.toString()).toBeDefined();
60
+ }
61
+ });
62
+
63
+ it("should execute a Gmail tool successfully via actions", async () => {
64
+ const toolInput = { max_results: 1 };
65
+
66
+ const response = await client.actions.executeTool({
67
+ toolName: GMAIL_TOOL_NAME,
68
+ toolInput,
69
+ identifier: GMAIL_IDENTIFIER,
70
+ connector: GMAIL_CONNECTION_NAME,
71
+ });
72
+
73
+ expect(response).toBeDefined();
74
+ // We expect a non-empty executionId and some data from the tool.
75
+ expect(typeof response.executionId).toBe("string");
76
+ expect(response.executionId.length).toBeGreaterThan(0);
77
+ expect(response.data).toBeDefined();
78
+ });
79
+ });
80
+
81
+ describe("getAuthorizationLink", () => {
82
+ it("should call underlying magic link API", async () => {
83
+ try {
84
+ const response = await client.actions.getAuthorizationLink({
85
+ connectionName: "test_connector",
86
+ identifier: "test_identifier",
87
+ organizationId: testOrg,
88
+ });
89
+
90
+ expect(response).toBeDefined();
91
+ } catch (error: any) {
92
+ expect(error).toBeDefined();
93
+ }
94
+ });
95
+ });
96
+
97
+ describe("createConnectedAccount", () => {
98
+ it("should construct CreateConnectedAccount payload", async () => {
99
+ const oauthToken = new OauthToken({
100
+ accessToken: "test_access_token",
101
+ });
102
+
103
+ const authorizationDetails = new AuthorizationDetails({
104
+ details: {
105
+ case: "oauthToken",
106
+ value: oauthToken,
107
+ },
108
+ });
109
+
110
+ try {
111
+ const response = await client.actions.createConnectedAccount({
112
+ connectionName: "test_connector",
113
+ identifier: `test_${Date.now()}`,
114
+ authorizationDetails,
115
+ organizationId: testOrg,
116
+ });
117
+
118
+ expect(response).toBeDefined();
119
+ } catch (error: any) {
120
+ expect(error).toBeDefined();
121
+ }
122
+ });
123
+
124
+ it("should validate required parameters", async () => {
125
+ const oauthToken = new OauthToken({
126
+ accessToken: "test_access_token",
127
+ });
128
+
129
+ const authorizationDetails = new AuthorizationDetails({
130
+ details: {
131
+ case: "oauthToken",
132
+ value: oauthToken,
133
+ },
134
+ });
135
+
136
+ // Missing connectionName
137
+ await expect(
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ client.actions.createConnectedAccount({
140
+ connectionName: "" as any,
141
+ identifier: "test_id",
142
+ authorizationDetails,
143
+ } as any)
144
+ ).rejects.toThrow("connectionName is required");
145
+
146
+ // Missing identifier
147
+ await expect(
148
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
149
+ client.actions.createConnectedAccount({
150
+ connectionName: "GMAIL",
151
+ identifier: "" as any,
152
+ authorizationDetails,
153
+ } as any)
154
+ ).rejects.toThrow("identifier is required");
155
+ });
156
+ });
157
+
158
+ describe("getOrCreateConnectedAccount", () => {
159
+ it("should delegate validation to underlying client", async () => {
160
+ // connector / connectionName required
161
+ await expect(
162
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
163
+ client.actions.getOrCreateConnectedAccount({
164
+ connectionName: "" as any,
165
+ identifier: "user_123",
166
+ } as any)
167
+ ).rejects.toThrow("connector is required");
168
+
169
+ // identifier required
170
+ await expect(
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ client.actions.getOrCreateConnectedAccount({
173
+ connectionName: "gmail",
174
+ identifier: "" as any,
175
+ } as any)
176
+ ).rejects.toThrow("identifier is required");
177
+ });
178
+ });
179
+
180
+ describe("request", () => {
181
+ it("should validate required parameters", async () => {
182
+ await expect(
183
+ client.actions.request({
184
+ connectionName: "",
185
+ identifier: "",
186
+ path: "",
187
+ })
188
+ ).rejects.toThrow();
189
+ });
190
+
191
+ it("should fetch user profile successfully via proxy request", async () => {
192
+ const response = await client.actions.request({
193
+ connectionName: GMAIL_CONNECTION_NAME,
194
+ identifier: GMAIL_IDENTIFIER,
195
+ path: USER_PROFILE_PATH,
196
+ method: "GET",
197
+ });
198
+
199
+ expect(response).toBeDefined();
200
+ expect(response.status).toBeGreaterThanOrEqual(200);
201
+ expect(response.status).toBeLessThan(300);
202
+ // Response data should be JSON-serializable user profile shape.
203
+ expect(response.data).toBeDefined();
204
+ expect(typeof response.data).toBe("object");
205
+ });
206
+ });
207
+
208
+ describe("connected account lifecycle (Gmail OAuth)", () => {
209
+ it("should create, get, update, and delete a Gmail connected account with apiConfig", async () => {
210
+ const uniqueId = TestDataGenerator.generateUniqueId();
211
+ const identifier = `test_actions_gmail_${uniqueId}`;
212
+
213
+ const oauthToken = new OauthToken({
214
+ accessToken: "test_access_token_api",
215
+ refreshToken: "test_refresh_token_api",
216
+ scopes: ["read", "write"],
217
+ });
218
+
219
+ const authorizationDetails = new AuthorizationDetails({
220
+ details: {
221
+ case: "oauthToken",
222
+ value: oauthToken,
223
+ },
224
+ });
225
+
226
+ const initialApiConfig = {
227
+ version: "v1.0",
228
+ domain: "gmail.com",
229
+ api_endpoint: "https://gmail.googleapis.com",
230
+ custom_auth_header: "Bearer",
231
+ };
232
+
233
+ // Create
234
+ const createResponse = await client.actions.createConnectedAccount({
235
+ connectionName: GMAIL_CONNECTION_NAME,
236
+ identifier,
237
+ authorizationDetails,
238
+ apiConfig: initialApiConfig,
239
+ });
240
+
241
+ expect(createResponse.connectedAccount).toBeDefined();
242
+ const created = createResponse.connectedAccount!;
243
+ expect(created.identifier).toBe(identifier);
244
+ expect(created.apiConfig).toBeDefined();
245
+
246
+ // Get via actions wrapper
247
+ const getResponse = await client.actions.getConnectedAccount({
248
+ connectionName: GMAIL_CONNECTION_NAME,
249
+ identifier,
250
+ });
251
+
252
+ expect(getResponse.connectedAccount).toBeDefined();
253
+ expect(getResponse.connectedAccount!.identifier).toBe(identifier);
254
+
255
+ // Update apiConfig
256
+ const updatedApiConfig = {
257
+ version: "v2.0",
258
+ domain: "updated.gmail.com",
259
+ api_endpoint: "https://updated.gmail.googleapis.com",
260
+ custom_auth_header: "Updated Bearer",
261
+ };
262
+
263
+ const updateResponse = await client.actions.updateConnectedAccount({
264
+ connectionName: GMAIL_CONNECTION_NAME,
265
+ identifier,
266
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
267
+ apiConfig: updatedApiConfig as any,
268
+ });
269
+
270
+ expect(updateResponse.connectedAccount).toBeDefined();
271
+ const updated = updateResponse.connectedAccount!;
272
+ expect(updated.identifier).toBe(identifier);
273
+ expect(updated.apiConfig).toBeDefined();
274
+
275
+ // Delete via actions wrapper
276
+ const deleteResponse = await client.actions.deleteConnectedAccount({
277
+ connectionName: GMAIL_CONNECTION_NAME,
278
+ identifier,
279
+ });
280
+ expect(deleteResponse).toBeDefined();
281
+ });
282
+ });
283
+
284
+ describe("connected account lifecycle (Freshdesk static auth)", () => {
285
+ it("should create, update, and delete a Freshdesk connected account with static auth", async () => {
286
+ const uniqueId = TestDataGenerator.generateUniqueId();
287
+ const identifier = `test_actions_freshdesk_${uniqueId}`;
288
+
289
+ // Initial static auth
290
+ const initialStaticAuth = {
291
+ static_auth: {
292
+ domain: "initial.freshdesk.com",
293
+ username: "initial_user",
294
+ password: "initial_password",
295
+ },
296
+ };
297
+
298
+ try {
299
+ // Create Freshdesk account
300
+ const createResponse = await client.actions.createConnectedAccount({
301
+ connectionName: FRESHDESK_CONNECTION_NAME,
302
+ identifier,
303
+ // For Node, authorizationDetails is carried through as a generic value; we rely
304
+ // on the backend to interpret the structure.
305
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
306
+ authorizationDetails: initialStaticAuth as any,
307
+ });
308
+
309
+ expect(createResponse.connectedAccount).toBeDefined();
310
+ const created = createResponse.connectedAccount!;
311
+ expect(created.identifier).toBe(identifier);
312
+
313
+ // Update static auth
314
+ const updatedStaticAuth = {
315
+ static_auth: {
316
+ domain: "updated.freshdesk.com",
317
+ username: "updated_user",
318
+ password: "updated_password",
319
+ },
320
+ };
321
+
322
+ const updateResponse = await client.actions.updateConnectedAccount({
323
+ connectionName: FRESHDESK_CONNECTION_NAME,
324
+ identifier,
325
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
326
+ authorizationDetails: updatedStaticAuth as any,
327
+ });
328
+
329
+ expect(updateResponse.connectedAccount).toBeDefined();
330
+ const updated = updateResponse.connectedAccount!;
331
+ expect(updated.identifier).toBe(identifier);
332
+
333
+ // Best-effort check: authorizationDetails should be present
334
+ expect(updated.authorizationDetails).toBeDefined();
335
+
336
+ // Delete Freshdesk account
337
+ const deleteResponse = await client.actions.deleteConnectedAccount({
338
+ connectionName: FRESHDESK_CONNECTION_NAME,
339
+ identifier,
340
+ });
341
+ expect(deleteResponse).toBeDefined();
342
+ } catch (error: any) {
343
+ // If the Freshdesk connector is not configured in this environment,
344
+ // treat this as a skipped integration scenario rather than a hard failure.
345
+ if (
346
+ error?.name === "ScalekitNotFoundException" ||
347
+ /connection not found/i.test(String(error.message ?? error))
348
+ ) {
349
+ return;
350
+ }
351
+ throw error;
352
+ }
353
+ });
354
+ });
355
+ });
356
+