@prisme.ai/sdk 0.0.2 → 1.0.0

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.
package/lib/api.test.ts CHANGED
@@ -1,14 +1,45 @@
1
1
  import api, { Api } from './api';
2
+ import ApiError from './ApiError';
3
+ import WorkspacesEndpoint from './endpoints/workspaces';
4
+ import WorkspacesVersionsEndpoint from './endpoints/workspacesVersions';
5
+
6
+ jest.mock('./events', () => {
7
+ class Events {
8
+ static mockedConstructor = jest.fn();
9
+ static mockedClose = jest.fn();
10
+ constructor({ api, ...args }: any) {
11
+ // @ts-ignore
12
+ this.api = api;
13
+ Events.mockedConstructor({ api, ...args });
14
+ }
15
+
16
+ once = jest.fn((event: string, cb: Function) => {
17
+ // @ts-ignore
18
+ const shouldFail = this.api._shouldFail;
19
+ if (!shouldFail && event === 'connect') cb();
20
+ if (shouldFail && event !== 'connect') cb();
21
+ return () => null;
22
+ });
23
+ close = Events.mockedClose;
24
+ }
25
+ return { Events };
26
+ });
2
27
 
3
28
  it('should export an instance', () => {
4
29
  expect(api).toBeInstanceOf(Api);
5
30
  });
6
31
 
7
- it('should call /me', () => {
32
+ it('should call /me', async () => {
8
33
  const api = new Api('/fake/');
9
- api.get = jest.fn();
10
- api.me();
34
+ api.get = jest.fn(
35
+ async () =>
36
+ ({
37
+ sessionId: 'session',
38
+ } as any)
39
+ );
40
+ const me = await api.me();
11
41
  expect(api.get).toHaveBeenCalledWith('/me');
42
+ expect(api.user).toBe(me);
12
43
  });
13
44
 
14
45
  it('should call /signin', () => {
@@ -21,15 +52,24 @@ it('should call /signin', () => {
21
52
  });
22
53
  });
23
54
 
55
+ it('should call /login/anonymous', async () => {
56
+ const api = new Api('/fake/');
57
+ api.post = jest.fn();
58
+ const user = await api.createAnonymousSession();
59
+ expect(api.post).toHaveBeenCalledWith('/login/anonymous');
60
+ expect(api.user).toBe(user);
61
+ });
62
+
24
63
  it('should call /signup', () => {
25
64
  const api = new Api('/fake/');
26
65
  api.post = jest.fn();
27
- api.signup('user@fake.com', 'password', 'firstname', 'lastname');
66
+ api.signup('user@fake.com', 'password', 'firstname', 'lastname', 'fr');
28
67
  expect(api.post).toHaveBeenCalledWith('/signup', {
29
68
  email: 'user@fake.com',
30
69
  password: 'password',
31
70
  firstName: 'firstname',
32
71
  lastName: 'lastname',
72
+ language: 'fr',
33
73
  });
34
74
  });
35
75
 
@@ -45,7 +85,7 @@ it('should call get /workspaces', () => {
45
85
  const api = new Api('/fake/');
46
86
  api.get = jest.fn();
47
87
  api.getWorkspaces();
48
- expect(api.get).toHaveBeenCalledWith('/workspaces?limit=300');
88
+ expect(api.get).toHaveBeenCalledWith('/workspaces?limit=600');
49
89
  });
50
90
 
51
91
  it('should call get /workspaces/42', () => {
@@ -83,22 +123,20 @@ it('should call patch /workspaces/42', async () => {
83
123
  });
84
124
  });
85
125
 
126
+ it('should call delete /workspaces/42', async () => {
127
+ const api = new Api('/fake/');
128
+ api.delete = jest.fn();
129
+ await api.deleteWorkspace('42');
130
+ expect(api.delete).toHaveBeenCalledWith('/workspaces/42');
131
+ });
132
+
86
133
  it('should call post /workspaces/42/automations', () => {
87
134
  const api = new Api('/fake/');
88
135
  api.post = jest.fn();
89
- api.createAutomation(
90
- {
91
- id: '42',
92
- name: 'foo',
93
- automations: {},
94
- createdAt: '',
95
- updatedAt: '',
96
- },
97
- {
98
- name: 'foo',
99
- do: [],
100
- }
101
- );
136
+ api.createAutomation('42', {
137
+ name: 'foo',
138
+ do: [],
139
+ });
102
140
  expect(api.post).toHaveBeenCalledWith('/workspaces/42/automations', {
103
141
  name: 'foo',
104
142
  do: [],
@@ -108,20 +146,10 @@ it('should call post /workspaces/42/automations', () => {
108
146
  it('should call patch /workspaces/42/automations', async () => {
109
147
  const api = new Api('/fake/');
110
148
  api.patch = jest.fn();
111
- await api.updateAutomation(
112
- {
113
- id: '42',
114
- name: 'foo',
115
- automations: {},
116
- createdAt: '',
117
- updatedAt: '',
118
- },
119
- '42-1',
120
- {
121
- name: 'foo',
122
- do: [],
123
- }
124
- );
149
+ await api.updateAutomation('42', '42-1', {
150
+ name: 'foo',
151
+ do: [],
152
+ });
125
153
  expect(api.patch).toHaveBeenCalledWith('/workspaces/42/automations/42-1', {
126
154
  name: 'foo',
127
155
  do: [],
@@ -131,16 +159,7 @@ it('should call patch /workspaces/42/automations', async () => {
131
159
  it('should call delete /workspaces/42/automations/42-1', () => {
132
160
  const api = new Api('/fake/');
133
161
  api.delete = jest.fn();
134
- api.deleteAutomation(
135
- {
136
- id: '42',
137
- name: 'foo',
138
- automations: {},
139
- createdAt: '',
140
- updatedAt: '',
141
- },
142
- '42-1'
143
- );
162
+ api.deleteAutomation('42', '42-1');
144
163
  expect(api.delete).toHaveBeenCalledWith('/workspaces/42/automations/42-1');
145
164
  });
146
165
 
@@ -229,3 +248,510 @@ it('should upload file', async () => {
229
248
  expect(body.getAll('file').length).toBe(1);
230
249
  expect((body.getAll('file')[0] as File).name).toBe('foo.jpg');
231
250
  });
251
+
252
+ it('should generate api key', async () => {
253
+ const api = new Api('/fake/');
254
+ api.post = jest.fn((): any => ({
255
+ apiKey: 'api-key',
256
+ }));
257
+ const apiKey = await api.generateApiKey('42', ['event1', 'event2']);
258
+ expect(apiKey).toBe('api-key');
259
+ expect(api.post).toHaveBeenCalledWith('/workspaces/42/apiKeys', {
260
+ rules: {
261
+ events: {
262
+ types: ['event1', 'event2'],
263
+ filters: {
264
+ 'source.sessionId': '${user.sessionId}',
265
+ },
266
+ },
267
+ uploads: undefined,
268
+ },
269
+ });
270
+ (api.post as jest.Mock).mockClear();
271
+
272
+ await api.generateApiKey('42', ['event1', 'event2'], ['images/*']);
273
+ expect(api.post).toHaveBeenCalledWith('/workspaces/42/apiKeys', {
274
+ rules: {
275
+ events: {
276
+ types: ['event1', 'event2'],
277
+ filters: {
278
+ 'source.sessionId': '${user.sessionId}',
279
+ },
280
+ },
281
+ uploads: {
282
+ mimetypes: ['images/*'],
283
+ },
284
+ },
285
+ });
286
+ });
287
+
288
+ it('should update api key', async () => {
289
+ const api = new Api('/fake/');
290
+ api.put = jest.fn((): any => ({
291
+ apiKey: 'api-key',
292
+ }));
293
+ const apiKey = await api.updateApiKey('42', 'api-key', [
294
+ 'event1',
295
+ 'event2',
296
+ 'event3',
297
+ ]);
298
+ expect(apiKey).toBe('api-key');
299
+ expect(api.put).toHaveBeenCalledWith('/workspaces/42/apiKeys/api-key', {
300
+ rules: {
301
+ events: {
302
+ types: ['event1', 'event2', 'event3'],
303
+ filters: {
304
+ 'source.sessionId': '${user.sessionId}',
305
+ },
306
+ uploads: undefined,
307
+ },
308
+ },
309
+ });
310
+
311
+ (api.put as jest.Mock).mockClear();
312
+
313
+ await api.updateApiKey('42', 'api-key', ['event1', 'event2'], ['images/*']);
314
+ expect(api.put).toHaveBeenCalledWith('/workspaces/42/apiKeys/api-key', {
315
+ rules: {
316
+ events: {
317
+ types: ['event1', 'event2'],
318
+ filters: {
319
+ 'source.sessionId': '${user.sessionId}',
320
+ },
321
+ },
322
+ uploads: {
323
+ mimetypes: ['images/*'],
324
+ },
325
+ },
326
+ });
327
+ });
328
+
329
+ it('should send validation mail', () => {
330
+ const api = new Api('/fake/');
331
+ api.post = jest.fn();
332
+ api.sendValidationMail('email', 'fr');
333
+ expect(api.post).toHaveBeenCalledWith('/user/validate', {
334
+ email: 'email',
335
+ language: 'fr',
336
+ });
337
+ });
338
+
339
+ it('should validate mail', () => {
340
+ const api = new Api('/fake/');
341
+ api.post = jest.fn();
342
+ api.validateMail('token');
343
+ expect(api.post).toHaveBeenCalledWith('/user/validate', {
344
+ token: 'token',
345
+ });
346
+ });
347
+
348
+ it('should send password reset mail', () => {
349
+ const api = new Api('/fake/');
350
+ api.post = jest.fn();
351
+ api.sendPasswordResetMail('email', 'fr');
352
+ expect(api.post).toHaveBeenCalledWith('/user/password', {
353
+ email: 'email',
354
+ language: 'fr',
355
+ });
356
+ });
357
+
358
+ it('should reset password', () => {
359
+ const api = new Api('/fake/');
360
+ api.post = jest.fn();
361
+ api.passwordReset('token', 'azerty');
362
+ expect(api.post).toHaveBeenCalledWith('/user/password', {
363
+ token: 'token',
364
+ password: 'azerty',
365
+ });
366
+ });
367
+
368
+ it('should get pages', async () => {
369
+ const api = new Api('/fake/');
370
+ api.get = jest.fn((): any => [
371
+ {
372
+ id: '123',
373
+ createdAt: 'must be removed',
374
+ createdBy: 'must be removed',
375
+ updatedAt: 'must be removed',
376
+ updatedBy: 'must be removed',
377
+ },
378
+ ]);
379
+ const pages = await api.getPages('42');
380
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/pages');
381
+ expect(pages).toEqual([{ id: '123' }]);
382
+
383
+ api.get = jest.fn((): any => {
384
+ throw new Error();
385
+ });
386
+ const empty = await api.getPages('42');
387
+ expect(empty).toEqual([]);
388
+ });
389
+
390
+ it('should get page', () => {
391
+ const api = new Api('/fake/');
392
+ api.get = jest.fn((): any => ({}));
393
+ api.getPage('42', '123');
394
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/pages/123');
395
+ });
396
+
397
+ it('should get page by slug', () => {
398
+ const api = new Api('/fake/');
399
+ api.get = jest.fn((): any => ({}));
400
+ api.getPageBySlug('42', '123');
401
+ expect(api.get).toHaveBeenCalledWith('/pages/42/123');
402
+ });
403
+
404
+ it('should create page', () => {
405
+ const api = new Api('/fake/');
406
+ api.post = jest.fn((): any => ({}));
407
+ api.createPage('42', {} as Prismeai.Page);
408
+ expect(api.post).toHaveBeenCalledWith(`/workspaces/42/pages`, {});
409
+ });
410
+
411
+ it('should update page', async () => {
412
+ const api = new Api('/fake/');
413
+ api.patch = jest.fn((): any => ({ slug: 'my-page' }));
414
+ await api.updatePage('42', { id: '123', slug: 'my-page' } as Prismeai.Page);
415
+ expect(api.patch).toHaveBeenCalledWith(`/workspaces/42/pages/my-page`, {
416
+ id: '123',
417
+ slug: 'my-page',
418
+ });
419
+ });
420
+
421
+ it('should delete page', () => {
422
+ const api = new Api('/fake/');
423
+ api.delete = jest.fn((): any => ({ id: '123' }));
424
+ api.deletePage('42', '123');
425
+ expect(api.delete).toHaveBeenCalledWith('/workspaces/42/pages/123');
426
+ });
427
+
428
+ it('should stream events', async () => {
429
+ const api = new Api('/fake/');
430
+
431
+ const events = await api.streamEvents('42');
432
+ expect((events.constructor as any).mockedConstructor).toHaveBeenCalledWith({
433
+ workspaceId: '42',
434
+ token: '',
435
+ apiKey: undefined,
436
+ apiHost: '/fake/',
437
+ filters: undefined,
438
+ api,
439
+ });
440
+ expect(events.once).toHaveBeenCalled();
441
+ (events.constructor as any).mockedConstructor.mockClear();
442
+
443
+ const events2 = await api.streamEvents('42', { 'source.sessionId': true });
444
+ expect((events2.constructor as any).mockedConstructor).toHaveBeenCalledWith({
445
+ workspaceId: '42',
446
+ token: '',
447
+ apiKey: undefined,
448
+ apiHost: '/fake/',
449
+ filters: {},
450
+ api,
451
+ });
452
+ expect(events2.once).toHaveBeenCalled();
453
+
454
+ // @ts-ignore
455
+ api.sessionId = 'session id';
456
+ // @ts-ignore
457
+ api._apiKey = 'api key';
458
+ const events3 = await api.streamEvents('42', { 'source.sessionId': true });
459
+ expect((events2.constructor as any).mockedConstructor).toHaveBeenCalledWith({
460
+ workspaceId: '42',
461
+ token: '',
462
+ apiKey: 'api key',
463
+ apiHost: '/fake/',
464
+ filters: {
465
+ 'source.sessionId': 'session id',
466
+ },
467
+ api,
468
+ });
469
+ expect(events3.once).toHaveBeenCalled();
470
+
471
+ // @ts-ignore
472
+ api._shouldFail = true;
473
+ try {
474
+ await api.streamEvents('42');
475
+ } catch (e) {}
476
+ expect((events3.constructor as any).mockedConstructor).toHaveBeenCalledWith({
477
+ workspaceId: '42',
478
+ token: '',
479
+ apiKey: 'api key',
480
+ apiHost: '/fake/',
481
+ filters: undefined,
482
+ api,
483
+ });
484
+ expect(events3.close).toHaveBeenCalled();
485
+ });
486
+
487
+ it('should get events', async () => {
488
+ const api = new Api('/fake/');
489
+ api.get = jest.fn((): any => ({
490
+ result: { events: [{ createdAt: '2022-01-01', id: '123' }] },
491
+ }));
492
+ api.getEvents('42');
493
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/events');
494
+
495
+ const events = await api.getEvents('42', { foo: 'bar' });
496
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/events?foo=bar');
497
+ expect(events).toEqual([{ id: '123', createdAt: new Date('2022-01-01') }]);
498
+
499
+ api.get = jest.fn((): any => {
500
+ throw new Error();
501
+ });
502
+ const empty = await api.getEvents('42', { foo: 'bar' });
503
+ expect(empty).toEqual([]);
504
+ });
505
+
506
+ it('should post events', async () => {
507
+ const api = new Api('/fake/');
508
+ api.post = jest.fn((): any => {});
509
+ const res = await api.postEvents('42', [
510
+ {
511
+ type: 'foo',
512
+ payload: {},
513
+ },
514
+ ]);
515
+ expect(api.post).toHaveBeenCalledWith('/workspaces/42/events', {
516
+ events: [
517
+ {
518
+ type: 'foo',
519
+ payload: {},
520
+ },
521
+ ],
522
+ });
523
+ expect(res).toBe(true);
524
+
525
+ api.post = jest.fn((): any => {
526
+ throw new Error();
527
+ });
528
+ const res2 = await api.postEvents('42', [
529
+ {
530
+ type: 'foo',
531
+ payload: {},
532
+ },
533
+ ]);
534
+ expect(res2).toBe(false);
535
+ });
536
+
537
+ describe('permissions', () => {
538
+ const api = new Api('/fake/');
539
+ api.get = jest.fn((path: string): any => {
540
+ if (path === '/pages/id/permissions') {
541
+ return {
542
+ result: [],
543
+ };
544
+ }
545
+ });
546
+ api.post = jest.fn((path: string, body: any): any => {
547
+ if (path === '/pages/id/permissions') {
548
+ return body;
549
+ }
550
+ });
551
+ api.findContacts = jest.fn(({ email }): any => {
552
+ if (email === 'foo@bar.com')
553
+ return {
554
+ contacts: [
555
+ {
556
+ id: '1234',
557
+ },
558
+ ],
559
+ };
560
+ return { contacts: [] };
561
+ });
562
+ api.getPermissions('pages', 'id');
563
+
564
+ it('should get permissions', async () => {
565
+ expect(api.get).toHaveBeenCalledWith('/pages/id/permissions');
566
+ });
567
+
568
+ it('should add permissions', async () => {
569
+ await api.addPermissions('pages', 'id', {
570
+ permissions: {},
571
+ target: { email: 'foo@bar.com' },
572
+ });
573
+ expect(api.post).toHaveBeenCalledWith('/pages/id/permissions', {
574
+ permissions: {},
575
+ target: {
576
+ id: '1234',
577
+ },
578
+ });
579
+ });
580
+
581
+ it('should fail to add permissions', async () => {
582
+ let expected: ApiError = {} as ApiError;
583
+ try {
584
+ await api.addPermissions('pages', 'id', {
585
+ permissions: {},
586
+ target: { email: 'foofoo@bar.com' },
587
+ });
588
+ } catch (e) {
589
+ console.log(e);
590
+ expected = e as ApiError;
591
+ }
592
+ expect(expected.message).toBe('This user does not exist');
593
+ });
594
+
595
+ it('should delete permissions', async () => {
596
+ api.delete = jest.fn((): any => {});
597
+ api.deletePermissions('pages', 'id', 'foo');
598
+ expect(api.delete).toHaveBeenCalledWith('/pages/id/permissions/foo');
599
+ });
600
+ });
601
+
602
+ it('should get apps', async () => {
603
+ const api = new Api('/fake/');
604
+ const get: jest.Mock = (api.get = jest.fn((): any => {}));
605
+ api.getApps({});
606
+ expect(api.get).toHaveBeenCalledWith('/apps?');
607
+ get.mockClear();
608
+
609
+ api.getApps({ query: 'foo' });
610
+ expect(get).toHaveBeenCalledWith('/apps?text=foo');
611
+ get.mockClear();
612
+
613
+ api.getApps({ query: 'foo', page: 1, limit: 10, workspaceId: '42' });
614
+ expect(get).toHaveBeenCalledWith(
615
+ '/apps?text=foo&page=1&limit=10&workspaceId=42'
616
+ );
617
+ });
618
+
619
+ it('should install app', async () => {
620
+ const api = new Api('/fake/');
621
+ api.post = jest.fn((): any => {});
622
+ api.installApp('42', { appSlug: 'app' });
623
+ expect(api.post).toHaveBeenCalledWith('/workspaces/42/apps', {
624
+ appSlug: 'app',
625
+ });
626
+ });
627
+
628
+ it('should update app', async () => {
629
+ const api = new Api('/fake/');
630
+ api.patch = jest.fn((): any => {});
631
+ api.updateApp('42', 'app', { appSlug: 'app' });
632
+ expect(api.patch).toHaveBeenCalledWith('/workspaces/42/apps/app', {
633
+ appSlug: 'app',
634
+ });
635
+ });
636
+
637
+ it('should uninstall app', async () => {
638
+ const api = new Api('/fake/');
639
+ api.delete = jest.fn((): any => {});
640
+ api.uninstallApp('42', 'app');
641
+ expect(api.delete).toHaveBeenCalledWith('/workspaces/42/apps/app');
642
+ });
643
+
644
+ it('should publish app', async () => {
645
+ const api = new Api('/fake/');
646
+ api.post = jest.fn((): any => {});
647
+ api.publishApp({ workspaceId: '42' });
648
+ expect(api.post).toHaveBeenCalledWith('/apps', { workspaceId: '42' });
649
+ });
650
+
651
+ it('should list app instances', async () => {
652
+ const api = new Api('/fake/');
653
+ api.get = jest.fn((): any => {});
654
+ api.listAppInstances('42');
655
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/apps');
656
+ });
657
+
658
+ it('should get app config', async () => {
659
+ const api = new Api('/fake/');
660
+ api.get = jest.fn((): any => {});
661
+ api.getAppConfig('42', 'app');
662
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/apps/app/config');
663
+ });
664
+
665
+ it('should update app config', async () => {
666
+ const api = new Api('/fake/');
667
+ api.patch = jest.fn((): any => {});
668
+ api.updateAppConfig('42', 'app', {});
669
+ expect(api.patch).toHaveBeenCalledWith('/workspaces/42/apps/app/config', {});
670
+ });
671
+
672
+ it('should save app instance', async () => {
673
+ const api = new Api('/fake/');
674
+ api.patch = jest.fn((): any => {});
675
+ api.saveAppInstance('42', 'app', {});
676
+ expect(api.patch).toHaveBeenCalledWith('/workspaces/42/apps/app', {});
677
+ });
678
+
679
+ it('should upload files', async () => {
680
+ const api = new Api('/fake/');
681
+ // @ts-ignore
682
+ let fetch: jest.Mock = (api._fetch = jest.fn((): any => {}));
683
+ api.uploadFiles('data:text/plain;base64,SGVsbG8gV29ybGQ', '42');
684
+ // @ts-ignore
685
+ expect(fetch).toHaveBeenCalledWith('/workspaces/42/files', {
686
+ method: 'POST',
687
+ body: expect.any(FormData),
688
+ });
689
+
690
+ expect(fetch.mock.calls[0][1].body.get('file')).toBeInstanceOf(Blob);
691
+ fetch.mockClear();
692
+
693
+ api.uploadFiles(['data:text/plain;text,Hello World'], '42');
694
+ // @ts-ignore
695
+ expect(fetch).toHaveBeenCalledWith('/workspaces/42/files', {
696
+ method: 'POST',
697
+ body: expect.any(FormData),
698
+ });
699
+
700
+ // @ts-ignore
701
+ fetch = api._fetch = jest.fn((): any => {
702
+ throw new Error();
703
+ });
704
+ const empty = await api.uploadFiles('data:text/plain;text,Hello World', '42');
705
+ expect(empty).toEqual([]);
706
+ });
707
+
708
+ it('should call automation', () => {
709
+ const api = new Api('/fake/');
710
+ api.post = jest.fn((): any => {});
711
+ api.callAutomation('42', 'automation');
712
+ expect(api.post).toHaveBeenCalledWith(
713
+ '/workspaces/42/webhooks/automation',
714
+ undefined
715
+ );
716
+ });
717
+
718
+ it('should get workspace usage', () => {
719
+ const api = new Api('/fake/');
720
+ const get: jest.Mock = (api.get = jest.fn((): any => {}));
721
+ api.getWorkspaceUsage('42');
722
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/usage?');
723
+ get.mockClear();
724
+
725
+ api.getWorkspaceUsage('42', {
726
+ afterDate: '2022',
727
+ beforeDate: undefined,
728
+ });
729
+ expect(api.get).toHaveBeenCalledWith('/workspaces/42/usage?afterDate=2022');
730
+ });
731
+
732
+ describe('Workspaces', () => {
733
+ it('should access workspaces methods', () => {
734
+ expect(api.workspaces('42')).toBeInstanceOf(WorkspacesEndpoint);
735
+ });
736
+ describe('Versions', () => {
737
+ it('should access versions methods', () => {
738
+ expect(api.workspaces('42').versions).toBeInstanceOf(
739
+ WorkspacesVersionsEndpoint
740
+ );
741
+ });
742
+ it('create', () => {
743
+ api.post = jest.fn();
744
+ api.workspaces('42').versions.create({ description: 'foo' });
745
+ expect(api.post).toHaveBeenCalledWith('/workspaces/42/versions', {
746
+ description: 'foo',
747
+ });
748
+ });
749
+ it('rollback', () => {
750
+ api.post = jest.fn();
751
+ api.workspaces('42').versions.rollback('v1.0.0');
752
+ expect(api.post).toHaveBeenCalledWith(
753
+ '/workspaces/42/versions/v1.0.0/rollback'
754
+ );
755
+ });
756
+ });
757
+ });