ChatApiHandlerTests.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. const SandboxedModule = require('sandboxed-module')
  2. const path = require('path')
  3. const sinon = require('sinon')
  4. const { expect } = require('chai')
  5. const { RequestFailedError } = require('@overleaf/fetch-utils')
  6. const MODULE_PATH = path.join(
  7. __dirname,
  8. '../../../../app/src/Features/Chat/ChatApiHandler'
  9. )
  10. describe('ChatApiHandler', function () {
  11. beforeEach(function () {
  12. this.settings = {
  13. apis: {
  14. chat: {
  15. internal_url: 'http://chat.overleaf.env',
  16. },
  17. },
  18. }
  19. this.FetchUtils = {
  20. fetchJson: sinon.stub(),
  21. fetchNothing: sinon.stub().resolves(),
  22. }
  23. this.ChatApiHandler = SandboxedModule.require(MODULE_PATH, {
  24. requires: {
  25. '@overleaf/settings': this.settings,
  26. '@overleaf/fetch-utils': this.FetchUtils,
  27. },
  28. })
  29. this.project_id = '3213213kl12j'
  30. this.user_id = '2k3jlkjs9'
  31. this.content = 'my message here'
  32. })
  33. describe('sendGlobalMessage', function () {
  34. describe('successfully', function () {
  35. beforeEach(async function () {
  36. this.message = { mock: 'message' }
  37. this.FetchUtils.fetchJson.resolves(this.message)
  38. this.result = await this.ChatApiHandler.promises.sendGlobalMessage(
  39. this.project_id,
  40. this.user_id,
  41. this.content
  42. )
  43. })
  44. it('should post the data to the chat api', function () {
  45. this.FetchUtils.fetchJson.should.have.been.calledWith(
  46. sinon.match(
  47. url =>
  48. url.toString() ===
  49. `${this.settings.apis.chat.internal_url}/project/${this.project_id}/messages`
  50. ),
  51. {
  52. method: 'POST',
  53. json: {
  54. content: this.content,
  55. user_id: this.user_id,
  56. },
  57. }
  58. )
  59. })
  60. it('should return the message from the post', function () {
  61. expect(this.result).to.deep.equal(this.message)
  62. })
  63. })
  64. describe('with a non-success status code', function () {
  65. beforeEach(async function () {
  66. this.error = new RequestFailedError('some-url', {}, { status: 500 })
  67. this.FetchUtils.fetchJson.rejects(this.error)
  68. await expect(
  69. this.ChatApiHandler.promises.sendGlobalMessage(
  70. this.project_id,
  71. this.user_id,
  72. this.content
  73. )
  74. ).to.be.rejectedWith(this.error)
  75. })
  76. })
  77. })
  78. describe('getGlobalMessages', function () {
  79. beforeEach(function () {
  80. this.messages = [{ mock: 'message' }]
  81. this.limit = 30
  82. this.before = '1234'
  83. })
  84. describe('successfully', function () {
  85. beforeEach(async function () {
  86. this.FetchUtils.fetchJson.resolves(this.messages)
  87. this.result = await this.ChatApiHandler.promises.getGlobalMessages(
  88. this.project_id,
  89. this.limit,
  90. this.before
  91. )
  92. })
  93. it('should make get request for room to chat api', function () {
  94. this.FetchUtils.fetchJson.should.have.been.calledWith(
  95. sinon.match(
  96. url =>
  97. url.toString() ===
  98. `${this.settings.apis.chat.internal_url}/project/${this.project_id}/messages?limit=${this.limit}&before=${this.before}`
  99. )
  100. )
  101. })
  102. it('should return the messages from the request', function () {
  103. expect(this.result).to.deep.equal(this.messages)
  104. })
  105. })
  106. describe('with failure error code', function () {
  107. beforeEach(async function () {
  108. this.error = new RequestFailedError('some-url', {}, { status: 500 })
  109. this.FetchUtils.fetchJson.rejects(this.error)
  110. await expect(
  111. this.ChatApiHandler.getGlobalMessages(
  112. this.project_id,
  113. this.limit,
  114. this.before
  115. )
  116. ).to.be.rejectedWith(this.error)
  117. })
  118. })
  119. })
  120. describe('duplicateCommentThreads', function () {
  121. beforeEach(async function () {
  122. this.FetchUtils.fetchJson.resolves(
  123. (this.mapping = {
  124. 'comment-thread-1': 'comment-thread-1-dup',
  125. 'comment-thread-2': 'comment-thread-2-dup',
  126. 'comment-thread-3': 'comment-thread-3-dup',
  127. })
  128. )
  129. this.threads = [
  130. 'comment-thread-1',
  131. 'comment-thread-2',
  132. 'comment-thread-3',
  133. ]
  134. this.result = await this.ChatApiHandler.promises.duplicateCommentThreads(
  135. this.project_id,
  136. this.threads
  137. )
  138. })
  139. it('should make a post request to the chat api', function () {
  140. expect(this.FetchUtils.fetchJson).to.have.been.calledWith(
  141. sinon.match(
  142. url =>
  143. url.toString() ===
  144. `${this.settings.apis.chat.internal_url}/project/${this.project_id}/duplicate-comment-threads`
  145. ),
  146. {
  147. method: 'POST',
  148. json: {
  149. threads: this.threads,
  150. },
  151. }
  152. )
  153. })
  154. it('should return the thread mapping', function () {
  155. expect(this.result).to.deep.equal(this.mapping)
  156. })
  157. })
  158. describe('generateThreadData', async function () {
  159. beforeEach(async function () {
  160. this.FetchUtils.fetchJson.resolves(
  161. (this.chatResponse = {
  162. 'comment-thread-1': {
  163. messages: [
  164. {
  165. content: 'message 1',
  166. timestamp: '2024-01-01T00:00:00.000Z',
  167. user_id: 'user-1',
  168. },
  169. ],
  170. },
  171. 'comment-thread-2': {
  172. messages: [
  173. {
  174. content: 'message 2',
  175. timestamp: '2024-01-01T00:00:00.000Z',
  176. user_id: 'user-2',
  177. },
  178. ],
  179. },
  180. })
  181. )
  182. // Chat won't return threads that couldn't be found, so response can have
  183. // fewer threads
  184. this.threads = [
  185. 'comment-thread-1',
  186. 'comment-thread-2',
  187. 'comment-thread-3',
  188. ]
  189. this.result = await this.ChatApiHandler.promises.generateThreadData(
  190. this.project_id,
  191. this.threads
  192. )
  193. })
  194. it('should make a post request to the chat api', function () {
  195. expect(this.FetchUtils.fetchJson).to.have.been.calledWith(
  196. sinon.match(
  197. url =>
  198. url.toString() ===
  199. `${this.settings.apis.chat.internal_url}/project/${this.project_id}/generate-thread-data`
  200. ),
  201. {
  202. method: 'POST',
  203. json: {
  204. threads: this.threads,
  205. },
  206. }
  207. )
  208. })
  209. it('should return the thread data', function () {
  210. expect(this.result).to.deep.equal(this.chatResponse)
  211. })
  212. })
  213. })