UNPKG

el-borracho-stats

Version:

REST and SSE API and worker producing daily and all-time totals for Bull queues

376 lines (288 loc) 11.7 kB
ElBorrachoStats = require "../src/models/stats" redis = require "ioredis" errify = require "errify" mocha = require "mocha" {expect} = require "chai" {spy} = require "sinon" spy.on = spy dontCatch = -> listeners = process.listeners "uncaughtException" process.removeAllListeners "uncaughtException" restore: -> process.on "uncaughtException", listener for listener in listeners describe "ElBorrachoStats", -> queuename = "tacos" expire = 2 client = null instance = null beforeEach -> client = redis.createClient() instance = new ElBorrachoStats {redis: client, queuename, expire} afterEach -> client = null instance = null describe "##incrementCompleted", -> it "should increment completed", -> count = instance.completed instance.incrementCompleted() expect(instance.completed).to.equal count + 1 describe "##incrementFailed", -> it "should increment failed", -> count = instance.failed instance.incrementFailed() expect(instance.failed).to.equal count + 1 describe "##clearCompleted", -> it "should clear completed", -> instance.completed = 2 instance.clearCompleted() expect(instance.completed).to.equal 0 describe "##clearFailed", -> it "should clear failed", -> instance.failed = 2 instance.clearFailed() expect(instance.failed).to.equal 0 describe "##constructor", -> it "should throw an error unless redisClient is passed in", -> newInstance = -> new ElBorrachoStats expect(newInstance).to.throw Error it "should initialize namespace to default value", -> expect(instance.namespace).to.equal "stat" it "should initialize namespace to specified value", -> namespace = "universe" instance = new ElBorrachoStats {redis: client, namespace} expect(instance.namespace).to.equal namespace it "should initialize queuename to specified value", -> instance = new ElBorrachoStats {redis: client, queuename} expect(instance.queuename).to.equal queuename it "should initialize the prefix", -> queuename = "tacos" instance = new ElBorrachoStats {redis: client, queuename} expect(instance.prefix).to.equal instance.prefixForQueue queuename it "should initialize the statistician", -> expect(instance.statistician).to.equal "#{instance.prefix}:statistician" it "should initialize expire to default value", -> instance = new ElBorrachoStats {redis: client, queuename} expect(instance.expire).to.equal 60 it "should initialize expire to specified value", -> expire = 100 instance = new ElBorrachoStats {redis: client, expire} expect(instance.expire).to.equal expire it "should initialize overallTotal to default value", -> expect(instance.overallTotal).to.be.true it "should initialize overallTotal to specified value", -> overallTotal = false instance = new ElBorrachoStats {redis: client, overallTotal} expect(instance.overallTotal).to.equal overallTotal describe "lock methods", -> _runTest = null before -> # https://github.com/mochajs/mocha/issues/1985 _runTest = mocha.Runner.prototype.runTest mocha.Runner.prototype.runTest = -> @allowUncaught = true _runTest.apply this, arguments after -> mocha.Runner.prototype.runTest = _runTest beforeEach (done) -> instance.lock done return afterEach (done) -> instance.unlock done return describe "##lock", -> it "should set the lock in redis", (done) -> ideally = errify done await client.keys instance.statistician, ideally defer lock expect(lock?[0]).to.exist done() it "should set the lock to expire in redis", (done) -> ideally = errify done await client.ttl instance.statistician, ideally defer ttl expect(ttl).to.equal expire done() it "should throw an error if already locked", (done) -> process.removeAllListeners "uncaughtException" await process.once "uncaughtException", defer err instance.lock() expect(err).to.be.an.instanceof Error expect(err.toString()).to.match /exists/ done() describe "##updateLock", -> it "should update the lock TTL", (done) -> ideally = errify done instance.expire = 60 await instance.updateLock ideally defer() await client.ttl instance.statistician, ideally defer ttl expect(ttl).to.equal instance.expire done() describe "##unlock", -> it "should throw an error if error in lock removal", (done) -> handler = dontCatch() _del = client.del client.del = (_, callback) -> callback new Error "fake" expectation = (err) -> expect(err).to.be.an.instanceof Error expect(err.toString()).to.match /fake/ client.del = _del handler.restore() done() process.once "uncaughtException", expectation instance.unlock() it "should remove the lock", (done) -> ideally = errify done await instance.unlock ideally defer() await client.keys instance.statistician, ideally defer result expect(result).to.be.empty done() describe "##update", -> statStates = [ "completed" "failed" ] for state in statStates then do (state) -> today = ElBorrachoStats::shortDate new Date statKeys = [ "bull:stat:tacos:#{state}" "bull:stat:tacos:#{state}:#{today}" "bull:stat:all:#{state}" "bull:stat:all:#{state}:#{today}" ] beforeEach (done) -> multi = client.multi() multi.del key for key in statKeys multi.exec done return it "should throw an error if lock update failed", (done) -> handler = dontCatch() _expire = client.expire client.expire = (_, __, callback) -> callback new Error "fake" expectation = (err) -> expect(err).to.be.an.instanceof Error expect(err.toString()).to.match /fake/ client.expire = _expire handler.restore() done() process.once "uncaughtException", expectation instance.update() it "should not increase totals for #{state} if empty", (done) -> ideally = errify done {prefix} = instance await instance.update ideally defer() await client.keys "#{prefix}:#{state}*", ideally defer keys expect(keys).to.be.empty done() it "should increase alltime queue totals for #{state}", (done) -> ideally = errify done {prefix} = instance expected = instance[state] = 2 await instance.update ideally defer() await client.get "#{prefix}:#{state}", ideally defer result expect(Number result).to.equal expected done() it "should increase todays queue totals for #{state}", (done) -> ideally = errify done {prefix} = instance expected = instance[state] = 4 await instance.update ideally defer() await client.get "#{prefix}:#{state}:#{today}", ideally defer result expect(Number result).to.equal expected done() it "should not increase alltime totals for #{state} if overallTotal option is false", (done) -> ideally = errify done instance = new ElBorrachoStats {redis: client, queuename, expire, overallTotal: false} prefix = "bull:#{instance.namespace}:all" instance[state] = 4 await instance.update ideally defer() await client.get "#{prefix}:#{state}", ideally defer result expect(Number result).to.equal 0 done() it "should increase alltime overall totals for #{state}", (done) -> ideally = errify done prefix = "bull:#{instance.namespace}:all" expected = instance[state] = 4 await instance.update ideally defer() await client.get "#{prefix}:#{state}", ideally defer result expect(Number result).to.equal expected done() it "should increase todays overall totals for #{state}", (done) -> ideally = errify done prefix = "bull:#{instance.namespace}:all" expected = instance[state] = 6 await instance.update ideally defer() await client.get "#{prefix}:#{state}:#{today}", ideally defer result expect(Number result).to.equal expected done() it "should throw an error if atomic #{state} update failed", (done) -> handler = dontCatch() _multi = client.multi.bind client client.multi = -> m = _multi() m.exec = (callback) -> callback new Error "fake" m prefix = instance.prefix = " " expected = instance[state] = 2 expectation = (err) -> expect(err).to.be.an.instanceof Error expect(err.toString()).to.match /fake/ client.multi = _multi handler.restore() done() process.once "uncaughtException", expectation instance.update() describe "fetch methods", -> today = ElBorrachoStats::shortDate new Date beforeEach (done) -> statStates = [ "completed" "failed" ] statKeys = [] for state in statStates statKeys = statKeys.concat [ "bull:stat:tacos:#{state}" "bull:stat:tacos:#{state}:#{today}" "bull:stat:all:#{state}" "bull:stat:all:#{state}:#{today}" ] multi = client.multi() multi.del key for key in statKeys multi.exec done return describe "##fetch", -> it "should callback with totals for queue", (done) -> ideally = errify done expectedCompleted = instance.completed = 6 expectedFailed = instance.failed = 3 await instance.update ideally defer() await instance.fetch ideally defer {completed, failed} expect(Number completed).to.equal expectedCompleted expect(Number failed).to.equal expectedFailed done() describe "##fetchForAll", -> it "should callback with overall totals", (done) -> ideally = errify done expectedCompleted = instance.completed = 8 expectedFailed = instance.failed = 4 await instance.update ideally defer() await instance.fetchForAll ideally defer {completed, failed} expect(Number completed).to.equal expectedCompleted expect(Number failed).to.equal expectedFailed done() describe "##fetchHistory", -> it "should callback with history for queue", (done) -> ideally = errify done expectedCompleted = instance.completed = 6 expectedFailed = instance.failed = 3 await instance.update ideally defer() await instance.fetchHistory null, null, ideally defer history completedToday = do -> return stat for stat in history when stat.date is today and stat.type is "completed" failedToday = do -> return stat for stat in history when stat.date is today and stat.type is "failed" expect(Number completedToday.value).to.equal expectedCompleted expect(Number failedToday.value).to.equal expectedFailed done() describe "shortDate", -> it "should return a date in YYYY-MM-DD format", (done) -> datestr = "2002-02-02" date = instance.shortDate new Date datestr expect(date).to.equal datestr done()