TpdsUpdateSenderTests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. const { ObjectId } = require('mongodb')
  2. const SandboxedModule = require('sandboxed-module')
  3. const chai = require('chai')
  4. const path = require('path')
  5. const sinon = require('sinon')
  6. chai.should()
  7. const modulePath = path.join(
  8. __dirname,
  9. '../../../../app/src/Features/ThirdPartyDataStore/TpdsUpdateSender.js'
  10. )
  11. const projectId = 'project_id_here'
  12. const userId = ObjectId()
  13. const readOnlyRef = ObjectId()
  14. const collaberatorRef = ObjectId()
  15. const projectName = 'project_name_here'
  16. const thirdPartyDataStoreApiUrl = 'http://third-party-json-store.herokuapp.com'
  17. const httpUsername = 'user'
  18. const httpPass = 'pass'
  19. const siteUrl = 'http://www.localhost:3000'
  20. const httpAuthSiteUrl = `http://${httpUsername}:${httpPass}@www.localhost:3000`
  21. const filestoreUrl = 'filestore.sharelatex.com'
  22. describe('TpdsUpdateSender', function() {
  23. beforeEach(function() {
  24. this.fakeUser = {
  25. _id: '12390i'
  26. }
  27. this.requestQueuer = function(queue, meth, opts, callback) {}
  28. const memberIds = [userId, collaberatorRef, readOnlyRef]
  29. this.CollaboratorsGetter = {
  30. promises: {
  31. getInvitedMemberIds: sinon.stub().resolves(memberIds)
  32. }
  33. }
  34. this.docstoreUrl = 'docstore.sharelatex.env'
  35. this.request = sinon.stub().resolves()
  36. this.settings = {
  37. siteUrl,
  38. httpAuthSiteUrl,
  39. apis: {
  40. thirdPartyDataStore: { url: thirdPartyDataStoreApiUrl },
  41. filestore: {
  42. url: filestoreUrl
  43. },
  44. docstore: {
  45. pubUrl: this.docstoreUrl
  46. }
  47. }
  48. }
  49. const getUsers = sinon.stub().resolves(
  50. memberIds.slice(1).map(userId => {
  51. return { _id: userId }
  52. })
  53. )
  54. this.UserGetter = {
  55. promises: { getUsers }
  56. }
  57. this.updateSender = SandboxedModule.require(modulePath, {
  58. globals: {
  59. console: console
  60. },
  61. requires: {
  62. mongodb: { ObjectId },
  63. 'settings-sharelatex': this.settings,
  64. 'logger-sharelatex': { log() {} },
  65. 'request-promise-native': this.request,
  66. '../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
  67. '../User/UserGetter.js': this.UserGetter,
  68. '@overleaf/metrics': {
  69. inc() {}
  70. }
  71. }
  72. })
  73. })
  74. describe('enqueue', function() {
  75. it('should not call request if there is no tpdsworker url', async function() {
  76. await this.updateSender.promises.enqueue(null, null, null)
  77. this.request.should.not.have.been.called
  78. })
  79. it('should post the message to the tpdsworker', async function() {
  80. this.settings.apis.tpdsworker = { url: 'www.tpdsworker.env' }
  81. const group0 = 'myproject'
  82. const method0 = 'somemethod0'
  83. const job0 = 'do something'
  84. await this.updateSender.promises.enqueue(group0, method0, job0)
  85. const args = this.request.firstCall.args[0]
  86. args.json.group.should.equal(group0)
  87. args.json.job.should.equal(job0)
  88. args.json.method.should.equal(method0)
  89. args.uri.should.equal(
  90. 'www.tpdsworker.env/enqueue/web_to_tpds_http_requests'
  91. )
  92. })
  93. })
  94. describe('sending updates', function() {
  95. beforeEach(function() {
  96. this.settings.apis.tpdsworker = { url: 'www.tpdsworker.env' }
  97. })
  98. it('queues a post the file with user and file id', async function() {
  99. const fileId = '4545345'
  100. const path = '/some/path/here.jpg'
  101. await this.updateSender.promises.addFile({
  102. project_id: projectId,
  103. file_id: fileId,
  104. path,
  105. project_name: projectName
  106. })
  107. const {
  108. group: group0,
  109. job: job0,
  110. method: method0
  111. } = this.request.firstCall.args[0].json
  112. group0.should.equal(userId)
  113. method0.should.equal('pipeStreamFrom')
  114. job0.method.should.equal('post')
  115. job0.streamOrigin.should.equal(
  116. `${filestoreUrl}/project/${projectId}/file/${fileId}`
  117. )
  118. const expectedUrl = `${thirdPartyDataStoreApiUrl}/user/${userId}/entity/${encodeURIComponent(
  119. projectName
  120. )}${encodeURIComponent(path)}`
  121. job0.uri.should.equal(expectedUrl)
  122. job0.headers.sl_all_user_ids.should.equal(JSON.stringify([userId]))
  123. job0.headers.sl_project_owner_user_id.should.equal(userId)
  124. const { group: group1, job: job1 } = this.request.secondCall.args[0].json
  125. group1.should.equal(collaberatorRef)
  126. job1.headers.sl_all_user_ids.should.equal(
  127. JSON.stringify([collaberatorRef])
  128. )
  129. job1.headers.sl_project_owner_user_id.should.equal(userId)
  130. const { group: group2, job: job2 } = this.request.thirdCall.args[0].json
  131. group2.should.equal(readOnlyRef)
  132. job2.headers.sl_all_user_ids.should.equal(JSON.stringify([readOnlyRef]))
  133. job2.headers.sl_project_owner_user_id.should.equal(userId)
  134. this.UserGetter.promises.getUsers.should.have.been.calledOnce.and.calledWith(
  135. {
  136. _id: {
  137. $in: [collaberatorRef, readOnlyRef]
  138. },
  139. 'dropbox.access_token.uid': { $ne: null }
  140. },
  141. { _id: 1 }
  142. )
  143. })
  144. it('post doc with stream origin of docstore', async function() {
  145. const docId = '4545345'
  146. const path = '/some/path/here.tex'
  147. const lines = ['line1', 'line2', 'line3']
  148. await this.updateSender.promises.addDoc({
  149. project_id: projectId,
  150. doc_id: docId,
  151. path,
  152. docLines: lines,
  153. project_name: projectName
  154. })
  155. const {
  156. group: group0,
  157. job: job0,
  158. method: method0
  159. } = this.request.firstCall.args[0].json
  160. group0.should.equal(userId)
  161. method0.should.equal('pipeStreamFrom')
  162. job0.method.should.equal('post')
  163. const expectedUrl = `${thirdPartyDataStoreApiUrl}/user/${userId}/entity/${encodeURIComponent(
  164. projectName
  165. )}${encodeURIComponent(path)}`
  166. job0.uri.should.equal(expectedUrl)
  167. job0.streamOrigin.should.equal(
  168. `${this.docstoreUrl}/project/${projectId}/doc/${docId}/raw`
  169. )
  170. job0.headers.sl_all_user_ids.should.eql(JSON.stringify([userId]))
  171. const { group: group1, job: job1 } = this.request.secondCall.args[0].json
  172. group1.should.equal(collaberatorRef)
  173. job1.headers.sl_all_user_ids.should.equal(
  174. JSON.stringify([collaberatorRef])
  175. )
  176. const { group: group2, job: job2 } = this.request.thirdCall.args[0].json
  177. group2.should.equal(readOnlyRef)
  178. job2.headers.sl_all_user_ids.should.equal(JSON.stringify([readOnlyRef]))
  179. this.UserGetter.promises.getUsers.should.have.been.calledOnce.and.calledWith(
  180. {
  181. _id: {
  182. $in: [collaberatorRef, readOnlyRef]
  183. },
  184. 'dropbox.access_token.uid': { $ne: null }
  185. },
  186. { _id: 1 }
  187. )
  188. })
  189. it('deleting entity', async function() {
  190. const path = '/path/here/t.tex'
  191. await this.updateSender.promises.deleteEntity({
  192. project_id: projectId,
  193. path,
  194. project_name: projectName
  195. })
  196. const {
  197. group: group0,
  198. job: job0,
  199. method: method0
  200. } = this.request.firstCall.args[0].json
  201. group0.should.equal(userId)
  202. method0.should.equal('standardHttpRequest')
  203. job0.method.should.equal('delete')
  204. const expectedUrl = `${thirdPartyDataStoreApiUrl}/user/${userId}/entity/${encodeURIComponent(
  205. projectName
  206. )}${encodeURIComponent(path)}`
  207. job0.headers.sl_all_user_ids.should.eql(JSON.stringify([userId]))
  208. job0.uri.should.equal(expectedUrl)
  209. const { group: group1, job: job1 } = this.request.secondCall.args[0].json
  210. group1.should.equal(collaberatorRef)
  211. job1.headers.sl_all_user_ids.should.equal(
  212. JSON.stringify([collaberatorRef])
  213. )
  214. const { group: group2, job: job2 } = this.request.thirdCall.args[0].json
  215. group2.should.equal(readOnlyRef)
  216. job2.headers.sl_all_user_ids.should.equal(JSON.stringify([readOnlyRef]))
  217. this.UserGetter.promises.getUsers.should.have.been.calledOnce.and.calledWith(
  218. {
  219. _id: {
  220. $in: [collaberatorRef, readOnlyRef]
  221. },
  222. 'dropbox.access_token.uid': { $ne: null }
  223. },
  224. { _id: 1 }
  225. )
  226. })
  227. it('moving entity', async function() {
  228. const startPath = 'staring/here/file.tex'
  229. const endPath = 'ending/here/file.tex'
  230. await this.updateSender.promises.moveEntity({
  231. project_id: projectId,
  232. startPath,
  233. endPath,
  234. project_name: projectName
  235. })
  236. const {
  237. group: group0,
  238. job: job0,
  239. method: method0
  240. } = this.request.firstCall.args[0].json
  241. group0.should.equal(userId)
  242. method0.should.equal('standardHttpRequest')
  243. job0.method.should.equal('put')
  244. job0.uri.should.equal(
  245. `${thirdPartyDataStoreApiUrl}/user/${userId}/entity`
  246. )
  247. job0.json.startPath.should.equal(`/${projectName}/${startPath}`)
  248. job0.json.endPath.should.equal(`/${projectName}/${endPath}`)
  249. job0.headers.sl_all_user_ids.should.eql(JSON.stringify([userId]))
  250. const { group: group1, job: job1 } = this.request.secondCall.args[0].json
  251. group1.should.equal(collaberatorRef)
  252. job1.headers.sl_all_user_ids.should.equal(
  253. JSON.stringify([collaberatorRef])
  254. )
  255. const { group: group2, job: job2 } = this.request.thirdCall.args[0].json
  256. group2.should.equal(readOnlyRef)
  257. job2.headers.sl_all_user_ids.should.equal(JSON.stringify([readOnlyRef]))
  258. this.UserGetter.promises.getUsers.should.have.been.calledOnce.and.calledWith(
  259. {
  260. _id: {
  261. $in: [collaberatorRef, readOnlyRef]
  262. },
  263. 'dropbox.access_token.uid': { $ne: null }
  264. },
  265. { _id: 1 }
  266. )
  267. })
  268. it('should be able to rename a project using the move entity func', async function() {
  269. const oldProjectName = '/oldProjectName/'
  270. const newProjectName = '/newProjectName/'
  271. await this.updateSender.promises.moveEntity({
  272. project_id: projectId,
  273. project_name: oldProjectName,
  274. newProjectName
  275. })
  276. const {
  277. group: group0,
  278. job: job0,
  279. method: method0
  280. } = this.request.firstCall.args[0].json
  281. group0.should.equal(userId)
  282. method0.should.equal('standardHttpRequest')
  283. job0.method.should.equal('put')
  284. job0.uri.should.equal(
  285. `${thirdPartyDataStoreApiUrl}/user/${userId}/entity`
  286. )
  287. job0.json.startPath.should.equal(oldProjectName)
  288. job0.json.endPath.should.equal(newProjectName)
  289. job0.headers.sl_all_user_ids.should.eql(JSON.stringify([userId]))
  290. const { group: group1, job: job1 } = this.request.secondCall.args[0].json
  291. group1.should.equal(collaberatorRef)
  292. job1.headers.sl_all_user_ids.should.equal(
  293. JSON.stringify([collaberatorRef])
  294. )
  295. const { group: group2, job: job2 } = this.request.thirdCall.args[0].json
  296. group2.should.equal(readOnlyRef)
  297. job2.headers.sl_all_user_ids.should.equal(JSON.stringify([readOnlyRef]))
  298. this.UserGetter.promises.getUsers.should.have.been.calledOnce.and.calledWith(
  299. {
  300. _id: {
  301. $in: [collaberatorRef, readOnlyRef]
  302. },
  303. 'dropbox.access_token.uid': { $ne: null }
  304. },
  305. { _id: 1 }
  306. )
  307. })
  308. it('pollDropboxForUser', async function() {
  309. await this.updateSender.promises.pollDropboxForUser(userId)
  310. const {
  311. group: group0,
  312. job: job0,
  313. method: method0
  314. } = this.request.firstCall.args[0].json
  315. group0.should.equal(`poll-dropbox:${userId}`)
  316. method0.should.equal('standardHttpRequest')
  317. job0.method.should.equal('post')
  318. job0.uri.should.equal(`${thirdPartyDataStoreApiUrl}/user/poll`)
  319. job0.json.user_ids[0].should.equal(userId)
  320. })
  321. })
  322. })