http.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. const Path = require('node:path')
  2. const SandboxedModule = require('sandboxed-module')
  3. const sinon = require('sinon')
  4. const MODULE_PATH = Path.join(__dirname, '../../../http.js')
  5. describe('http.monitor', function () {
  6. beforeEach(function () {
  7. this.req = {
  8. method: 'POST',
  9. url: '/project/1234/cleanup',
  10. headers: {
  11. 'content-length': '123',
  12. },
  13. route: {
  14. path: '/project/:id/cleanup',
  15. },
  16. }
  17. this.originalResponseEnd = sinon.stub()
  18. this.res = {
  19. end: this.originalResponseEnd,
  20. }
  21. this.data = 'data'
  22. this.logger = {
  23. debug: sinon.stub(),
  24. info: sinon.stub(),
  25. warn: sinon.stub(),
  26. }
  27. this.Metrics = {
  28. timing: sinon.stub(),
  29. summary: sinon.stub(),
  30. }
  31. this.clock = sinon.useFakeTimers()
  32. this.http = SandboxedModule.require(MODULE_PATH, {
  33. requires: {
  34. './index': this.Metrics,
  35. },
  36. })
  37. })
  38. afterEach(function () {
  39. this.clock.restore()
  40. })
  41. describe('with the default options', function () {
  42. beforeEach('set up the monitor', function (done) {
  43. this.http.monitor(this.logger)(this.req, this.res, done)
  44. })
  45. describe('after a simple request', function () {
  46. endRequest()
  47. expectOriginalEndCalled()
  48. expectMetrics()
  49. it('logs the request at the DEBUG level', function () {
  50. sinon.assert.calledWith(
  51. this.logger.debug,
  52. { req: this.req, res: this.res, responseTimeMs: 500 },
  53. '%s %s',
  54. this.req.method,
  55. this.req.url
  56. )
  57. })
  58. })
  59. describe('when logging is disabled', function () {
  60. beforeEach('disable logging', function () {
  61. this.req.logger.disable()
  62. })
  63. endRequest()
  64. expectOriginalEndCalled()
  65. expectMetrics()
  66. it("doesn't log the request", function () {
  67. sinon.assert.notCalled(this.logger.debug)
  68. })
  69. })
  70. describe('with custom log fields', function () {
  71. beforeEach('add custom fields', function () {
  72. this.req.logger.addFields({ a: 1, b: 2 })
  73. })
  74. endRequest()
  75. it('logs the request with the custom log fields', function () {
  76. sinon.assert.calledWith(
  77. this.logger.debug,
  78. { req: this.req, res: this.res, responseTimeMs: 500, a: 1, b: 2 },
  79. '%s %s',
  80. this.req.method,
  81. this.req.url
  82. )
  83. })
  84. })
  85. describe('when setting the log level', function () {
  86. beforeEach('set custom level', function () {
  87. this.req.logger.setLevel('warn')
  88. })
  89. endRequest()
  90. it('logs the request at the custom level', function () {
  91. sinon.assert.calledWith(
  92. this.logger.warn,
  93. { req: this.req, res: this.res, responseTimeMs: 500 },
  94. '%s %s',
  95. this.req.method,
  96. this.req.url
  97. )
  98. })
  99. })
  100. })
  101. describe('with a different default log level', function () {
  102. beforeEach('set up the monitor', function (done) {
  103. this.http.monitor(this.logger, 'info')(this.req, this.res, done)
  104. })
  105. endRequest()
  106. it('logs the request at that level', function () {
  107. sinon.assert.calledWith(
  108. this.logger.info,
  109. { req: this.req, res: this.res, responseTimeMs: 500 },
  110. '%s %s',
  111. this.req.method,
  112. this.req.url
  113. )
  114. })
  115. })
  116. })
  117. function endRequest() {
  118. beforeEach('end the request', function () {
  119. this.clock.tick(500)
  120. this.res.end(this.data)
  121. })
  122. }
  123. function expectOriginalEndCalled() {
  124. it('calls the original res.end()', function () {
  125. sinon.assert.calledWith(this.originalResponseEnd, this.data)
  126. })
  127. }
  128. function expectMetrics() {
  129. it('records the response time', function () {
  130. sinon.assert.calledWith(this.Metrics.timing, 'http_request', 500, null, {
  131. method: this.req.method,
  132. status_code: this.res.status_code,
  133. path: 'project_id_cleanup',
  134. })
  135. })
  136. it('records the request size', function () {
  137. sinon.assert.calledWith(
  138. this.Metrics.summary,
  139. 'http_request_size_bytes',
  140. 123,
  141. {
  142. method: this.req.method,
  143. status_code: this.res.status_code,
  144. path: 'project_id_cleanup',
  145. }
  146. )
  147. })
  148. }