/// // tslint:disable import * as _ from "underscore"; import { repeat, createSequence, retryAction, retry } from "./retry"; import { createDeferred } from "../deferred" import { sleep } from "../sleep"; let invokeCount = 0; function failFirst(): Promise { const result = createDeferred(); invokeCount++; if (invokeCount < 3) { result.reject('try again'); } else { result.resolve(true); } return result.promise; } describe('repeat function', () => { beforeEach(async done => { await repeat(failFirst, () => Promise.resolve(true), async () => { await sleep(10); }); done(); }); afterAll(done => { invokeCount = 0; done(); }) it('try 3 times before success', () => { expect(invokeCount).toBe(3); }) }); describe('constant sequence function', () => { it('has constant sequence', () => { const seq = createSequence('constant'); const result = _.range(0, 3).map(() => seq.next()); expect(result).toEqual([1, 1, 1]) }); it('has exponential sequence', () => { const seq = createSequence('exponential'); const result = _.range(0, 4).map(() => seq.next()); expect(result).toEqual([1, 2, 4, 8]) }); it('has fibonacci sequence', () => { const seq = createSequence('fibonacci'); const result = _.range(0, 5).map(() => seq.next()); expect(result).toEqual([1, 2, 3, 5, 8]); }); it('has linear sequnce', () => { const seq = createSequence('linear'); const result = _.range(0, 6).map(() => seq.next()); expect(result).toEqual([1, 2, 3, 4, 5, 6]); }); }); describe('retry error cases', () => { let err: Error; beforeEach(async done => { try { await retryAction(failFirst, () => Promise.resolve(true), { type: 'constant', maxIteration: 2 }); } catch (ex) { err = ex; } done(); }); afterAll(done => { invokeCount = 0; done(); }); it('fails with maxIteration set to 2', () => { expect(err.message).toBe('exceeded maxIteration 2'); }); }); describe('retry error cases', () => { let err: Error; beforeEach(async done => { try { await retryAction(failFirst, () => Promise.resolve(true), { maxDuration: 1000, type: 'exponential', beat: 550 }) } catch (ex) { err = ex; } done(); }); afterAll(done => { invokeCount = 0; done(); }); it('fails with maxDuration set 2000', () => { expect(err.message).toBe('exceeded maxDuration 1000'); }) }); class Dummy { private clock = createSequence('linear'); constructor(private trigger: number) { } innerToggle(password: string): Promise { let tick = 0; tick = this.clock.next(); if (tick >= this.trigger) { if (password === 'sesame') { return Promise.resolve(true); } return Promise.reject('wrong password'); } return Promise.reject('not yet'); } @retry({ type: 'constant' }, () => Promise.resolve(true)) tryToggle(password: string): Promise { console.log('tryToggle called') return this.innerToggle(password); } } describe('retry decorator', () => { const dummy = new Dummy(5); beforeEach(async done => { spyOn(dummy, 'innerToggle').and.callThrough(); await dummy.tryToggle('sesame'); done(); }); it('retried 5 times', function () { expect(dummy.innerToggle).toHaveBeenCalledTimes(5); }); })