Notifications.test.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import { beforeEach, describe, expect, it, vi } from 'vitest'
  2. import { ObjectId } from 'mongodb-legacy'
  3. import assert from 'node:assert'
  4. const modulePath = '../../../app/js/Notifications.js'
  5. const userId = '51dc93e6fb625a261300003b'
  6. const notificationId = '574ee8d6f40c3a244e704249'
  7. const notificationKey = 'notification-key'
  8. describe('Notifications Tests', () => {
  9. let countStub,
  10. db,
  11. deleteOneStub,
  12. findStub,
  13. findToArrayStub,
  14. notifications,
  15. stubbedNotification,
  16. stubbedNotificationArray,
  17. updateOneStub
  18. beforeEach(async () => {
  19. findToArrayStub = vi.fn()
  20. findStub = vi.fn().mockReturnValue({ toArray: findToArrayStub })
  21. countStub = vi.fn()
  22. updateOneStub = vi.fn()
  23. deleteOneStub = vi.fn()
  24. db = {
  25. notifications: {
  26. find: findStub,
  27. count: countStub,
  28. updateOne: updateOneStub,
  29. deleteOne: deleteOneStub,
  30. },
  31. }
  32. vi.doMock('@overleaf/settings', () => ({}))
  33. vi.doMock('../../../app/js/mongodb', () => ({
  34. db,
  35. ObjectId,
  36. }))
  37. notifications = await import(modulePath)
  38. stubbedNotification = {
  39. user_id: new ObjectId(userId),
  40. key: 'notification-key',
  41. messageOpts: 'some info',
  42. templateKey: 'template-key',
  43. }
  44. stubbedNotificationArray = [stubbedNotification]
  45. })
  46. describe('getUserNotifications', () => {
  47. it('should find all notifications and return i', async () => {
  48. findToArrayStub.mockResolvedValue(stubbedNotificationArray)
  49. const result = await notifications.getUserNotifications(userId)
  50. result.should.equal(stubbedNotificationArray)
  51. assert.deepEqual(findStub.mock.calls[0][0], {
  52. user_id: new ObjectId(userId),
  53. templateKey: { $exists: true },
  54. })
  55. })
  56. })
  57. describe('addNotification', () => {
  58. let expectedDocument, expectedQuery
  59. beforeEach(() => {
  60. stubbedNotification = {
  61. user_id: new ObjectId(userId),
  62. key: 'notification-key',
  63. messageOpts: 'some info',
  64. templateKey: 'template-key',
  65. }
  66. expectedDocument = {
  67. user_id: stubbedNotification.user_id,
  68. key: 'notification-key',
  69. messageOpts: 'some info',
  70. templateKey: 'template-key',
  71. }
  72. expectedQuery = {
  73. user_id: stubbedNotification.user_id,
  74. key: 'notification-key',
  75. }
  76. updateOneStub.mockResolvedValue()
  77. countStub.mockResolvedValue(0)
  78. })
  79. it('should insert the notification into the collection', async () => {
  80. await notifications.addNotification(userId, stubbedNotification)
  81. expect(updateOneStub).toHaveBeenCalledWith(
  82. expectedQuery,
  83. { $set: expectedDocument },
  84. { upsert: true }
  85. )
  86. })
  87. describe('when there is an existing notification', done => {
  88. beforeEach(() => {
  89. countStub.mockResolvedValue(1)
  90. })
  91. it('should fail to insert', async () => {
  92. await notifications.addNotification(userId, stubbedNotification)
  93. expect(updateOneStub).not.toHaveBeenCalled()
  94. })
  95. it('should update the key if forceCreate is true', async () => {
  96. stubbedNotification.forceCreate = true
  97. await notifications.addNotification(userId, stubbedNotification)
  98. expect(updateOneStub).toHaveBeenCalledWith(
  99. expectedQuery,
  100. { $set: expectedDocument },
  101. { upsert: true }
  102. )
  103. })
  104. })
  105. describe('when the notification is set to expire', () => {
  106. beforeEach(() => {
  107. stubbedNotification = {
  108. user_id: new ObjectId(userId),
  109. key: 'notification-key',
  110. messageOpts: 'some info',
  111. templateKey: 'template-key',
  112. expires: '2922-02-13T09:32:56.289Z',
  113. }
  114. expectedDocument = {
  115. user_id: stubbedNotification.user_id,
  116. key: 'notification-key',
  117. messageOpts: 'some info',
  118. templateKey: 'template-key',
  119. expires: new Date(stubbedNotification.expires),
  120. }
  121. expectedQuery = {
  122. user_id: stubbedNotification.user_id,
  123. key: 'notification-key',
  124. }
  125. })
  126. it('should add an `expires` Date field to the document', async () => {
  127. await notifications.addNotification(userId, stubbedNotification)
  128. expect(updateOneStub).toHaveBeenCalledWith(
  129. expectedQuery,
  130. { $set: expectedDocument },
  131. { upsert: true }
  132. )
  133. })
  134. })
  135. describe('when the notification has a nonsensical expires field', () => {
  136. beforeEach(() => {
  137. stubbedNotification = {
  138. user_id: new ObjectId(userId),
  139. key: 'notification-key',
  140. messageOpts: 'some info',
  141. templateKey: 'template-key',
  142. expires: 'WAT',
  143. }
  144. expectedDocument = {
  145. user_id: stubbedNotification.user_id,
  146. key: 'notification-key',
  147. messageOpts: 'some info',
  148. templateKey: 'template-key',
  149. expires: new Date(stubbedNotification.expires),
  150. }
  151. })
  152. it('should produce an error', async () => {
  153. await expect(
  154. notifications.addNotification(userId, stubbedNotification)
  155. ).to.eventually.be.rejectedWith(Error)
  156. expect(updateOneStub).not.toHaveBeenCalled()
  157. })
  158. })
  159. })
  160. describe('removeNotificationId', () => {
  161. it('should mark the notification id as read', async () => {
  162. updateOneStub.mockResolvedValue(null)
  163. await notifications.removeNotificationId(userId, notificationId)
  164. const searchOps = {
  165. user_id: new ObjectId(userId),
  166. _id: new ObjectId(notificationId),
  167. }
  168. const updateOperation = {
  169. $unset: { templateKey: true, messageOpts: true },
  170. }
  171. assert.deepEqual(updateOneStub.mock.calls[0][0], searchOps)
  172. assert.deepEqual(updateOneStub.mock.calls[0][1], updateOperation)
  173. })
  174. })
  175. describe('removeNotificationKey', () => {
  176. it('should mark the notification key as read', async () => {
  177. updateOneStub.mockResolvedValue(null)
  178. await notifications.removeNotificationKey(userId, notificationKey)
  179. const searchOps = {
  180. user_id: new ObjectId(userId),
  181. key: notificationKey,
  182. }
  183. const updateOperation = {
  184. $unset: { templateKey: true },
  185. }
  186. assert.deepEqual(updateOneStub.mock.calls[0][0], searchOps)
  187. assert.deepEqual(updateOneStub.mock.calls[0][1], updateOperation)
  188. })
  189. })
  190. describe('removeNotificationByKeyOnly', () => {
  191. it('should mark the notification key as read', async () => {
  192. updateOneStub.mockResolvedValue(null)
  193. await notifications.removeNotificationByKeyOnly(notificationKey)
  194. const searchOps = { key: notificationKey }
  195. const updateOperation = { $unset: { templateKey: true } }
  196. assert.deepEqual(updateOneStub.mock.calls[0][0], searchOps)
  197. assert.deepEqual(updateOneStub.mock.calls[0][1], updateOperation)
  198. })
  199. })
  200. describe('deleteNotificationByKeyOnly', () => {
  201. it('should completely remove the notification', async () => {
  202. deleteOneStub.mockResolvedValue()
  203. await notifications.deleteNotificationByKeyOnly(notificationKey)
  204. const searchOps = { key: notificationKey }
  205. expect(deleteOneStub.mock.calls[0][0]).toEqual(searchOps)
  206. })
  207. })
  208. })