@superblocksteam/sabs-client 0.0.1-demo-databricks-deploy

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,953 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const sabs_types_1 = require("@superblocksteam/sabs-types");
37
+ const axios_1 = __importStar(require("axios"));
38
+ const sabs_1 = require("./sabs");
39
+ describe('sabs service', () => {
40
+ let anyBuildKey;
41
+ let anyAccessToken;
42
+ beforeEach(() => {
43
+ anyBuildKey = 'any-secret-build-key';
44
+ anyAccessToken = 'any-access-token';
45
+ });
46
+ afterEach(() => {
47
+ jest.restoreAllMocks();
48
+ });
49
+ describe('build', () => {
50
+ test.each([{ accessToken: 'anyScopedJwt', expectedHeaders: { Authorization: 'Bearer anyScopedJwt' } }])('returns expected response with accessToken=$accessToken', async ({ accessToken, expectedHeaders }) => {
51
+ const expectedBuildId = 'expectedBuildId';
52
+ const expectedCreated = new Date();
53
+ const expectedUpdated = new Date();
54
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
55
+ mockAxios.mockResolvedValue({
56
+ data: {
57
+ buildId: expectedBuildId,
58
+ created: expectedCreated,
59
+ updated: expectedUpdated
60
+ }
61
+ });
62
+ const anyDirectoryHash = 'anyDirectoryHash';
63
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
64
+ id: 'anyApplicationId',
65
+ organizationId: 'anyOrganizationId'
66
+ });
67
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
68
+ const result = await sabs.build({
69
+ directoryHash: anyDirectoryHash,
70
+ meta: anyApplicationMetadata,
71
+ buildKey: anyBuildKey,
72
+ accessToken
73
+ });
74
+ expect(result).toEqual({
75
+ buildId: expectedBuildId,
76
+ created: expectedCreated,
77
+ updated: expectedUpdated
78
+ });
79
+ expect(mockAxios).toHaveBeenCalledWith({
80
+ method: 'POST',
81
+ url: 'http://localhost:3000/v1/builds',
82
+ headers: expectedHeaders,
83
+ data: {
84
+ directoryHash: anyDirectoryHash,
85
+ applicationMetadata: anyApplicationMetadata,
86
+ buildKey: anyBuildKey
87
+ }
88
+ });
89
+ });
90
+ test('raises error when request fails', async () => {
91
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
92
+ mockAxios.mockRejectedValue(new Error('any error'));
93
+ const anyDirectoryHash = 'anyDirectoryHash';
94
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
95
+ id: 'anyApplicationId',
96
+ organizationId: 'anyOrganizationId'
97
+ });
98
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
99
+ await expect(sabs.build({ directoryHash: anyDirectoryHash, meta: anyApplicationMetadata, buildKey: anyBuildKey, accessToken: anyAccessToken })).rejects.toThrow();
100
+ expect(mockAxios).toHaveBeenCalledWith({
101
+ method: 'POST',
102
+ url: 'http://localhost:3000/v1/builds',
103
+ headers: { Authorization: `Bearer ${anyAccessToken}` },
104
+ data: {
105
+ directoryHash: anyDirectoryHash,
106
+ applicationMetadata: anyApplicationMetadata,
107
+ buildKey: anyBuildKey
108
+ }
109
+ });
110
+ });
111
+ test('raises error when directory hash is empty', async () => {
112
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
113
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
114
+ id: 'anyApplicationId',
115
+ organizationId: 'anyOrganizationId'
116
+ });
117
+ await expect(sabs.build({ directoryHash: '', meta: anyApplicationMetadata, buildKey: anyBuildKey })).rejects.toThrow();
118
+ });
119
+ test('raises error when application metadata is empty', async () => {
120
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
121
+ await expect(sabs.build({
122
+ directoryHash: '',
123
+ meta: undefined,
124
+ buildKey: anyBuildKey,
125
+ accessToken: anyAccessToken
126
+ })).rejects.toThrow();
127
+ });
128
+ test('raises error when build key is empty', async () => {
129
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
130
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
131
+ id: 'anyApplicationId',
132
+ organizationId: 'anyOrganizationId'
133
+ });
134
+ await expect(sabs.build({ directoryHash: 'anyDirectoryHash', meta: anyApplicationMetadata, buildKey: '', accessToken: anyAccessToken })).rejects.toThrow();
135
+ });
136
+ test('raises error when application id is empty', async () => {
137
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
138
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
139
+ id: '',
140
+ organizationId: 'anyOrganizationId'
141
+ });
142
+ await expect(sabs.build({ directoryHash: 'anyDirectoryHash', meta: anyApplicationMetadata, buildKey: anyBuildKey, accessToken: anyAccessToken })).rejects.toThrow();
143
+ });
144
+ test('raises error when organization id is empty', async () => {
145
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
146
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
147
+ id: 'anyApplicationId',
148
+ organizationId: ''
149
+ });
150
+ await expect(sabs.build({ directoryHash: 'anyDirectoryHash', meta: anyApplicationMetadata, buildKey: anyBuildKey, accessToken: anyAccessToken })).rejects.toThrow();
151
+ });
152
+ test('raises error when access token is empty', async () => {
153
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
154
+ const anyApplicationMetadata = new sabs_types_1.ApplicationMetadata({
155
+ id: 'anyApplicationId',
156
+ organizationId: 'anyOrganizationId'
157
+ });
158
+ await expect(sabs.build({ directoryHash: 'anyDirectoryHash', meta: anyApplicationMetadata, buildKey: anyBuildKey, accessToken: '' })).rejects.toThrow();
159
+ });
160
+ });
161
+ describe('status', () => {
162
+ test.each([{ accessToken: 'anyScopedJwt', expectedHeaders: { Authorization: 'Bearer anyScopedJwt' } }])('returns expected response with accessToken=$accessToken', async ({ accessToken, expectedHeaders }) => {
163
+ const expectedBuildId = 'expectedBuildId';
164
+ const expectedStatus = sabs_types_1.BuildStatus.SUCCESS;
165
+ const expectedCreated = new Date();
166
+ const expectedUpdated = new Date();
167
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
168
+ mockAxios.mockResolvedValue({
169
+ data: {
170
+ buildId: expectedBuildId,
171
+ status: expectedStatus,
172
+ created: expectedCreated,
173
+ updated: expectedUpdated
174
+ }
175
+ });
176
+ const anyBuildId = 'anyBuildId';
177
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
178
+ const result = await sabs.status({ buildId: anyBuildId, accessToken });
179
+ expect(result).toEqual({
180
+ buildId: expectedBuildId,
181
+ status: expectedStatus,
182
+ created: expectedCreated,
183
+ updated: expectedUpdated
184
+ });
185
+ expect(mockAxios).toHaveBeenCalledWith({
186
+ method: 'GET',
187
+ url: `http://localhost:3000/v1/builds/${anyBuildId}`,
188
+ headers: expectedHeaders
189
+ });
190
+ });
191
+ test('raises error when request fails', async () => {
192
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
193
+ mockAxios.mockRejectedValue(new Error('any error'));
194
+ const anyBuildId = 'anyBuildId';
195
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
196
+ await expect(sabs.status({ buildId: anyBuildId, accessToken: anyAccessToken })).rejects.toThrow();
197
+ expect(mockAxios).toHaveBeenCalledWith({
198
+ headers: { Authorization: `Bearer ${anyAccessToken}` },
199
+ method: 'GET',
200
+ url: `http://localhost:3000/v1/builds/${anyBuildId}`
201
+ });
202
+ });
203
+ test('raises error when build id is empty', async () => {
204
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
205
+ await expect(sabs.status({ buildId: '', accessToken: anyAccessToken })).rejects.toThrow();
206
+ });
207
+ test('raises error when access token is empty', async () => {
208
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
209
+ await expect(sabs.status({ buildId: 'anyBuildId', accessToken: '' })).rejects.toThrow();
210
+ });
211
+ });
212
+ describe('list', () => {
213
+ test.each([{ accessToken: 'anyScopedJwt', expectedHeaders: { Authorization: 'Bearer anyScopedJwt' } }])('returns expected response with accessToken=$accessToken', async ({ accessToken, expectedHeaders }) => {
214
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
215
+ mockAxios.mockResolvedValue({
216
+ data: {
217
+ builds: [
218
+ {
219
+ buildId: 'id1',
220
+ status: sabs_types_1.BuildStatus.SUCCESS,
221
+ created: new Date('2023-01-01'),
222
+ updated: new Date('2023-01-02')
223
+ },
224
+ {
225
+ buildId: 'id2',
226
+ status: sabs_types_1.BuildStatus.RUNNING,
227
+ created: new Date('2023-01-03'),
228
+ updated: new Date('2023-01-04')
229
+ },
230
+ {
231
+ buildId: 'id3',
232
+ status: sabs_types_1.BuildStatus.FAILED,
233
+ error: 'Build failed',
234
+ created: new Date('2023-01-05'),
235
+ updated: new Date('2023-01-06')
236
+ }
237
+ ]
238
+ }
239
+ });
240
+ const anyOrganizationId = 'anyOrganizationId';
241
+ const anyApplicationId = 'anyApplicationId';
242
+ const anyDirectoryHash = 'anyDirectoryHash';
243
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
244
+ const result = await sabs.list({
245
+ organizationId: anyOrganizationId,
246
+ applicationId: anyApplicationId,
247
+ directoryHash: anyDirectoryHash,
248
+ accessToken
249
+ });
250
+ expect(result).toEqual({
251
+ builds: [
252
+ {
253
+ buildId: 'id1',
254
+ status: sabs_types_1.BuildStatus.SUCCESS,
255
+ created: new Date('2023-01-01'),
256
+ updated: new Date('2023-01-02')
257
+ },
258
+ {
259
+ buildId: 'id2',
260
+ status: sabs_types_1.BuildStatus.RUNNING,
261
+ created: new Date('2023-01-03'),
262
+ updated: new Date('2023-01-04')
263
+ },
264
+ {
265
+ buildId: 'id3',
266
+ status: sabs_types_1.BuildStatus.FAILED,
267
+ error: 'Build failed',
268
+ created: new Date('2023-01-05'),
269
+ updated: new Date('2023-01-06')
270
+ }
271
+ ]
272
+ });
273
+ expect(mockAxios).toHaveBeenCalledWith({
274
+ method: 'GET',
275
+ url: `http://localhost:3000/v1/build`,
276
+ headers: expectedHeaders,
277
+ params: {
278
+ organizationId: anyOrganizationId,
279
+ applicationId: anyApplicationId,
280
+ directoryHash: anyDirectoryHash
281
+ }
282
+ });
283
+ });
284
+ test('raises error when request fails', async () => {
285
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
286
+ mockAxios.mockRejectedValue(new Error('any error'));
287
+ const anyOrganizationId = 'anyOrganizationId';
288
+ const anyApplicationId = 'anyApplicationId';
289
+ const anyDirectoryHash = 'anyDirectoryHash';
290
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
291
+ await expect(sabs.list({
292
+ organizationId: anyOrganizationId,
293
+ applicationId: anyApplicationId,
294
+ directoryHash: anyDirectoryHash,
295
+ accessToken: anyAccessToken
296
+ })).rejects.toThrow();
297
+ expect(mockAxios).toHaveBeenCalledWith({
298
+ method: 'GET',
299
+ url: `http://localhost:3000/v1/build`,
300
+ headers: { Authorization: `Bearer ${anyAccessToken}` },
301
+ params: {
302
+ organizationId: anyOrganizationId,
303
+ applicationId: anyApplicationId,
304
+ directoryHash: anyDirectoryHash
305
+ }
306
+ });
307
+ });
308
+ test('raises error when organization id is empty', async () => {
309
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
310
+ await expect(sabs.list({ organizationId: '', applicationId: 'anyApplicationId', directoryHash: 'anyDirectoryHash', accessToken: anyAccessToken })).rejects.toThrow();
311
+ });
312
+ test('raises error when application id is empty', async () => {
313
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
314
+ await expect(sabs.list({
315
+ organizationId: 'anyOrganizationId',
316
+ applicationId: '',
317
+ directoryHash: 'anyDirectoryHash',
318
+ accessToken: anyAccessToken
319
+ })).rejects.toThrow();
320
+ });
321
+ test('raises error when directory hash is empty', async () => {
322
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
323
+ await expect(sabs.list({
324
+ organizationId: 'anyOrganizationId',
325
+ applicationId: 'anyApplicationId',
326
+ directoryHash: '',
327
+ accessToken: anyAccessToken
328
+ })).rejects.toThrow();
329
+ });
330
+ test('raises error when access token is empty', async () => {
331
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
332
+ await expect(sabs.list({
333
+ organizationId: 'anyOrganizationId',
334
+ applicationId: 'anyApplicationId',
335
+ directoryHash: 'anyDirectoryHash',
336
+ accessToken: ''
337
+ })).rejects.toThrow();
338
+ });
339
+ });
340
+ describe('terminate', () => {
341
+ test.each([{ accessToken: 'anyScopedJwt', expectedHeaders: { Authorization: 'Bearer anyScopedJwt' } }])('returns expected response with accessToken=$accessToken', async ({ accessToken, expectedHeaders }) => {
342
+ const expectedBuildId = 'expectedBuildId';
343
+ const expectedStatus = sabs_types_1.BuildStatus.TIMED_OUT;
344
+ const expectedError = 'build timed out';
345
+ const expectedCreated = new Date();
346
+ const expectedUpdated = new Date();
347
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
348
+ mockAxios.mockResolvedValue({
349
+ data: {
350
+ buildId: expectedBuildId,
351
+ status: expectedStatus,
352
+ error: expectedError,
353
+ created: expectedCreated,
354
+ updated: expectedUpdated
355
+ }
356
+ });
357
+ const anyBuildId = 'anyBuildId';
358
+ const anyStatus = sabs_types_1.BuildStatus.TIMED_OUT;
359
+ const anyError = 'build timed out';
360
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
361
+ const result = await sabs.terminate({
362
+ buildId: anyBuildId,
363
+ status: anyStatus,
364
+ buildKey: anyBuildKey,
365
+ error: anyError,
366
+ accessToken
367
+ });
368
+ expect(result).toEqual({
369
+ buildId: expectedBuildId,
370
+ status: expectedStatus,
371
+ error: expectedError,
372
+ created: expectedCreated,
373
+ updated: expectedUpdated
374
+ });
375
+ expect(mockAxios).toHaveBeenCalledWith({
376
+ method: 'POST',
377
+ url: `http://localhost:3000/v1/builds/${anyBuildId}/terminate`,
378
+ headers: expectedHeaders,
379
+ data: {
380
+ buildId: anyBuildId,
381
+ status: anyStatus,
382
+ error: anyError,
383
+ buildKey: anyBuildKey
384
+ }
385
+ });
386
+ });
387
+ test('raises error when request fails', async () => {
388
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
389
+ mockAxios.mockRejectedValue(new Error('any error'));
390
+ const anyBuildId = 'anyBuildId';
391
+ const anyStatus = sabs_types_1.BuildStatus.TIMED_OUT;
392
+ const anyError = 'build timed out';
393
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
394
+ await expect(sabs.terminate({ buildId: anyBuildId, status: anyStatus, buildKey: anyBuildKey, error: anyError, accessToken: anyAccessToken })).rejects.toThrow();
395
+ expect(mockAxios).toHaveBeenCalledWith({
396
+ method: 'POST',
397
+ headers: { Authorization: `Bearer ${anyAccessToken}` },
398
+ url: `http://localhost:3000/v1/builds/${anyBuildId}/terminate`,
399
+ data: {
400
+ buildId: anyBuildId,
401
+ status: anyStatus,
402
+ error: anyError,
403
+ buildKey: anyBuildKey
404
+ }
405
+ });
406
+ });
407
+ test('raises error when build id is empty', async () => {
408
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
409
+ await expect(sabs.terminate({
410
+ buildId: '',
411
+ status: sabs_types_1.BuildStatus.TIMED_OUT,
412
+ buildKey: anyBuildKey,
413
+ error: 'build timed out',
414
+ accessToken: anyAccessToken
415
+ })).rejects.toThrow();
416
+ });
417
+ test('raises error when status is empty', async () => {
418
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
419
+ await expect(sabs.terminate({
420
+ buildId: 'anyBuildId',
421
+ status: undefined,
422
+ buildKey: anyBuildKey,
423
+ error: 'build timed out',
424
+ accessToken: anyAccessToken
425
+ })).rejects.toThrow();
426
+ });
427
+ test('raises error when build key is empty', async () => {
428
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
429
+ await expect(sabs.terminate({
430
+ buildId: 'anyBuildId',
431
+ status: sabs_types_1.BuildStatus.TIMED_OUT,
432
+ buildKey: '',
433
+ error: 'build timed out',
434
+ accessToken: anyAccessToken
435
+ })).rejects.toThrow();
436
+ });
437
+ test('raises error when access token is empty', async () => {
438
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
439
+ await expect(sabs.terminate({
440
+ buildId: 'anyBuildId',
441
+ status: sabs_types_1.BuildStatus.TIMED_OUT,
442
+ buildKey: anyBuildKey,
443
+ error: 'build timed out',
444
+ accessToken: ''
445
+ })).rejects.toThrow();
446
+ });
447
+ });
448
+ describe('bulkStatus', () => {
449
+ test('returns expected response', async () => {
450
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
451
+ mockAxios.mockResolvedValue({
452
+ data: {
453
+ builds: [
454
+ {
455
+ buildId: 'build1',
456
+ status: sabs_types_1.BuildStatus.SUCCESS,
457
+ created: new Date('2023-01-01'),
458
+ updated: new Date('2023-01-02')
459
+ },
460
+ {
461
+ buildId: 'build2',
462
+ status: sabs_types_1.BuildStatus.RUNNING,
463
+ created: new Date('2023-01-03'),
464
+ updated: new Date('2023-01-04')
465
+ }
466
+ ]
467
+ }
468
+ });
469
+ const anyOrganizationId = 'anyOrganizationId';
470
+ const anyApplicationId = 'anyApplicationId';
471
+ const anyDirectoryHashes = ['hash1', 'hash2'];
472
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
473
+ const result = await sabs.bulkStatus({
474
+ organizationId: anyOrganizationId,
475
+ applicationId: anyApplicationId,
476
+ directoryHashes: anyDirectoryHashes,
477
+ accessToken: anyAccessToken
478
+ });
479
+ expect(result).toEqual({
480
+ builds: [
481
+ {
482
+ buildId: 'build1',
483
+ status: sabs_types_1.BuildStatus.SUCCESS,
484
+ created: new Date('2023-01-01'),
485
+ updated: new Date('2023-01-02')
486
+ },
487
+ {
488
+ buildId: 'build2',
489
+ status: sabs_types_1.BuildStatus.RUNNING,
490
+ created: new Date('2023-01-03'),
491
+ updated: new Date('2023-01-04')
492
+ }
493
+ ]
494
+ });
495
+ expect(mockAxios).toHaveBeenCalledWith({
496
+ method: 'POST',
497
+ headers: { Authorization: `Bearer ${anyAccessToken}` },
498
+ url: `http://localhost:3000/v1/builds/${anyOrganizationId}/${anyApplicationId}/bulk-status`,
499
+ data: {
500
+ organizationId: anyOrganizationId,
501
+ applicationId: anyApplicationId,
502
+ directoryHashes: anyDirectoryHashes
503
+ }
504
+ });
505
+ });
506
+ test('raises error when access token is empty', async () => {
507
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
508
+ await expect(sabs.bulkStatus({
509
+ organizationId: 'anyOrganizationId',
510
+ applicationId: 'anyApplicationId',
511
+ directoryHashes: ['anyDirectoryHash'],
512
+ accessToken: ''
513
+ })).rejects.toThrow();
514
+ });
515
+ test('raises error when organization id is empty', async () => {
516
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
517
+ await expect(sabs.bulkStatus({
518
+ organizationId: '',
519
+ applicationId: 'anyApplicationId',
520
+ directoryHashes: ['anyDirectoryHash'],
521
+ accessToken: anyAccessToken
522
+ })).rejects.toThrow();
523
+ });
524
+ test('raises error when application id is empty', async () => {
525
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
526
+ await expect(sabs.bulkStatus({
527
+ organizationId: 'anyOrganizationId',
528
+ applicationId: '',
529
+ directoryHashes: ['anyDirectoryHash'],
530
+ accessToken: anyAccessToken
531
+ })).rejects.toThrow();
532
+ });
533
+ test('raises error when directory hashes is empty', async () => {
534
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
535
+ await expect(sabs.bulkStatus({
536
+ organizationId: 'anyOrganizationId',
537
+ applicationId: 'anyApplicationId',
538
+ directoryHashes: [],
539
+ accessToken: anyAccessToken
540
+ })).rejects.toThrow();
541
+ });
542
+ });
543
+ describe('liveEdit', () => {
544
+ const applicationId = 'anyApplicationId';
545
+ const organizationId = 'anyOrganizationId';
546
+ const branch = 'anyBranch';
547
+ test('raises error when application id is empty', async () => {
548
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
549
+ await expect(sabs.createLiveEdit({
550
+ applicationId: '',
551
+ organizationId: 'anyOrganizationId',
552
+ branch: 'anyBranch',
553
+ expiresIn: 1000,
554
+ accessToken: anyAccessToken
555
+ })).rejects.toThrow();
556
+ });
557
+ test('raises error when organization id is empty', async () => {
558
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
559
+ await expect(sabs.createLiveEdit({
560
+ applicationId: 'anyApplicationId',
561
+ organizationId: '',
562
+ branch: 'anyBranch',
563
+ expiresIn: 1000,
564
+ accessToken: anyAccessToken
565
+ })).rejects.toThrow();
566
+ });
567
+ test('raises error when branch is empty', async () => {
568
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
569
+ await expect(sabs.createLiveEdit({
570
+ applicationId: 'anyApplicationId',
571
+ organizationId: 'anyOrganizationId',
572
+ branch: '',
573
+ expiresIn: 1000,
574
+ accessToken: anyAccessToken
575
+ })).rejects.toThrow();
576
+ });
577
+ test('raises error when access token is empty', async () => {
578
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
579
+ await expect(sabs.createLiveEdit({
580
+ applicationId: 'anyApplicationId',
581
+ organizationId: 'anyOrganizationId',
582
+ branch: 'anyBranch',
583
+ expiresIn: 1000,
584
+ accessToken: ''
585
+ })).rejects.toThrow();
586
+ });
587
+ test('createLiveEdit', async () => {
588
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
589
+ const expiresInSeconds = 1000;
590
+ const now = Date.now();
591
+ const expectedRequest = {
592
+ application: {
593
+ applicationId,
594
+ organizationId,
595
+ branch
596
+ },
597
+ expiresIn: BigInt(expiresInSeconds),
598
+ sessionJwt: anyAccessToken
599
+ };
600
+ const newLiveEditResponse = new sabs_types_1.CreateLiveEditResponse({
601
+ liveEditId: 'liveEditId',
602
+ liveEditUrl: 'http://localhost:3000/live-edit/liveEditId',
603
+ application: {
604
+ applicationId,
605
+ organizationId,
606
+ branch
607
+ },
608
+ expiresAt: BigInt(now + expiresInSeconds * 1000)
609
+ });
610
+ mockAxios.mockResolvedValue({ data: newLiveEditResponse });
611
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
612
+ const result = await sabs.createLiveEdit({
613
+ applicationId,
614
+ organizationId,
615
+ branch,
616
+ expiresIn: expiresInSeconds,
617
+ accessToken: anyAccessToken
618
+ });
619
+ expect(result).toEqual(newLiveEditResponse);
620
+ expect(mockAxios).toHaveBeenCalledWith({
621
+ method: 'POST',
622
+ url: 'http://localhost:3000/v1/live-edit',
623
+ data: expectedRequest,
624
+ headers: {
625
+ Authorization: `Bearer ${anyAccessToken}`
626
+ }
627
+ });
628
+ });
629
+ test('terminateLiveEdit', async () => {
630
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
631
+ const liveEditId = 'liveEditId';
632
+ const expectedRequest = { liveEditId };
633
+ const expectedCreated = new Date();
634
+ const expectedUpdated = new Date();
635
+ const expectedResponse = {
636
+ liveEditId,
637
+ status: sabs_types_1.LiveEditStatus.TERMINATED,
638
+ application: {
639
+ applicationId,
640
+ organizationId,
641
+ branch
642
+ },
643
+ created: expectedCreated,
644
+ updated: expectedUpdated
645
+ };
646
+ mockAxios.mockResolvedValue({ data: expectedResponse });
647
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
648
+ const result = await sabs.terminateLiveEdit({ liveEditId, accessToken: anyAccessToken });
649
+ expect(result).toEqual(expectedResponse);
650
+ expect(mockAxios).toHaveBeenCalledWith({
651
+ method: 'POST',
652
+ url: `http://localhost:3000/v1/live-edit/${liveEditId}/terminate`,
653
+ data: expectedRequest,
654
+ headers: {
655
+ Authorization: `Bearer ${anyAccessToken}`
656
+ }
657
+ });
658
+ });
659
+ test('raises error when live edit id is empty', async () => {
660
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
661
+ await expect(sabs.terminateLiveEdit({ liveEditId: '', accessToken: anyAccessToken })).rejects.toThrow();
662
+ });
663
+ test('raises error when access token is empty', async () => {
664
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
665
+ await expect(sabs.terminateLiveEdit({ liveEditId: 'liveEditId', accessToken: '' })).rejects.toThrow();
666
+ });
667
+ test('terminateLiveEdit raises error when request fails', async () => {
668
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
669
+ mockAxios.mockRejectedValue(new Error('any error'));
670
+ const liveEditId = 'liveEditId';
671
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
672
+ await expect(sabs.terminateLiveEdit({ liveEditId, accessToken: anyAccessToken })).rejects.toThrow();
673
+ expect(mockAxios).toHaveBeenCalledWith({
674
+ method: 'POST',
675
+ url: `http://localhost:3000/v1/live-edit/${liveEditId}/terminate`,
676
+ data: {
677
+ liveEditId
678
+ },
679
+ headers: {
680
+ Authorization: `Bearer ${anyAccessToken}`
681
+ }
682
+ });
683
+ });
684
+ });
685
+ describe('executeRequest', () => {
686
+ test('raies expected error when error is axios error', async () => {
687
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
688
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('request failed', 'internal', undefined, undefined, {
689
+ headers: {},
690
+ config: {
691
+ headers: new axios_1.AxiosHeaders()
692
+ },
693
+ status: 500,
694
+ statusText: 'internal server error',
695
+ data: 'failed to process request'
696
+ }));
697
+ const anyBuildId = 'anyBuildId';
698
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
699
+ await expect(sabs.status({ buildId: anyBuildId, accessToken: anyAccessToken })).rejects.toThrow();
700
+ });
701
+ test('re-raises error when error is not axios error', async () => {
702
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
703
+ mockAxios.mockRejectedValue(new Error('unexpected error'));
704
+ const anyBuildId = 'anyBuildId';
705
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
706
+ await expect(sabs.status({ buildId: anyBuildId, accessToken: anyAccessToken })).rejects.toThrow();
707
+ });
708
+ });
709
+ describe('backward compatibility', () => {
710
+ test('errors can be caught as generic Error instances (backward compatibility)', async () => {
711
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
712
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Service unavailable', 'ECONNREFUSED', undefined, undefined, {
713
+ headers: {},
714
+ config: {
715
+ headers: new axios_1.AxiosHeaders()
716
+ },
717
+ status: 503,
718
+ statusText: 'Service Unavailable',
719
+ data: { message: 'Server temporarily unavailable' }
720
+ }));
721
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
722
+ const applicationId = 'test-app';
723
+ const organizationId = 'test-org';
724
+ const directoryHash = 'abc123';
725
+ // This simulates how existing applications might handle errors
726
+ try {
727
+ const result = await sabs.build({
728
+ directoryHash,
729
+ meta: new sabs_types_1.ApplicationMetadata({
730
+ id: applicationId,
731
+ organizationId
732
+ }),
733
+ buildKey: anyBuildKey,
734
+ accessToken: anyAccessToken
735
+ });
736
+ // Should not reach here
737
+ expect(result).toBeUndefined();
738
+ }
739
+ catch (error) {
740
+ // Legacy error handling - should still work with new error types
741
+ expect(error).toBeInstanceOf(Error);
742
+ expect(error.message).toContain('SABS API Error (503)');
743
+ // Verify we can still access basic Error properties
744
+ expect(typeof error.message).toBe('string');
745
+ expect(error.name).toBeDefined();
746
+ // This is how existing code might log and re-throw
747
+ const loggedError = {
748
+ error,
749
+ applicationId,
750
+ organizationId,
751
+ directoryHash,
752
+ message: error.message
753
+ };
754
+ expect(loggedError.error).toBe(error);
755
+ expect(loggedError.message).toBe(error.message);
756
+ // Re-throwing as generic Error should work
757
+ expect(() => {
758
+ throw new Error('Unable to launch build');
759
+ }).toThrow('Unable to launch build');
760
+ }
761
+ });
762
+ test('new error types provide additional functionality when accessed', async () => {
763
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
764
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Unauthorized', 'UNAUTHORIZED', undefined, undefined, {
765
+ headers: {},
766
+ config: {
767
+ headers: new axios_1.AxiosHeaders()
768
+ },
769
+ status: 401,
770
+ statusText: 'Unauthorized',
771
+ data: { message: 'Invalid token' }
772
+ }));
773
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
774
+ try {
775
+ await sabs.status({ buildId: 'test-build', accessToken: 'invalid-token' });
776
+ }
777
+ catch (error) {
778
+ // Backward compatible - can still catch as Error
779
+ expect(error).toBeInstanceOf(Error);
780
+ // But applications can now also check for specific error types
781
+ const { UnauthorizedError } = await Promise.resolve().then(() => __importStar(require('./errors')));
782
+ expect(error).toBeInstanceOf(UnauthorizedError);
783
+ // And access the status code if needed
784
+ if ('status' in error) {
785
+ expect(error.status).toBe(401);
786
+ }
787
+ }
788
+ });
789
+ describe('response data handling', () => {
790
+ test('handles responseData with message property', async () => {
791
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
792
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Bad Request', 'BAD_REQUEST', undefined, undefined, {
793
+ headers: {},
794
+ config: {
795
+ headers: new axios_1.AxiosHeaders()
796
+ },
797
+ status: 400,
798
+ statusText: 'Bad Request',
799
+ data: { message: 'Directory hash is required' }
800
+ }));
801
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
802
+ try {
803
+ await sabs.build({
804
+ directoryHash: 'test', // Use non-empty value to avoid client-side validation
805
+ meta: new sabs_types_1.ApplicationMetadata({ id: 'test', organizationId: 'test' }),
806
+ buildKey: 'test',
807
+ accessToken: 'test'
808
+ });
809
+ }
810
+ catch (error) {
811
+ expect(error).toBeInstanceOf(Error);
812
+ expect(error.message).toBe('SABS API Error (400): Directory hash is required');
813
+ }
814
+ });
815
+ test('handles responseData as string', async () => {
816
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
817
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Internal Server Error', 'INTERNAL_SERVER_ERROR', undefined, undefined, {
818
+ headers: {},
819
+ config: {
820
+ headers: new axios_1.AxiosHeaders()
821
+ },
822
+ status: 500,
823
+ statusText: 'Internal Server Error',
824
+ data: 'Database connection failed'
825
+ }));
826
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
827
+ try {
828
+ await sabs.build({
829
+ directoryHash: 'test',
830
+ meta: new sabs_types_1.ApplicationMetadata({ id: 'test', organizationId: 'test' }),
831
+ buildKey: 'test',
832
+ accessToken: 'test'
833
+ });
834
+ }
835
+ catch (error) {
836
+ expect(error).toBeInstanceOf(Error);
837
+ expect(error.message).toBe('SABS API Error (500): Database connection failed');
838
+ }
839
+ });
840
+ test('handles null responseData', async () => {
841
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
842
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Service Unavailable', 'SERVICE_UNAVAILABLE', undefined, undefined, {
843
+ headers: {},
844
+ config: {
845
+ headers: new axios_1.AxiosHeaders()
846
+ },
847
+ status: 503,
848
+ statusText: 'Service Unavailable',
849
+ data: null
850
+ }));
851
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
852
+ try {
853
+ await sabs.build({
854
+ directoryHash: 'test',
855
+ meta: new sabs_types_1.ApplicationMetadata({ id: 'test', organizationId: 'test' }),
856
+ buildKey: 'test',
857
+ accessToken: 'test'
858
+ });
859
+ }
860
+ catch (error) {
861
+ expect(error).toBeInstanceOf(Error);
862
+ expect(error.message).toBe('SABS API Error (503): Service Unavailable (503)');
863
+ }
864
+ });
865
+ test('handles undefined responseData', async () => {
866
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
867
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Gateway Timeout', 'GATEWAY_TIMEOUT', undefined, undefined, {
868
+ headers: {},
869
+ config: {
870
+ headers: new axios_1.AxiosHeaders()
871
+ },
872
+ status: 504,
873
+ statusText: 'Gateway Timeout',
874
+ data: undefined
875
+ }));
876
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
877
+ try {
878
+ await sabs.build({
879
+ directoryHash: 'test',
880
+ meta: new sabs_types_1.ApplicationMetadata({ id: 'test', organizationId: 'test' }),
881
+ buildKey: 'test',
882
+ accessToken: 'test'
883
+ });
884
+ }
885
+ catch (error) {
886
+ expect(error).toBeInstanceOf(Error);
887
+ expect(error.message).toBe('SABS API Error (504): Gateway Timeout (504)');
888
+ }
889
+ });
890
+ test('handles responseData object without message property', async () => {
891
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
892
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Bad Request', 'BAD_REQUEST', undefined, undefined, {
893
+ headers: {},
894
+ config: {
895
+ headers: new axios_1.AxiosHeaders()
896
+ },
897
+ status: 400,
898
+ statusText: 'Bad Request',
899
+ data: { error: 'validation_failed', details: ['field is required'] }
900
+ }));
901
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
902
+ try {
903
+ await sabs.build({
904
+ directoryHash: 'test',
905
+ meta: new sabs_types_1.ApplicationMetadata({ id: 'test', organizationId: 'test' }),
906
+ buildKey: 'test',
907
+ accessToken: 'test'
908
+ });
909
+ }
910
+ catch (error) {
911
+ expect(error).toBeInstanceOf(Error);
912
+ expect(error.message).toBe('SABS API Error (400): Bad Request (400)');
913
+ }
914
+ });
915
+ test('all errors have consistent HttpError interface', async () => {
916
+ const responseData = { code: 'TIMEOUT', retryAfter: 30 };
917
+ const mockAxios = jest.spyOn(axios_1.default, 'request');
918
+ mockAxios.mockRejectedValue(new axios_1.AxiosError('Request timeout', 'TIMEOUT', undefined, undefined, {
919
+ headers: {},
920
+ config: {
921
+ headers: new axios_1.AxiosHeaders()
922
+ },
923
+ status: 500,
924
+ statusText: 'Internal Server Error',
925
+ data: responseData
926
+ }));
927
+ const sabs = new sabs_1.SabsClient('http://localhost:3000');
928
+ try {
929
+ await sabs.build({
930
+ directoryHash: 'test',
931
+ meta: new sabs_types_1.ApplicationMetadata({ id: 'test', organizationId: 'test' }),
932
+ buildKey: 'test',
933
+ accessToken: 'test'
934
+ });
935
+ }
936
+ catch (error) {
937
+ expect(error).toBeInstanceOf(Error);
938
+ expect(error.message).toBe('SABS API Error (500): Internal Server Error (500)');
939
+ // All error types now have consistent HttpError interface
940
+ expect('status' in error).toBe(true);
941
+ expect('title' in error).toBe(true);
942
+ if ('status' in error) {
943
+ expect(error.status).toBe(500);
944
+ }
945
+ if ('title' in error) {
946
+ expect(error.title).toBe('Internal server error');
947
+ }
948
+ }
949
+ });
950
+ });
951
+ });
952
+ });
953
+ //# sourceMappingURL=sabs.test.js.map