193 lines
5.4 KiB
JavaScript
193 lines
5.4 KiB
JavaScript
'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 () {
|
|
tokenPool = new TokenPool({ batchSize })
|
|
ids.forEach(id => tokenPool.add(id))
|
|
})
|
|
|
|
it('allValidTokenIds() should return the full list', function () {
|
|
expect(tokenPool.allValidTokenIds()).to.deep.equal(ids)
|
|
})
|
|
|
|
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))
|
|
)
|
|
})
|
|
|
|
describe('serializeDebugInfo should initially return the expected', function () {
|
|
beforeEach(function () {
|
|
sinon.useFakeTimers({ now: 1544307744484 })
|
|
})
|
|
|
|
afterEach(function () {
|
|
sinon.restore()
|
|
})
|
|
|
|
context('sanitize is not specified', function () {
|
|
it('returns fully sanitized results', function () {
|
|
// This is `sha()` of '1', '2', '3', '4', '5'. These are written
|
|
// literally for avoidance of doubt as to whether sanitization is
|
|
// happening.
|
|
const sanitizedIds = [
|
|
'6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b',
|
|
'd4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35',
|
|
'4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce',
|
|
'4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a',
|
|
'ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d',
|
|
]
|
|
|
|
expect(tokenPool.serializeDebugInfo()).to.deep.equal({
|
|
allValidTokenIds: sanitizedIds,
|
|
priorityQueue: [],
|
|
fifoQueue: sanitizedIds.map(id => ({
|
|
data: '[redacted]',
|
|
id,
|
|
isFrozen: false,
|
|
isValid: true,
|
|
nextReset: Token.nextResetNever,
|
|
usesRemaining: batchSize,
|
|
})),
|
|
sanitized: true,
|
|
utcEpochSeconds: 1544307744,
|
|
})
|
|
})
|
|
})
|
|
|
|
context('with sanitize: false', function () {
|
|
it('returns unsanitized results', function () {
|
|
expect(tokenPool.serializeDebugInfo({ sanitize: false })).to.deep.equal(
|
|
{
|
|
allValidTokenIds: ids,
|
|
priorityQueue: [],
|
|
fifoQueue: ids.map(id => ({
|
|
data: undefined,
|
|
id,
|
|
isFrozen: false,
|
|
isValid: true,
|
|
nextReset: Token.nextResetNever,
|
|
usesRemaining: batchSize,
|
|
})),
|
|
sanitized: false,
|
|
utcEpochSeconds: 1544307744,
|
|
}
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
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)
|
|
})
|
|
})
|
|
|
|
context('when empty', function () {
|
|
it('next() should return the expected error', function () {
|
|
const tokenPool = new TokenPool()
|
|
expect(() => tokenPool.next()).to.throw('Token pool is exhausted')
|
|
})
|
|
})
|
|
})
|