'use strict'; const { expect } = require('chai'); const sinon = require('sinon'); const times = require('lodash.times'); const { Token, TokenPool } = require('./token-pool'); function expectPoolToBeExhausted (pool) { expect(() => { pool.next(); }).to.throw(Error, /^Token pool is exhausted$/); } describe('The token pool', function () { const ids = [1, 2, 3, 4, 5]; const batchSize = 3; let tokenPool; beforeEach(function () { // Set up. tokenPool = new TokenPool(batchSize); ids.forEach(id => tokenPool.add(id)); }); it('should yield the expected tokens', function () { ids.forEach(id => times(batchSize, () => expect(tokenPool.next().id).to.equal(id))); }); it('should repeat when reaching the end', function () { ids.forEach(id => times(batchSize, () => expect(tokenPool.next().id).to.equal(id))); ids.forEach(id => times(batchSize, () => expect(tokenPool.next().id).to.equal(id))); }); context('tokens are marked exhausted immediately', function () { it('should be exhausted', function () { ids.forEach(() => { const token = tokenPool.next(); token.update(0, Token.nextResetNever); }); expectPoolToBeExhausted(tokenPool); }); }); context('tokens are marked after the last request', function () { it('should be exhausted', function () { ids.forEach(() => { const token = times(batchSize, () => tokenPool.next()).pop(); token.update(0, Token.nextResetNever); }); expectPoolToBeExhausted(tokenPool); }); }); context('tokens are renewed', function () { it('should keep using them', function () { const tokensToRenew = [2, 4]; const renewalCount = 3; ids.forEach(id => { const token = times(batchSize, () => tokenPool.next()).pop(); const usesRemaining = tokensToRenew.includes(token.id) ? renewalCount : 0; token.update(usesRemaining, Token.nextResetNever); }); tokensToRenew.forEach(id => { let token; times(renewalCount, () => { token = tokenPool.next(); expect(token.id).to.equal(id); }).pop(); token.update(0, Token.nextResetNever); }); expectPoolToBeExhausted(tokenPool); }); }); context('tokens reset', function () { let clock; beforeEach(function () { clock = sinon.useFakeTimers(); }); afterEach(function () { clock.restore(); }); it('should start using them', function () { const tokensToReset = [2, 4]; const futureTime = 1440; ids.forEach(id => { const token = times(batchSize, () => tokenPool.next()).pop(); const nextReset = tokensToReset.includes(token.id) ? futureTime : Token.nextResetNever; token.update(0, nextReset); }); expectPoolToBeExhausted(tokenPool); clock.tick(1000 * futureTime); tokensToReset.forEach(id => { const token = times(batchSize, () => tokenPool.next()).pop(); token.update(0, Token.nextResetNever); }); expectPoolToBeExhausted(tokenPool); }); }); });