timeAsyncMethodTests.coffee 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. require('coffee-script')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. path = require('path')
  6. modulePath = path.join __dirname, '../../../timeAsyncMethod.coffee'
  7. SandboxedModule = require('sandboxed-module')
  8. sinon = require("sinon")
  9. describe 'timeAsyncMethod', ->
  10. beforeEach ->
  11. @Timer = {done: sinon.stub()}
  12. @TimerConstructor = sinon.stub().returns(@Timer)
  13. @metrics = {
  14. Timer: @TimerConstructor
  15. inc: sinon.stub()
  16. }
  17. @timeAsyncMethod = SandboxedModule.require modulePath, requires:
  18. './metrics': @metrics
  19. @testObject = {
  20. nextNumber: (n, callback=(err, result)->) ->
  21. setTimeout(
  22. () ->
  23. callback(null, n+1)
  24. , 100
  25. )
  26. }
  27. it 'should have the testObject behave correctly before wrapping', (done) ->
  28. @testObject.nextNumber 2, (err, result) ->
  29. expect(err).to.not.exist
  30. expect(result).to.equal 3
  31. done()
  32. it 'should wrap method without error', (done) ->
  33. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  34. done()
  35. it 'should transparently wrap method invocation in timer', (done) ->
  36. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  37. @testObject.nextNumber 2, (err, result) =>
  38. expect(err).to.not.exist
  39. expect(result).to.equal 3
  40. expect(@TimerConstructor.callCount).to.equal 1
  41. expect(@Timer.done.callCount).to.equal 1
  42. done()
  43. it 'should increment success count', (done) ->
  44. @metrics.inc = sinon.stub()
  45. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  46. @testObject.nextNumber 2, (err, result) =>
  47. expect(@metrics.inc.callCount).to.equal 1
  48. expect(@metrics.inc.calledWith('someContext_result', 1, { method: 'TestObject_nextNumber', status: 'success'})).to.equal true
  49. done()
  50. describe 'when base method produces an error', ->
  51. beforeEach ->
  52. @metrics.inc = sinon.stub()
  53. @testObject.nextNumber = (n, callback=(err, result)->) ->
  54. setTimeout(
  55. () ->
  56. callback(new Error('woops'))
  57. , 100
  58. )
  59. it 'should propagate the error transparently', (done) ->
  60. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  61. @testObject.nextNumber 2, (err, result) =>
  62. expect(err).to.exist
  63. expect(err).to.be.instanceof Error
  64. expect(result).to.not.exist
  65. done()
  66. it 'should increment failure count', (done) ->
  67. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  68. @testObject.nextNumber 2, (err, result) =>
  69. expect(@metrics.inc.callCount).to.equal 1
  70. expect(@metrics.inc.calledWith('someContext_result', 1, { method: 'TestObject_nextNumber', status: 'failed'})).to.equal true
  71. done()
  72. describe 'when a logger is supplied', ->
  73. beforeEach ->
  74. @logger = {log: sinon.stub()}
  75. it 'should also call logger.log', (done) ->
  76. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject', @logger
  77. @testObject.nextNumber 2, (err, result) =>
  78. expect(err).to.not.exist
  79. expect(result).to.equal 3
  80. expect(@TimerConstructor.callCount).to.equal 1
  81. expect(@Timer.done.callCount).to.equal 1
  82. expect(@logger.log.callCount).to.equal 1
  83. done()
  84. describe 'when the wrapper cannot be applied', ->
  85. beforeEach ->
  86. it 'should raise an error', ->
  87. badWrap = () =>
  88. @timeAsyncMethod @testObject, 'DEFINITELY_NOT_A_REAL_METHOD', 'someContext.TestObject'
  89. expect(badWrap).to.throw(
  90. /^.*expected object property 'DEFINITELY_NOT_A_REAL_METHOD' to be a function.*$/
  91. )
  92. describe 'when the wrapped function is not using a callback', ->
  93. beforeEach ->
  94. @realMethod = sinon.stub().returns(42)
  95. @testObject.nextNumber = @realMethod
  96. it 'should not throw an error', ->
  97. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  98. badCall = () =>
  99. @testObject.nextNumber 2
  100. expect(badCall).to.not.throw(Error)
  101. it 'should call the underlying method', ->
  102. @timeAsyncMethod @testObject, 'nextNumber', 'someContext.TestObject'
  103. result = @testObject.nextNumber(12)
  104. expect(@realMethod.callCount).to.equal 1
  105. expect(@realMethod.calledWith(12)).to.equal true
  106. expect(result).to.equal 42