UpdateMergerTests.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. const SandboxedModule = require('sandboxed-module')
  2. const sinon = require('sinon')
  3. const { expect } = require('chai')
  4. const BufferedStream = require('bufferedstream')
  5. const { ObjectId } = require('mongodb')
  6. const MODULE_PATH =
  7. '../../../../app/src/Features/ThirdPartyDataStore/UpdateMerger.js'
  8. describe('UpdateMerger :', function () {
  9. beforeEach(function () {
  10. this.projectId = 'project_id_here'
  11. this.userId = 'mock-user-id'
  12. this.docPath = this.newDocPath = '/folder/doc.tex'
  13. this.filePath = this.newFilePath = '/folder/file.png'
  14. this.existingDocPath = '/folder/other.tex'
  15. this.existingFilePath = '/folder/fig1.pdf'
  16. this.linkedFileData = { provider: 'url' }
  17. this.existingDocs = [{ path: '/main.tex' }, { path: '/folder/other.tex' }]
  18. this.existingFiles = [{ path: '/figure.pdf' }, { path: '/folder/fig1.pdf' }]
  19. this.fsPath = '/tmp/file/path'
  20. this.fileContents = `\\documentclass{article}
  21. \\usepackage[utf8]{inputenc}
  22. \\title{42}
  23. \\author{Jane Doe}
  24. \\date{June 2011}`
  25. this.docLines = this.fileContents.split('\n')
  26. this.source = 'dropbox'
  27. this.updateRequest = new BufferedStream()
  28. this.fsPromises = {
  29. unlink: sinon.stub().resolves(),
  30. readFile: sinon.stub().withArgs(this.fsPath).resolves(this.fileContents),
  31. }
  32. this.doc = {
  33. _id: ObjectId(),
  34. rev: 2,
  35. }
  36. this.file = {
  37. _id: ObjectId(),
  38. rev: 6,
  39. }
  40. this.folder = {
  41. _id: ObjectId(),
  42. }
  43. this.EditorController = {
  44. promises: {
  45. deleteEntityWithPath: sinon.stub().resolves(),
  46. upsertDocWithPath: sinon
  47. .stub()
  48. .resolves({ doc: this.doc, folder: this.folder }),
  49. upsertFileWithPath: sinon
  50. .stub()
  51. .resolves({ file: this.file, folder: this.folder }),
  52. },
  53. }
  54. this.FileTypeManager = {
  55. promises: {
  56. getType: sinon.stub(),
  57. },
  58. }
  59. this.FileWriter = {
  60. promises: {
  61. writeStreamToDisk: sinon.stub().resolves(this.fsPath),
  62. },
  63. }
  64. this.ProjectEntityHandler = {
  65. promises: {
  66. getAllEntities: sinon.stub().resolves({
  67. docs: this.existingDocs,
  68. files: this.existingFiles,
  69. }),
  70. },
  71. }
  72. this.Settings = { path: { dumpPath: 'dump_here' } }
  73. this.UpdateMerger = SandboxedModule.require(MODULE_PATH, {
  74. requires: {
  75. 'fs/promises': this.fsPromises,
  76. '../Editor/EditorController': this.EditorController,
  77. '../Uploads/FileTypeManager': this.FileTypeManager,
  78. '../../infrastructure/FileWriter': this.FileWriter,
  79. '../Project/ProjectEntityHandler': this.ProjectEntityHandler,
  80. '@overleaf/settings': this.Settings,
  81. },
  82. })
  83. })
  84. describe('mergeUpdate', function () {
  85. describe('doc updates for a new doc', function () {
  86. beforeEach(async function () {
  87. this.FileTypeManager.promises.getType.resolves({
  88. binary: false,
  89. encoding: 'utf-8',
  90. })
  91. await this.UpdateMerger.promises.mergeUpdate(
  92. this.userId,
  93. this.projectId,
  94. this.docPath,
  95. this.updateRequest,
  96. this.source
  97. )
  98. })
  99. it('should look at the file contents', function () {
  100. expect(this.FileTypeManager.promises.getType).to.have.been.called
  101. })
  102. it('should process update as doc', function () {
  103. expect(
  104. this.EditorController.promises.upsertDocWithPath
  105. ).to.have.been.calledWith(
  106. this.projectId,
  107. this.docPath,
  108. this.docLines,
  109. this.source,
  110. this.userId
  111. )
  112. })
  113. it('removes the temp file from disk', function () {
  114. expect(this.fsPromises.unlink).to.have.been.calledWith(this.fsPath)
  115. })
  116. })
  117. describe('file updates for a new file ', function () {
  118. beforeEach(async function () {
  119. this.FileTypeManager.promises.getType.resolves({ binary: true })
  120. await this.UpdateMerger.promises.mergeUpdate(
  121. this.userId,
  122. this.projectId,
  123. this.filePath,
  124. this.updateRequest,
  125. this.source
  126. )
  127. })
  128. it('should look at the file contents', function () {
  129. expect(this.FileTypeManager.promises.getType).to.have.been.called
  130. })
  131. it('should process update as file', function () {
  132. expect(
  133. this.EditorController.promises.upsertFileWithPath
  134. ).to.have.been.calledWith(
  135. this.projectId,
  136. this.filePath,
  137. this.fsPath,
  138. null,
  139. this.source,
  140. this.userId
  141. )
  142. })
  143. it('removes the temp file from disk', function () {
  144. expect(this.fsPromises.unlink).to.have.been.calledWith(this.fsPath)
  145. })
  146. })
  147. describe('doc updates for an existing doc', function () {
  148. beforeEach(async function () {
  149. this.FileTypeManager.promises.getType.resolves({
  150. binary: false,
  151. encoding: 'utf-8',
  152. })
  153. await this.UpdateMerger.promises.mergeUpdate(
  154. this.userId,
  155. this.projectId,
  156. this.existingDocPath,
  157. this.updateRequest,
  158. this.source
  159. )
  160. })
  161. it('should look at the file contents', function () {
  162. expect(this.FileTypeManager.promises.getType).to.have.been.called
  163. })
  164. it('should process update as doc', function () {
  165. expect(
  166. this.EditorController.promises.upsertDocWithPath
  167. ).to.have.been.calledWith(
  168. this.projectId,
  169. this.existingDocPath,
  170. this.docLines,
  171. this.source,
  172. this.userId
  173. )
  174. })
  175. it('removes the temp file from disk', function () {
  176. expect(this.fsPromises.unlink).to.have.been.calledWith(this.fsPath)
  177. })
  178. })
  179. describe('file updates for an existing file', function () {
  180. beforeEach(async function () {
  181. this.FileTypeManager.promises.getType.resolves({ binary: true })
  182. await this.UpdateMerger.promises.mergeUpdate(
  183. this.userId,
  184. this.projectId,
  185. this.existingFilePath,
  186. this.updateRequest,
  187. this.source
  188. )
  189. })
  190. it('should look at the file contents', function () {
  191. expect(this.FileTypeManager.promises.getType).to.have.been.called
  192. })
  193. it('should process update as file', function () {
  194. expect(
  195. this.EditorController.promises.upsertFileWithPath
  196. ).to.have.been.calledWith(
  197. this.projectId,
  198. this.existingFilePath,
  199. this.fsPath,
  200. null,
  201. this.source,
  202. this.userId
  203. )
  204. })
  205. it('removes the temp file from disk', function () {
  206. expect(this.fsPromises.unlink).to.have.been.calledWith(this.fsPath)
  207. })
  208. })
  209. })
  210. describe('file updates for an existing doc', function () {
  211. beforeEach(async function () {
  212. this.FileTypeManager.promises.getType.resolves({ binary: true })
  213. await this.UpdateMerger.promises.mergeUpdate(
  214. this.userId,
  215. this.projectId,
  216. this.existingDocPath,
  217. this.updateRequest,
  218. this.source
  219. )
  220. })
  221. it('should look at the file contents', function () {
  222. expect(this.FileTypeManager.promises.getType).to.have.been.called
  223. })
  224. it('should process update as file', function () {
  225. expect(
  226. this.EditorController.promises.upsertFileWithPath
  227. ).to.have.been.calledWith(
  228. this.projectId,
  229. this.existingDocPath,
  230. this.fsPath,
  231. null,
  232. this.source,
  233. this.userId
  234. )
  235. })
  236. it('removes the temp file from disk', function () {
  237. expect(this.fsPromises.unlink).to.have.been.calledWith(this.fsPath)
  238. })
  239. })
  240. describe('doc updates for an existing file', function () {
  241. beforeEach(async function () {
  242. this.FileTypeManager.promises.getType.resolves({ binary: true })
  243. await this.UpdateMerger.promises.mergeUpdate(
  244. this.userId,
  245. this.projectId,
  246. this.existingFilePath,
  247. this.updateRequest,
  248. this.source
  249. )
  250. })
  251. it('should look at the file contents', function () {
  252. expect(this.FileTypeManager.promises.getType).to.have.been.called
  253. })
  254. it('should not delete the existing file', function () {
  255. expect(this.EditorController.promises.deleteEntityWithPath).to.not.have
  256. .been.called
  257. })
  258. it('should process update as file', function () {
  259. expect(
  260. this.EditorController.promises.upsertFileWithPath
  261. ).to.have.been.calledWith(
  262. this.projectId,
  263. this.existingFilePath,
  264. this.fsPath,
  265. null,
  266. this.source,
  267. this.userId
  268. )
  269. })
  270. it('removes the temp file from disk', function () {
  271. expect(this.fsPromises.unlink).to.have.been.calledWith(this.fsPath)
  272. })
  273. })
  274. describe('deleteUpdate', function () {
  275. beforeEach(async function () {
  276. await this.UpdateMerger.promises.deleteUpdate(
  277. this.userId,
  278. this.projectId,
  279. this.docPath,
  280. this.source
  281. )
  282. })
  283. it('should delete the entity in the editor controller', function () {
  284. expect(
  285. this.EditorController.promises.deleteEntityWithPath
  286. ).to.have.been.calledWith(
  287. this.projectId,
  288. this.docPath,
  289. this.source,
  290. this.userId
  291. )
  292. })
  293. })
  294. })