"use strict";
var _this = this;
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var chai_1 = require("chai");
var sinon = require("sinon");
var promise_utils_1 = require("../src/promise-utils");
var accuracyFactor = 0.9;
describe('Promise utilities', function () {
    describe('delayedPromise', function () {
        it('resolves after provided the ms', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            var startTime, delay;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        startTime = Date.now(), delay = 50;
                        return [4 /*yield*/, promise_utils_1.delayedPromise(delay)];
                    case 1:
                        _a.sent();
                        chai_1.expect(Date.now(), 'verify delay').to.be.gte(startTime + (delay * accuracyFactor));
                        return [2 /*return*/];
                }
            });
        }); });
    });
    describe('timeoutPromise', function () {
        it('resolves with original value if original promise resolves within time frame', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, chai_1.expect(promise_utils_1.timeoutPromise(Promise.resolve('test'), 100)).to.eventually.become('test')];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('rejects with original value if original promise rejects within time frame', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, chai_1.expect(promise_utils_1.timeoutPromise(Promise.reject('an error'), 100)).to.eventually.be.rejectedWith('an error')];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('rejects with a timeout message if time is up and original promise is pending', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, chai_1.expect(promise_utils_1.timeoutPromise(promise_utils_1.delayedPromise(200), 50)).to.eventually.be.rejectedWith('timed out after 50ms')];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('allows providing a custom timeout message', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, chai_1.expect(promise_utils_1.timeoutPromise(promise_utils_1.delayedPromise(200), 50, 'FAILED!')).to.eventually.be.rejectedWith('FAILED!')];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
    });
    describe('retryPromise', function () {
        function verifyCallCount(spy, count, noExtraEventsGrace) {
            return tslib_1.__awaiter(this, void 0, void 0, function () {
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            chai_1.expect(spy).to.have.callCount(count);
                            return [4 /*yield*/, promise_utils_1.delayedPromise(noExtraEventsGrace)];
                        case 1:
                            _a.sent(); // to catch unwanted calls to provider post fullfillment 
                            chai_1.expect(spy).to.have.callCount(count);
                            return [2 /*return*/];
                    }
                });
            });
        }
        it('resolves if first run was a success', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            var retryOptions, promiseProvider;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        retryOptions = { retries: 2, interval: 5 };
                        promiseProvider = sinon.stub().resolves('value');
                        return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.become('value')];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, verifyCallCount(promiseProvider, 1, retryOptions.interval + 1)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('rejects if first run failed, and no retries', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            var retryOptions, promiseProvider;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        retryOptions = { retries: 0, interval: 10 };
                        promiseProvider = sinon.stub().rejects(new Error('failed'));
                        return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.rejectedWith('failed')];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, verifyCallCount(promiseProvider, 1, retryOptions.interval + 1)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('resolves if a success run was achieved during a retry', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            var retryOptions, promiseProvider, startTime;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        retryOptions = { retries: 2, interval: 10 };
                        promiseProvider = sinon.stub()
                            .onFirstCall().rejects(new Error('first failure'))
                            .onSecondCall().rejects(new Error('second failure'))
                            .resolves('success');
                        startTime = Date.now();
                        return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.become('success')];
                    case 1:
                        _a.sent();
                        chai_1.expect(Date.now(), 'verify interval').to.be.gte(startTime + (retryOptions.interval * 2 * accuracyFactor));
                        return [4 /*yield*/, verifyCallCount(promiseProvider, 3, retryOptions.interval + 1)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('rejects if all tries failed', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            var retryOptions, promiseProvider;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        retryOptions = { retries: 5, interval: 5 };
                        promiseProvider = sinon.stub().rejects(new Error('failed'));
                        return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.rejectedWith('failed')];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, verifyCallCount(promiseProvider, 6, retryOptions.interval + 1)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        it('rejects with error of last failed attempt', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
            var retryOptions, promiseProvider;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        retryOptions = { retries: 1, interval: 5 };
                        promiseProvider = sinon.stub()
                            .onFirstCall().rejects(new Error('first failure'))
                            .onSecondCall().rejects(new Error('second failure'))
                            .rejects(new Error('other failures'));
                        return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.rejectedWith('second failure')];
                    case 1:
                        _a.sent();
                        return [4 /*yield*/, verifyCallCount(promiseProvider, 2, retryOptions.interval + 1)];
                    case 2:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        }); });
        describe('when provided with a timeout', function () {
            it('verifies timeout is greater than retries*interval', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                var retryOptions, promiseProvider;
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            retryOptions = { retries: 10, interval: 10, timeout: 90 };
                            promiseProvider = sinon.stub();
                            return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually
                                    .be.rejectedWith('timeout (90ms) must be greater than retries (10) times interval (10ms)')];
                        case 1:
                            _a.sent();
                            return [4 /*yield*/, verifyCallCount(promiseProvider, 0, retryOptions.interval + 1)];
                        case 2:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            }); });
            it('resolves if a success run was achieved during timeout', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                var retryOptions, promiseProvider;
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            retryOptions = { retries: 1, interval: 5, timeout: 1500 };
                            promiseProvider = sinon.stub()
                                .onFirstCall().rejects(new Error('first failure'))
                                .resolves('success');
                            return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.become('success')];
                        case 1:
                            _a.sent();
                            return [4 /*yield*/, verifyCallCount(promiseProvider, 2, retryOptions.interval + 1)];
                        case 2:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            }); });
            it('rejects with error of last failed attempt if timeout expires', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                var retryOptions, promiseProvider;
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            retryOptions = { retries: 1, interval: 10, timeout: 400 };
                            promiseProvider = sinon.stub()
                                .onFirstCall().rejects(new Error('first failure'))
                                .onSecondCall().returns(promise_utils_1.delayedPromise(2000));
                            return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.be.rejectedWith('first failure')];
                        case 1:
                            _a.sent();
                            return [4 /*yield*/, verifyCallCount(promiseProvider, 2, retryOptions.interval + 1)];
                        case 2:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            }); });
            it('rejects with default timeout message, if no last failed attempt and timeout expires', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                var retryOptions, promiseProvider;
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            retryOptions = { retries: 0, interval: 25, timeout: 50 };
                            promiseProvider = sinon.stub().returns(promise_utils_1.delayedPromise(1000));
                            return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.be.rejectedWith('timed out after 50ms')];
                        case 1:
                            _a.sent();
                            return [4 /*yield*/, verifyCallCount(promiseProvider, 1, retryOptions.interval + 1)];
                        case 2:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            }); });
            it('rejects with provided timeout message, if no last failed attempt and timeout expires', function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
                var retryOptions, promiseProvider;
                return tslib_1.__generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            retryOptions = { retries: 0, interval: 25, timeout: 50, timeoutMessage: 'FAILED' };
                            promiseProvider = sinon.stub().returns(promise_utils_1.delayedPromise(1000));
                            return [4 /*yield*/, chai_1.expect(promise_utils_1.retryPromise(promiseProvider, retryOptions)).to.eventually.be.rejectedWith('FAILED')];
                        case 1:
                            _a.sent();
                            return [4 /*yield*/, verifyCallCount(promiseProvider, 1, retryOptions.interval + 1)];
                        case 2:
                            _a.sent();
                            return [2 /*return*/];
                    }
                });
            }); });
        });
    });
});
//# sourceMappingURL=promise-utils.spec.js.map