I'm trying to create a valid test case for the promiseRateLimit
function below. The way the promiseRateLimit
function works is it uses a queue
to store incoming promises and will place a delay
between them.
import Promise from 'bluebird'
export default function promiseRateLimit (fn, delay, count) {
let working = 0
let queue = []
function work () {
if ((queue.length === 0) || (working === count)) return
working++
Promise.delay(delay).tap(() => working--).then(work)
let {self, args, resolve} = queue.shift()
resolve(fn.apply(self, args))
}
return function debounced (...args) {
return new Promise(resolve => {
queue.push({self: this, args, resolve})
if (working < count) work()
})
}
}
Here's an example of the function in action.
async function main () {
const example = (v) => Promise.delay(50)
const exampleLimited = promiseRateLimit(example, 100, 1)
const alpha = await exampleLimited('alpha')
const beta = await exampleLimited('beta')
const gamma = await exampleLimited('gamma')
const epsilon = await exampleLimited('epsilon')
const phi = await exampleLimited('phi')
}
The example
promise takes 50ms
to run and the promiseRateLimit
function will only allow 1
promise every 100ms
. So the interval between promises should be greater than 100ms
.
Here's a complete test that sometimes returns successful and sometimes fails:
import test from 'ava'
import Debug from 'debug'
import Promise from 'bluebird'
import promiseRateLimit from './index'
import {getIntervalsBetweenDates} from '../utilitiesForDates'
import {arraySum} from '../utilitiesForArrays'
import {filter} from 'lodash'
test('using async await', async (t) => {
let timeLog = []
let runCount = 0
const example = (v) => Promise.delay(50)
.then(() => timeLog.push(new Date))
.then(() => runCount++)
.then(() => v)
const exampleLimited = promiseRateLimit(example, 100, 1, 'a')
const alpha = await exampleLimited('alpha')
const beta = await exampleLimited('beta')
const gamma = await exampleLimited('gamma')
const epsilon = await exampleLimited('epsilon')
const phi = await exampleLimited('phi')
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < 100)
const totalTime = arraySum(intervals)
t.is(intervals.length, 4)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= 400, true)
t.is(alpha, 'alpha')
t.is(beta, 'beta')
t.is(gamma, 'gamma')
t.is(epsilon, 'epsilon')
t.is(phi, 'phi')
})
I've created a getIntervalsBetweenDates
function that simply diff's two unix timestamps and get the duration between an array of dates.
export function getIntervalsBetweenDates (dates) {
let intervals = []
dates.forEach((date, index) => {
let nextDate = dates[index + 1]
if (nextDate) intervals.push(nextDate - date)
})
return intervals
}
The issue is that the test above sometimes returns an interval that is lower than the delay
. For instance if the delay
is 100ms
sometimes an interval returns 98ms
or 96ms
. There is no reason this should ever happen.
Is there any way to make the above test pass 100% of the time? I'm trying to ensure that the delay
argument works and that there is at least that much time between promises.
Update 2016-12-28 9:20am (EST)
Here's the full test
import test from 'ava'
import Debug from 'debug'
import Promise from 'bluebird'
import promiseRateLimit from './index'
import {getIntervalsBetweenDates} from '../utilitiesForDates'
import {arraySum} from '../utilitiesForArrays'
import {filter} from 'lodash'
test('using async await', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 4
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(50, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const alpha = await exampleLimited('alpha')
const beta = await exampleLimited('beta')
const gamma = await exampleLimited('gamma')
const epsilon = await exampleLimited('epsilon')
const phi = await exampleLimited('phi')
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
t.is(alpha, 'alpha')
t.is(beta, 'beta')
t.is(gamma, 'gamma')
t.is(epsilon, 'epsilon')
t.is(phi, 'phi')
})
test('using Promise.all with 2 promises', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 1
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(50, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const results = await Promise.all([exampleLimited('alpha'), exampleLimited('beta')])
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
})
test('using Promise.props with 4 promises', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 3
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(200, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const results = await Promise.props({
'alpha': exampleLimited('alpha'),
'beta': exampleLimited('beta'),
'gamma': exampleLimited('gamma'),
'delta': exampleLimited('delta')
})
const intervals = getIntervalsBetweenDates(timeLog)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
t.is(results.alpha, 'alpha')
t.is(results.beta, 'beta')
t.is(results.gamma, 'gamma')
t.is(results.delta, 'delta')
})
test('using Promise.props with 12 promises', async (t) => {
let timeLog = []
let runCount = 0
let bufferInterval = 100
let promisesLength = 11
const example = v => {
timeLog.push(new Date)
runCount++
return Promise.delay(200, v)
}
const exampleLimited = promiseRateLimit(example, bufferInterval, 1)
const results = await Promise.props({
'a': exampleLimited('a'),
'b': exampleLimited('b'),
'c': exampleLimited('c'),
'd': exampleLimited('d'),
'e': exampleLimited('e'),
'f': exampleLimited('f'),
'g': exampleLimited('g'),
'h': exampleLimited('h'),
'i': exampleLimited('i'),
'j': exampleLimited('j'),
'k': exampleLimited('k'),
'l': exampleLimited('l')
})
const intervals = getIntervalsBetweenDates(timeLog)
console.log(intervals)
const invalidIntervals = filter(intervals, (interval) => interval < bufferInterval)
const totalTime = arraySum(intervals)
t.is(intervals.length, promisesLength)
t.deepEqual(invalidIntervals, [])
t.deepEqual(totalTime >= bufferInterval * promisesLength, true)
})
I'm still getting the issue even with the example
change.
[ 99, 98, 105, 106, 119, 106, 105, 105, 101, 106, 100 ]
2 passed
2 failed
using Promise.props with 4 promises
t.deepEqual(invalidIntervals, [])
|
[99]
Generator.next (<anonymous>)
using Promise.props with 12 promises
t.deepEqual(invalidIntervals, [])
|
[99,98]
Generator.next (<anonymous>)