OneTimeTokenHandlerTests.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. const sinon = require('sinon')
  2. const { expect } = require('chai')
  3. const Errors = require('../../../../app/src/Features/Errors/Errors')
  4. const {
  5. connectionPromise,
  6. cleanupTestDatabase,
  7. } = require('../../../../app/src/infrastructure/mongodb')
  8. const OneTimeTokenHandler = require('../../../../app/src/Features/Security/OneTimeTokenHandler')
  9. describe('OneTimeTokenHandler', function () {
  10. before(async function () {
  11. await connectionPromise
  12. })
  13. beforeEach(cleanupTestDatabase)
  14. beforeEach(function () {
  15. this.clock = sinon.useFakeTimers()
  16. })
  17. afterEach(function () {
  18. this.clock.restore()
  19. })
  20. describe('getNewToken', function () {
  21. it('generates a token and stores it in the database', async function () {
  22. const token = await OneTimeTokenHandler.promises.getNewToken(
  23. 'password',
  24. 'mock-data-to-store'
  25. )
  26. const { data } = await OneTimeTokenHandler.promises.peekValueFromToken(
  27. 'password',
  28. token
  29. )
  30. expect(data).to.equal('mock-data-to-store')
  31. })
  32. it('expires the generated token after 1 hour', async function () {
  33. const token = await OneTimeTokenHandler.promises.getNewToken(
  34. 'password',
  35. 'mock-data-to-store'
  36. )
  37. this.clock.tick('25:00:00')
  38. await expect(
  39. OneTimeTokenHandler.promises.peekValueFromToken('password', token)
  40. ).to.be.rejectedWith(Errors.NotFoundError)
  41. })
  42. it('accepts an expiresIn parameter', async function () {
  43. const token = await OneTimeTokenHandler.promises.getNewToken(
  44. 'password',
  45. 'mock-data-to-store',
  46. { expiresIn: 42 }
  47. )
  48. this.clock.tick('00:30')
  49. const { data } = await OneTimeTokenHandler.promises.peekValueFromToken(
  50. 'password',
  51. token
  52. )
  53. expect(data).to.equal('mock-data-to-store')
  54. this.clock.tick('00:15')
  55. await expect(
  56. OneTimeTokenHandler.promises.peekValueFromToken('password', token)
  57. ).to.be.rejectedWith(Errors.NotFoundError)
  58. })
  59. })
  60. describe('peekValueFromToken', function () {
  61. it('should return the data and peek count', async function () {
  62. const data = { email: 'some-mock-data' }
  63. const token = await OneTimeTokenHandler.promises.getNewToken(
  64. 'password',
  65. data
  66. )
  67. const result = await OneTimeTokenHandler.promises.peekValueFromToken(
  68. 'password',
  69. token
  70. )
  71. expect(result).to.deep.equal({
  72. data,
  73. remainingPeeks: OneTimeTokenHandler.MAX_PEEKS - 1,
  74. })
  75. })
  76. it('should throw a NotFoundError if the token is not found', async function () {
  77. await expect(
  78. OneTimeTokenHandler.promises.peekValueFromToken('password', 'bad-token')
  79. ).to.be.rejectedWith(Errors.NotFoundError)
  80. })
  81. it('should stop returning the data after the peek count is exceeded', async function () {
  82. const data = { email: 'some-mock-data' }
  83. const token = await OneTimeTokenHandler.promises.getNewToken(
  84. 'password',
  85. data
  86. )
  87. for (let peeks = 1; peeks <= OneTimeTokenHandler.MAX_PEEKS; peeks++) {
  88. const result = await OneTimeTokenHandler.promises.peekValueFromToken(
  89. 'password',
  90. token
  91. )
  92. expect(result).to.deep.equal({
  93. data,
  94. remainingPeeks: OneTimeTokenHandler.MAX_PEEKS - peeks,
  95. })
  96. }
  97. await expect(
  98. OneTimeTokenHandler.promises.peekValueFromToken('password', token)
  99. ).to.be.rejectedWith(Errors.NotFoundError)
  100. })
  101. })
  102. describe('expireToken', function () {
  103. it('should expire the token immediately', async function () {
  104. const token = await OneTimeTokenHandler.promises.getNewToken(
  105. 'password',
  106. 'mock-data'
  107. )
  108. await OneTimeTokenHandler.promises.expireToken('password', token)
  109. await expect(
  110. OneTimeTokenHandler.promises.peekValueFromToken('password', token)
  111. ).to.be.rejectedWith(Errors.NotFoundError)
  112. })
  113. })
  114. describe('getValueFromTokenAndExpire', function () {
  115. it('should return the value and expire the token', async function () {
  116. const token = await OneTimeTokenHandler.promises.getNewToken(
  117. 'password',
  118. 'mock-data'
  119. )
  120. const data =
  121. await OneTimeTokenHandler.promises.getValueFromTokenAndExpire(
  122. 'password',
  123. token
  124. )
  125. expect(data).to.equal('mock-data')
  126. await expect(
  127. OneTimeTokenHandler.promises.peekValueFromToken('password', token)
  128. ).to.be.rejectedWith(Errors.NotFoundError)
  129. })
  130. it('should throw a NotFoundError if the token is not found', async function () {
  131. await expect(
  132. OneTimeTokenHandler.promises.getValueFromTokenAndExpire(
  133. 'password',
  134. 'bad-token'
  135. )
  136. ).to.be.rejectedWith(Errors.NotFoundError)
  137. })
  138. })
  139. })