@webex/internal-plugin-device 3.12.0-next.1 → 3.12.0-next.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.
- package/dist/constants.js +6 -1
- package/dist/constants.js.map +1 -1
- package/dist/device.js +135 -79
- package/dist/device.js.map +1 -1
- package/dist/ipNetworkDetector.js +1 -1
- package/package.json +3 -3
- package/src/constants.js +5 -0
- package/src/device.js +107 -26
- package/test/unit/spec/device.js +401 -6
package/test/unit/spec/device.js
CHANGED
|
@@ -484,9 +484,26 @@ describe('plugin-device', () => {
|
|
|
484
484
|
});
|
|
485
485
|
|
|
486
486
|
describe('deleteDevices()', () => {
|
|
487
|
+
let requestStub;
|
|
488
|
+
let clock;
|
|
489
|
+
let waitForLimitStub;
|
|
490
|
+
|
|
487
491
|
const setup = (deviceType) => {
|
|
488
492
|
device.config.defaults = {body: {deviceType}};
|
|
489
493
|
};
|
|
494
|
+
|
|
495
|
+
beforeEach(() => {
|
|
496
|
+
waitForLimitStub = sinon.stub(device, '_waitForDeviceCountBelowLimit').resolves();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
afterEach(() => {
|
|
500
|
+
sinon.restore();
|
|
501
|
+
if (clock) {
|
|
502
|
+
clock.restore();
|
|
503
|
+
clock = null;
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
490
507
|
['WEB', 'WEBCLIENT'].forEach((deviceType) => {
|
|
491
508
|
it(`should delete correct number of devices for ${deviceType}`, async () => {
|
|
492
509
|
setup(deviceType);
|
|
@@ -504,7 +521,8 @@ describe('plugin-device', () => {
|
|
|
504
521
|
],
|
|
505
522
|
},
|
|
506
523
|
};
|
|
507
|
-
|
|
524
|
+
|
|
525
|
+
requestStub = sinon.stub(device, 'request');
|
|
508
526
|
requestStub.withArgs(sinon.match({method: 'GET'})).resolves(response);
|
|
509
527
|
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
510
528
|
|
|
@@ -523,7 +541,7 @@ describe('plugin-device', () => {
|
|
|
523
541
|
});
|
|
524
542
|
});
|
|
525
543
|
|
|
526
|
-
it('does not delete when there are
|
|
544
|
+
it('does not delete when there are only 2 devices (below MIN_DEVICES_FOR_CLEANUP)', async () => {
|
|
527
545
|
setup('WEB');
|
|
528
546
|
const response = {
|
|
529
547
|
body: {
|
|
@@ -534,15 +552,392 @@ describe('plugin-device', () => {
|
|
|
534
552
|
},
|
|
535
553
|
};
|
|
536
554
|
|
|
537
|
-
|
|
555
|
+
requestStub = sinon.stub(device, 'request');
|
|
538
556
|
requestStub.withArgs(sinon.match({method: 'GET'})).resolves(response);
|
|
539
557
|
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
540
558
|
|
|
541
559
|
await device.deleteDevices();
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
560
|
+
// MIN_DEVICES_FOR_CLEANUP = 5; 2 devices is below the threshold, so nothing should be deleted
|
|
561
|
+
assert(requestStub.neverCalledWith(sinon.match({method: 'DELETE'})));
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it('does not delete when device count equals MIN_DEVICES_FOR_CLEANUP (5 devices)', async () => {
|
|
565
|
+
setup('WEB');
|
|
566
|
+
const devices = Array.from({length: 5}, (_, i) => ({
|
|
567
|
+
url: `url${i}`,
|
|
568
|
+
modificationTime: `2023-10-0${i + 1}T10:00:00Z`,
|
|
569
|
+
deviceType: 'WEB',
|
|
570
|
+
}));
|
|
571
|
+
|
|
572
|
+
requestStub = sinon.stub(device, 'request');
|
|
573
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
574
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
575
|
+
|
|
576
|
+
await device.deleteDevices();
|
|
577
|
+
// MIN_DEVICES_FOR_CLEANUP = 5; exactly at the threshold means no deletion
|
|
578
|
+
assert(requestStub.neverCalledWith(sinon.match({method: 'DELETE'})));
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('waits for all deletions to complete before proceeding', async () => {
|
|
582
|
+
setup('WEB');
|
|
583
|
+
const devices = Array.from({length: 6}, (_, i) => ({
|
|
584
|
+
url: `url${i}`,
|
|
585
|
+
modificationTime: `2023-10-0${i}T10:00:00Z`,
|
|
586
|
+
deviceType: 'WEB',
|
|
587
|
+
}));
|
|
588
|
+
|
|
589
|
+
requestStub = sinon.stub(device, 'request');
|
|
590
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
591
|
+
|
|
592
|
+
const deleteOrder = [];
|
|
593
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).callsFake((opts) => {
|
|
594
|
+
deleteOrder.push(opts.uri);
|
|
595
|
+
return Promise.resolve();
|
|
545
596
|
});
|
|
597
|
+
|
|
598
|
+
await device.deleteDevices();
|
|
599
|
+
|
|
600
|
+
// ceil(6/3) = 2 devices should be deleted
|
|
601
|
+
assert.equal(deleteOrder.length, 2);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('does not delete when there are zero devices', async () => {
|
|
605
|
+
setup('WEB');
|
|
606
|
+
requestStub = sinon.stub(device, 'request');
|
|
607
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices: []}});
|
|
608
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
609
|
+
|
|
610
|
+
await device.deleteDevices();
|
|
611
|
+
|
|
612
|
+
assert(requestStub.neverCalledWith(sinon.match({method: 'DELETE'})));
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it('only deletes devices matching the current device type', async () => {
|
|
616
|
+
setup('WEB');
|
|
617
|
+
const devices = [
|
|
618
|
+
{url: 'web1', modificationTime: '2023-10-01T10:00:00Z', deviceType: 'WEB'},
|
|
619
|
+
{url: 'web2', modificationTime: '2023-10-02T10:00:00Z', deviceType: 'WEB'},
|
|
620
|
+
{url: 'web3', modificationTime: '2023-10-03T10:00:00Z', deviceType: 'WEB'},
|
|
621
|
+
{url: 'web4', modificationTime: '2023-10-04T10:00:00Z', deviceType: 'WEB'},
|
|
622
|
+
{url: 'web5', modificationTime: '2023-10-05T10:00:00Z', deviceType: 'WEB'},
|
|
623
|
+
{url: 'web6', modificationTime: '2023-10-06T10:00:00Z', deviceType: 'WEB'},
|
|
624
|
+
{url: 'desktop1', modificationTime: '2023-10-01T10:00:00Z', deviceType: 'DESKTOP'},
|
|
625
|
+
{url: 'mobile1', modificationTime: '2023-10-01T10:00:00Z', deviceType: 'MOBILE'},
|
|
626
|
+
];
|
|
627
|
+
|
|
628
|
+
requestStub = sinon.stub(device, 'request');
|
|
629
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
630
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
631
|
+
|
|
632
|
+
await device.deleteDevices();
|
|
633
|
+
|
|
634
|
+
// Only WEB devices considered: 6 total (> MIN_DEVICES_FOR_CLEANUP=5), ceil(6/3)=2 deleted (oldest: web1, web2)
|
|
635
|
+
assert(requestStub.calledWith(sinon.match({uri: 'web1', method: 'DELETE'})));
|
|
636
|
+
assert(requestStub.calledWith(sinon.match({uri: 'web2', method: 'DELETE'})));
|
|
637
|
+
assert(requestStub.neverCalledWith(sinon.match({uri: 'desktop1', method: 'DELETE'})));
|
|
638
|
+
assert(requestStub.neverCalledWith(sinon.match({uri: 'mobile1', method: 'DELETE'})));
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
it('rejects when fetching devices fails', async () => {
|
|
642
|
+
setup('WEB');
|
|
643
|
+
requestStub = sinon.stub(device, 'request');
|
|
644
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).rejects(new Error('network error'));
|
|
645
|
+
|
|
646
|
+
await assert.isRejected(device.deleteDevices(), 'network error');
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it('resolves when all deletion requests fail (best-effort)', async () => {
|
|
650
|
+
setup('WEB');
|
|
651
|
+
// Use 6 devices (> MIN_DEVICES_FOR_CLEANUP=5) to ensure deletion is attempted
|
|
652
|
+
const devices = [
|
|
653
|
+
{url: 'url1', modificationTime: '2023-10-01T10:00:00Z', deviceType: 'WEB'},
|
|
654
|
+
{url: 'url2', modificationTime: '2023-10-02T10:00:00Z', deviceType: 'WEB'},
|
|
655
|
+
{url: 'url3', modificationTime: '2023-10-03T10:00:00Z', deviceType: 'WEB'},
|
|
656
|
+
{url: 'url4', modificationTime: '2023-10-04T10:00:00Z', deviceType: 'WEB'},
|
|
657
|
+
{url: 'url5', modificationTime: '2023-10-05T10:00:00Z', deviceType: 'WEB'},
|
|
658
|
+
{url: 'url6', modificationTime: '2023-10-06T10:00:00Z', deviceType: 'WEB'},
|
|
659
|
+
];
|
|
660
|
+
|
|
661
|
+
requestStub = sinon.stub(device, 'request');
|
|
662
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
663
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).rejects(new Error('delete failed'));
|
|
664
|
+
|
|
665
|
+
// Should resolve despite DELETE failures — best-effort cleanup must not block registration retry
|
|
666
|
+
await device.deleteDevices();
|
|
667
|
+
assert.calledWith(device.logger.warn, sinon.match(/deletions failed/));
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
it('resolves when only some deletion requests fail (partial failure)', async () => {
|
|
671
|
+
setup('WEB');
|
|
672
|
+
const devices = [
|
|
673
|
+
{url: 'url1', modificationTime: '2023-10-01T10:00:00Z', deviceType: 'WEB'},
|
|
674
|
+
{url: 'url2', modificationTime: '2023-10-02T10:00:00Z', deviceType: 'WEB'},
|
|
675
|
+
{url: 'url3', modificationTime: '2023-10-03T10:00:00Z', deviceType: 'WEB'},
|
|
676
|
+
{url: 'url4', modificationTime: '2023-10-04T10:00:00Z', deviceType: 'WEB'},
|
|
677
|
+
{url: 'url5', modificationTime: '2023-10-05T10:00:00Z', deviceType: 'WEB'},
|
|
678
|
+
{url: 'url6', modificationTime: '2023-10-06T10:00:00Z', deviceType: 'WEB'},
|
|
679
|
+
];
|
|
680
|
+
|
|
681
|
+
requestStub = sinon.stub(device, 'request');
|
|
682
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
683
|
+
// ceil(6/3) = 2 deletions; first succeeds, second fails
|
|
684
|
+
requestStub
|
|
685
|
+
.withArgs(sinon.match({method: 'DELETE'}))
|
|
686
|
+
.onFirstCall()
|
|
687
|
+
.resolves()
|
|
688
|
+
.onSecondCall()
|
|
689
|
+
.rejects(new Error('404 not found'));
|
|
690
|
+
|
|
691
|
+
await device.deleteDevices();
|
|
692
|
+
assert.calledWith(device.logger.warn, sinon.match(/deletions failed/));
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('calls _waitForDeviceCountBelowLimit with targetCount equal to preCount minus min(5, deletedCount)', async () => {
|
|
696
|
+
setup('WEB');
|
|
697
|
+
const devices = Array.from({length: 20}, (_, i) => ({
|
|
698
|
+
url: `url${i}`,
|
|
699
|
+
modificationTime: `2023-10-${String(i + 1).padStart(2, '0')}T10:00:00Z`,
|
|
700
|
+
deviceType: 'WEB',
|
|
701
|
+
}));
|
|
702
|
+
|
|
703
|
+
requestStub = sinon.stub(device, 'request');
|
|
704
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
705
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
706
|
+
|
|
707
|
+
await device.deleteDevices();
|
|
708
|
+
|
|
709
|
+
// 20 WEB devices, ceil(20/3) = 7 deletions (>= 5), targetCount = 20 - min(5, 7) = 15
|
|
710
|
+
assert.calledWith(waitForLimitStub, 15, 0);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it('small-n: 6-device case — targetCount is reachable (ceil(6/3)=2 < 5, so wait for 6-2=4)', async () => {
|
|
714
|
+
setup('WEB');
|
|
715
|
+
const devices = Array.from({length: 6}, (_, i) => ({
|
|
716
|
+
url: `url${i}`,
|
|
717
|
+
modificationTime: `2023-10-0${i + 1}T10:00:00Z`,
|
|
718
|
+
deviceType: 'WEB',
|
|
719
|
+
}));
|
|
720
|
+
|
|
721
|
+
requestStub = sinon.stub(device, 'request');
|
|
722
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
723
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
724
|
+
|
|
725
|
+
await device.deleteDevices();
|
|
726
|
+
|
|
727
|
+
// ceil(6/3) = 2 deletions (< 5), targetCount = 6 - min(5, 2) = 4
|
|
728
|
+
// With the old n-5 formula this was 1, which is unreachable and burned all 5 polls
|
|
729
|
+
assert.equal(requestStub.withArgs(sinon.match({method: 'DELETE'})).callCount, 2);
|
|
730
|
+
assert.calledWith(waitForLimitStub, 4, 0);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it('regression: 144-device case — deleteDevices passes targetCount=139 (144 - min(5, ceil(144/3)))', async () => {
|
|
734
|
+
setup('WEB');
|
|
735
|
+
const devices = Array.from({length: 144}, (_, i) => ({
|
|
736
|
+
url: `url${i}`,
|
|
737
|
+
modificationTime: new Date(Date.UTC(2020, 0, 1, 0, i)).toISOString(),
|
|
738
|
+
deviceType: 'WEB',
|
|
739
|
+
}));
|
|
740
|
+
|
|
741
|
+
requestStub = sinon.stub(device, 'request');
|
|
742
|
+
requestStub.withArgs(sinon.match({method: 'GET'})).resolves({body: {devices}});
|
|
743
|
+
requestStub.withArgs(sinon.match({method: 'DELETE'})).resolves();
|
|
744
|
+
|
|
745
|
+
await device.deleteDevices();
|
|
746
|
+
|
|
747
|
+
// ceil(144/3) = 48 deletions (>= 5), targetCount = 144 - min(5, 48) = 139
|
|
748
|
+
assert.equal(requestStub.withArgs(sinon.match({method: 'DELETE'})).callCount, 48);
|
|
749
|
+
assert.calledWith(waitForLimitStub, 139, 0);
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
describe('_waitForDeviceCountBelowLimit()', () => {
|
|
754
|
+
let clock;
|
|
755
|
+
|
|
756
|
+
const setup = (deviceType) => {
|
|
757
|
+
device.config.defaults = {body: {deviceType}};
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
beforeEach(() => {
|
|
761
|
+
clock = sinon.useFakeTimers();
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
afterEach(() => {
|
|
765
|
+
sinon.restore();
|
|
766
|
+
clock.restore();
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
it('resolves immediately when device count is below the limit on first check', async () => {
|
|
770
|
+
setup('WEB');
|
|
771
|
+
const devices = Array.from({length: 50}, (_, i) => ({
|
|
772
|
+
url: `url${i}`,
|
|
773
|
+
modificationTime: `2023-10-01T10:00:00Z`,
|
|
774
|
+
deviceType: 'WEB',
|
|
775
|
+
}));
|
|
776
|
+
|
|
777
|
+
sinon.stub(device, 'request')
|
|
778
|
+
.withArgs(sinon.match({method: 'GET'}))
|
|
779
|
+
.resolves({body: {devices}});
|
|
780
|
+
|
|
781
|
+
const promise = device._waitForDeviceCountBelowLimit(55, 0);
|
|
782
|
+
await clock.tickAsync(3000);
|
|
783
|
+
await promise;
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
it('polls multiple times until device count drops below the limit', async () => {
|
|
787
|
+
setup('WEB');
|
|
788
|
+
const makeDevices = (count) =>
|
|
789
|
+
Array.from({length: count}, (_, i) => ({
|
|
790
|
+
url: `url${i}`,
|
|
791
|
+
modificationTime: `2023-10-01T10:00:00Z`,
|
|
792
|
+
deviceType: 'WEB',
|
|
793
|
+
}));
|
|
794
|
+
|
|
795
|
+
const requestStub = sinon.stub(device, 'request');
|
|
796
|
+
requestStub.withArgs(sinon.match({method: 'GET'}))
|
|
797
|
+
.onFirstCall().resolves({body: {devices: makeDevices(102)}})
|
|
798
|
+
.onSecondCall().resolves({body: {devices: makeDevices(100)}})
|
|
799
|
+
.onThirdCall().resolves({body: {devices: makeDevices(68)}});
|
|
800
|
+
|
|
801
|
+
const promise = device._waitForDeviceCountBelowLimit(95, 0);
|
|
802
|
+
|
|
803
|
+
// First poll: 102 devices (above target 95), continue polling
|
|
804
|
+
await clock.tickAsync(3000);
|
|
805
|
+
// Second poll: 100 devices (still above target 95), continue polling
|
|
806
|
+
await clock.tickAsync(3000);
|
|
807
|
+
// Third poll: 68 devices (below target 95), resolve
|
|
808
|
+
await clock.tickAsync(3000);
|
|
809
|
+
|
|
810
|
+
await promise;
|
|
811
|
+
|
|
812
|
+
assert.equal(requestStub.withArgs(sinon.match({method: 'GET'})).callCount, 3);
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('gives up after max confirmation attempts and resolves anyway', async () => {
|
|
816
|
+
setup('WEB');
|
|
817
|
+
const makeDevices = (count) =>
|
|
818
|
+
Array.from({length: count}, (_, i) => ({
|
|
819
|
+
url: `url${i}`,
|
|
820
|
+
modificationTime: `2023-10-01T10:00:00Z`,
|
|
821
|
+
deviceType: 'WEB',
|
|
822
|
+
}));
|
|
823
|
+
|
|
824
|
+
const requestStub = sinon.stub(device, 'request');
|
|
825
|
+
requestStub.withArgs(sinon.match({method: 'GET'}))
|
|
826
|
+
.resolves({body: {devices: makeDevices(105)}});
|
|
827
|
+
|
|
828
|
+
const promise = device._waitForDeviceCountBelowLimit(100, 0);
|
|
829
|
+
|
|
830
|
+
// Tick through all 5 attempts (5 * 3000ms)
|
|
831
|
+
for (let i = 0; i < 5; i += 1) {
|
|
832
|
+
await clock.tickAsync(3000);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
await promise;
|
|
836
|
+
|
|
837
|
+
assert(device.logger.warn.calledWith('device: max confirmation attempts reached, proceeding anyway'));
|
|
838
|
+
assert.equal(requestStub.withArgs(sinon.match({method: 'GET'})).callCount, 5);
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
it('resolves when count equals exactly 95 (5 below limit)', async () => {
|
|
842
|
+
setup('WEB');
|
|
843
|
+
const devices = Array.from({length: 95}, (_, i) => ({
|
|
844
|
+
url: `url${i}`,
|
|
845
|
+
modificationTime: `2023-10-01T10:00:00Z`,
|
|
846
|
+
deviceType: 'WEB',
|
|
847
|
+
}));
|
|
848
|
+
|
|
849
|
+
sinon.stub(device, 'request')
|
|
850
|
+
.withArgs(sinon.match({method: 'GET'}))
|
|
851
|
+
.resolves({body: {devices}});
|
|
852
|
+
|
|
853
|
+
const promise = device._waitForDeviceCountBelowLimit(95, 0);
|
|
854
|
+
await clock.tickAsync(3000);
|
|
855
|
+
await promise;
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
it('keeps polling when count is above the 5-below-limit threshold', async () => {
|
|
859
|
+
setup('WEB');
|
|
860
|
+
const makeDevices = (count) =>
|
|
861
|
+
Array.from({length: count}, (_, i) => ({
|
|
862
|
+
url: `url${i}`,
|
|
863
|
+
modificationTime: `2023-10-01T10:00:00Z`,
|
|
864
|
+
deviceType: 'WEB',
|
|
865
|
+
}));
|
|
866
|
+
|
|
867
|
+
const requestStub = sinon.stub(device, 'request');
|
|
868
|
+
requestStub.withArgs(sinon.match({method: 'GET'}))
|
|
869
|
+
.onFirstCall().resolves({body: {devices: makeDevices(100)}})
|
|
870
|
+
.onSecondCall().resolves({body: {devices: makeDevices(99)}})
|
|
871
|
+
.onThirdCall().resolves({body: {devices: makeDevices(95)}});
|
|
872
|
+
|
|
873
|
+
const promise = device._waitForDeviceCountBelowLimit(95, 0);
|
|
874
|
+
// First poll: 100 devices (still over the 95 threshold), continue polling
|
|
875
|
+
await clock.tickAsync(3000);
|
|
876
|
+
// Second poll: 99 devices (still over the 95 threshold), continue polling
|
|
877
|
+
await clock.tickAsync(3000);
|
|
878
|
+
// Third poll: 95 devices (at the safe threshold), resolve
|
|
879
|
+
await clock.tickAsync(3000);
|
|
880
|
+
await promise;
|
|
881
|
+
|
|
882
|
+
assert.equal(requestStub.withArgs(sinon.match({method: 'GET'})).callCount, 3);
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it('resolves (best-effort) when the polling GET throws a transient error', async () => {
|
|
886
|
+
setup('WEB');
|
|
887
|
+
|
|
888
|
+
sinon.stub(device, 'request')
|
|
889
|
+
.withArgs(sinon.match({method: 'GET'}))
|
|
890
|
+
.rejects(new Error('transient network error'));
|
|
891
|
+
|
|
892
|
+
const promise = device._waitForDeviceCountBelowLimit(95, 0);
|
|
893
|
+
await clock.tickAsync(3000);
|
|
894
|
+
await promise;
|
|
895
|
+
|
|
896
|
+
assert(device.logger.warn.calledWith(
|
|
897
|
+
sinon.match('device: confirmation check 1 failed, proceeding anyway:')
|
|
898
|
+
));
|
|
899
|
+
});
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
describe('_getDevicesOfCurrentType()', () => {
|
|
903
|
+
const setup = (deviceType) => {
|
|
904
|
+
device.config.defaults = {body: {deviceType}};
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
afterEach(() => {
|
|
908
|
+
sinon.restore();
|
|
909
|
+
});
|
|
910
|
+
|
|
911
|
+
it('filters devices by the current device type', async () => {
|
|
912
|
+
setup('WEB');
|
|
913
|
+
const allDevices = [
|
|
914
|
+
{url: 'web1', deviceType: 'WEB'},
|
|
915
|
+
{url: 'desktop1', deviceType: 'DESKTOP'},
|
|
916
|
+
{url: 'web2', deviceType: 'WEB'},
|
|
917
|
+
{url: 'mobile1', deviceType: 'MOBILE'},
|
|
918
|
+
];
|
|
919
|
+
|
|
920
|
+
sinon.stub(device, 'request').resolves({body: {devices: allDevices}});
|
|
921
|
+
|
|
922
|
+
const result = await device._getDevicesOfCurrentType();
|
|
923
|
+
|
|
924
|
+
assert.equal(result.length, 2);
|
|
925
|
+
assert.equal(result[0].url, 'web1');
|
|
926
|
+
assert.equal(result[1].url, 'web2');
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
it('returns an empty array when no devices match', async () => {
|
|
930
|
+
setup('WEB');
|
|
931
|
+
const allDevices = [
|
|
932
|
+
{url: 'desktop1', deviceType: 'DESKTOP'},
|
|
933
|
+
{url: 'mobile1', deviceType: 'MOBILE'},
|
|
934
|
+
];
|
|
935
|
+
|
|
936
|
+
sinon.stub(device, 'request').resolves({body: {devices: allDevices}});
|
|
937
|
+
|
|
938
|
+
const result = await device._getDevicesOfCurrentType();
|
|
939
|
+
|
|
940
|
+
assert.equal(result.length, 0);
|
|
546
941
|
});
|
|
547
942
|
});
|
|
548
943
|
|