try-to
Version:
try to do stuff, get promises back, chainy api
408 lines (361 loc) • 18.3 kB
JavaScript
const tryto = require('../src/try-to');
describe('try-to', function() {
describe('when we try to run a function', function() {
describe('and we don\'t supply a function', function() {
it('it rejects', function(done) {
tryto()
.for(1)
.every(100)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, err => {
expect(err).toMatch(/function must be supplied/i);
done();
});
});
});
describe('and we do supply a function', function() {
describe('and we don\'t supply an "every" value', function() {
describe('and we do\'t supply a "for" value', function() {
it('it rejects', function(done) {
tryto(function(){})
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, err => {
expect(err).toMatch(/for or every must be called/i);
done();
});
});
});
describe('and we do supply a "for" value', function() {
it('it runs the task for the specified number of ticks', function(done) {
let i = 0;
tryto(function() { if(++i < 100) { throw 'fail'; } })
.for(100)
.now()
.then(() => {
expect(i).toBe(100);
done();
}, err => {
expect(err).toBe('no error');
done();
});
});
});
});
describe('and we do supply an "every" value', function() {
beforeEach(function() {
jasmine.clock().install();
});
afterEach(function() {
jasmine.clock().uninstall();
});
describe('and we don\'t supply a "for" value', function() {
it('it runs the task indefinitely', function(done) {
let i = 0,
// To test extreme iterations (100,000,000), set STRESS_TEST
// WARNING: It'll probably take several minutes to run!
limit = process.env.STRESS_TEST ? 1e8 : 1e5;
tryto(function() { if(i < limit) { throw 'fail'; } })
.every(1)
.now()
.then(() => {
expect(i).toBe(limit + 1);
done();
}, err => {
expect(err).toBe('no error');
done();
});
while(i++ < limit) {
jasmine.clock().tick(1);
}
});
});
describe('and we do supply a "for" value', function() {
it('it runs the task that many times', function(done) {
let i = 0,
limit = 100;
tryto(function() { i++; throw 'fail'; })
.every(1)
.for(100)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(100);
done();
});
while(limit--) {
jasmine.clock().tick(1);
}
});
describe('and we\'re using the (default) nobackoff strategy', function() {
it('it repeats at regular intervals', function(done) {
let i = 0,
limit = 99,
d;
tryto(function() { i++; throw 'fail'; })
.every(1)
.for(100)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(100);
done();
});
while(limit--) {
d = i;
jasmine.clock().tick(1);
d = i - d;
// First tick triggers now AND retry #1
if(limit === 98) {
expect(d).toBe(2);
} else {
expect(d).toBe(1);
}
}
});
});
describe('and we\'re using the linear backoff strategy', function() {
it('it repeats at linearly increasing intervals', function(done) {
let i = 0,
limit = 99,
n = 1,
d;
tryto(function() { i++; throw 'fail'; })
.using(tryto.linear)
.config({ step: 1, max: 999999 })
.every(1)
.for(100)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(100);
done();
});
while(limit--) {
// Tick at increasing intervals and check how much 'i' changes by each time
d = i;
jasmine.clock().tick(n);
n += 1;
d = i - d;
// First tick triggers now AND retry #1 => d === 2
if(limit === 98) {
expect(d).toBe(2);
} else {
expect(d).toBe(1);
}
}
});
});
describe('and we\'re using the exponential backoff strategy', function() {
it('it repeats at exponentially increasing intervals', function(done) {
let i = 0,
// Need a much lower limit when exponential!
limit = 19,
n = 1,
d;
tryto(function() { i++; throw 'fail'; })
.using(tryto.exponential)
.config({ max: 1e10 })
.every(1)
.for(20)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(20);
done();
});
while(limit--) {
// Tick at increasing intervals and check how much 'i' changes by each time
d = i;
jasmine.clock().tick(n);
n *= 2;
d = i - d;
// First tick triggers now AND retry #1 => d === 2
if(limit === 18) {
expect(d).toBe(2);
} else {
expect(d).toBe(1);
}
}
});
});
describe('and we\'re using the fibonacci backoff strategy', function() {
it('it repeats at intervals in a fibonacci sequence', function(done) {
let i = 0,
// Need a bit of a lower limit when fibonacci
limit = 49,
n = 1,
c = 1,
t,
d;
tryto(function() { i++; throw 'fail'; })
.using(tryto.fibonacci)
.config({ max: 1e10 })
.every(1)
.for(50)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(50);
done();
});
while(limit--) {
// Tick at increasing intervals and check how much 'i' changes by each time
d = i;
jasmine.clock().tick(n);
// Get next fibonacci number
t = c;
c = n;
n = c + t;
d = i - d;
// First tick triggers now AND retry #1 => d === 2
if(limit === 48) {
expect(d).toBe(2);
} else {
expect(d).toBe(1);
}
}
});
});
describe('and we\'re using a custom backoff strategy', function() {
describe('and the strategy is null', function() {
it('it rejects', function(done) {
tryto(function(){ throw 'fail'; })
.using(null)
.for(2)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, err => {
expect(err).toMatch(/nextable/i);
done();
});
jasmine.clock().tick(1);
});
});
describe('and the strategy isn\'t a function', function() {
it('it rejects', function(done) {
tryto(function(){ throw 'fail'; })
.using([])
.for(2)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, err => {
expect(err).toMatch(/nextable/i);
done();
});
jasmine.clock().tick(1);
});
});
describe('and the strategy isn\'t a "nextable" and doesn\'t return one', function() {
it('it rejects', function(done) {
tryto(function(){ throw 'fail'; })
.using(function(){})
.for(2)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, err => {
expect(err).toMatch(/nextable/i);
done();
});
jasmine.clock().tick(1);
});
});
describe('and the strategy is a "nextable"', function() {
it('it repeats at intervals returned by the strategy', function(done) {
let i = 0,
limit = 50,
n = 1,
d,
s;
tryto(function() { i++; throw 'fail'; })
.using(function() {
n = Math.floor(Math.random() * this.last_delay) + this.last_delay;
return n;
})
.every(1000)
.for(50)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(50);
done();
});
while(limit--) {
d = i;
jasmine.clock().tick(n);
d = i - d;
if((limit === 49) && (n === 1)) {
// If the first value was 1 it'll trigger now and the first retry
expect(d).toBe(2);
s = true;
} else if(!limit && s) {
// If the first value was 1 we finished one try ago
expect(d).toBe(0);
} else {
expect(d).toBe(1);
}
}
});
});
describe('and the strategy returns a "nextable"', function() {
it('it repeats at intervals returned by the strategy\'s nextable', function(done) {
let i = 0,
limit = 50,
n = 1,
d;
tryto(function() { i++; throw 'fail'; })
.using(function(cfg) {
return function() {
n = Math.floor(Math.random() * 1000 * this.retries * cfg.v);
return n;
};
})
.config({ v: 2 })
.every(1)
.for(50)
.now()
.then(() => {
expect('this not').toBe('hit');
done();
}, () => {
expect(i).toBe(50);
done();
});
while(limit--) {
d = i;
jasmine.clock().tick(n);
d = i - d;
expect(d).toBe(1);
}
});
});
});
});
});
});
});
});
;