updateProjectTests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. /* eslint-disable
  2. no-return-assign,
  3. no-unused-vars,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * DS206: Consider reworking classes to avoid initClass
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const sinon = require('sinon')
  14. const chai = require('chai')
  15. const should = chai.should()
  16. const modulePath = '../../../../app/js/ProjectManager.js'
  17. const SandboxedModule = require('sandboxed-module')
  18. const _ = require('lodash')
  19. describe('ProjectManager', function () {
  20. beforeEach(function () {
  21. let Timer
  22. this.ProjectManager = SandboxedModule.require(modulePath, {
  23. requires: {
  24. './RedisManager': (this.RedisManager = {}),
  25. './ProjectHistoryRedisManager': (this.ProjectHistoryRedisManager = {}),
  26. './DocumentManager': (this.DocumentManager = {}),
  27. 'logger-sharelatex': (this.logger = {
  28. log: sinon.stub(),
  29. error: sinon.stub()
  30. }),
  31. './HistoryManager': (this.HistoryManager = {}),
  32. './Metrics': (this.Metrics = {
  33. Timer: (Timer = (function () {
  34. Timer = class Timer {
  35. static initClass() {
  36. this.prototype.done = sinon.stub()
  37. }
  38. }
  39. Timer.initClass()
  40. return Timer
  41. })())
  42. })
  43. }
  44. })
  45. this.project_id = 'project-id-123'
  46. this.projectHistoryId = 'history-id-123'
  47. this.user_id = 'user-id-123'
  48. this.version = 1234567
  49. this.HistoryManager.shouldFlushHistoryOps = sinon.stub().returns(false)
  50. this.HistoryManager.flushProjectChangesAsync = sinon.stub()
  51. return (this.callback = sinon.stub())
  52. })
  53. return describe('updateProjectWithLocks', function () {
  54. describe('rename operations', function () {
  55. beforeEach(function () {
  56. this.firstDocUpdate = {
  57. id: 1,
  58. pathname: 'foo',
  59. newPathname: 'foo'
  60. }
  61. this.secondDocUpdate = {
  62. id: 2,
  63. pathname: 'bar',
  64. newPathname: 'bar2'
  65. }
  66. this.docUpdates = [this.firstDocUpdate, this.secondDocUpdate]
  67. this.firstFileUpdate = {
  68. id: 2,
  69. pathname: 'bar',
  70. newPathname: 'bar2'
  71. }
  72. this.fileUpdates = [this.firstFileUpdate]
  73. this.DocumentManager.renameDocWithLock = sinon.stub().yields()
  74. return (this.ProjectHistoryRedisManager.queueRenameEntity = sinon
  75. .stub()
  76. .yields())
  77. })
  78. describe('successfully', function () {
  79. beforeEach(function () {
  80. return this.ProjectManager.updateProjectWithLocks(
  81. this.project_id,
  82. this.projectHistoryId,
  83. this.user_id,
  84. this.docUpdates,
  85. this.fileUpdates,
  86. this.version,
  87. this.callback
  88. )
  89. })
  90. it('should rename the docs in the updates', function () {
  91. const firstDocUpdateWithVersion = _.extend({}, this.firstDocUpdate, {
  92. version: `${this.version}.0`
  93. })
  94. const secondDocUpdateWithVersion = _.extend(
  95. {},
  96. this.secondDocUpdate,
  97. { version: `${this.version}.1` }
  98. )
  99. this.DocumentManager.renameDocWithLock
  100. .calledWith(
  101. this.project_id,
  102. this.firstDocUpdate.id,
  103. this.user_id,
  104. firstDocUpdateWithVersion,
  105. this.projectHistoryId
  106. )
  107. .should.equal(true)
  108. return this.DocumentManager.renameDocWithLock
  109. .calledWith(
  110. this.project_id,
  111. this.secondDocUpdate.id,
  112. this.user_id,
  113. secondDocUpdateWithVersion,
  114. this.projectHistoryId
  115. )
  116. .should.equal(true)
  117. })
  118. it('should rename the files in the updates', function () {
  119. const firstFileUpdateWithVersion = _.extend(
  120. {},
  121. this.firstFileUpdate,
  122. { version: `${this.version}.2` }
  123. )
  124. return this.ProjectHistoryRedisManager.queueRenameEntity
  125. .calledWith(
  126. this.project_id,
  127. this.projectHistoryId,
  128. 'file',
  129. this.firstFileUpdate.id,
  130. this.user_id,
  131. firstFileUpdateWithVersion
  132. )
  133. .should.equal(true)
  134. })
  135. it('should not flush the history', function () {
  136. return this.HistoryManager.flushProjectChangesAsync
  137. .calledWith(this.project_id)
  138. .should.equal(false)
  139. })
  140. return it('should call the callback', function () {
  141. return this.callback.called.should.equal(true)
  142. })
  143. })
  144. describe('when renaming a doc fails', function () {
  145. beforeEach(function () {
  146. this.error = new Error('error')
  147. this.DocumentManager.renameDocWithLock = sinon
  148. .stub()
  149. .yields(this.error)
  150. return this.ProjectManager.updateProjectWithLocks(
  151. this.project_id,
  152. this.projectHistoryId,
  153. this.user_id,
  154. this.docUpdates,
  155. this.fileUpdates,
  156. this.version,
  157. this.callback
  158. )
  159. })
  160. return it('should call the callback with the error', function () {
  161. return this.callback.calledWith(this.error).should.equal(true)
  162. })
  163. })
  164. describe('when renaming a file fails', function () {
  165. beforeEach(function () {
  166. this.error = new Error('error')
  167. this.ProjectHistoryRedisManager.queueRenameEntity = sinon
  168. .stub()
  169. .yields(this.error)
  170. return this.ProjectManager.updateProjectWithLocks(
  171. this.project_id,
  172. this.projectHistoryId,
  173. this.user_id,
  174. this.docUpdates,
  175. this.fileUpdates,
  176. this.version,
  177. this.callback
  178. )
  179. })
  180. return it('should call the callback with the error', function () {
  181. return this.callback.calledWith(this.error).should.equal(true)
  182. })
  183. })
  184. return describe('with enough ops to flush', function () {
  185. beforeEach(function () {
  186. this.HistoryManager.shouldFlushHistoryOps = sinon.stub().returns(true)
  187. return this.ProjectManager.updateProjectWithLocks(
  188. this.project_id,
  189. this.projectHistoryId,
  190. this.user_id,
  191. this.docUpdates,
  192. this.fileUpdates,
  193. this.version,
  194. this.callback
  195. )
  196. })
  197. return it('should flush the history', function () {
  198. return this.HistoryManager.flushProjectChangesAsync
  199. .calledWith(this.project_id)
  200. .should.equal(true)
  201. })
  202. })
  203. })
  204. return describe('add operations', function () {
  205. beforeEach(function () {
  206. this.firstDocUpdate = {
  207. id: 1,
  208. docLines: 'a\nb'
  209. }
  210. this.secondDocUpdate = {
  211. id: 2,
  212. docLines: 'a\nb'
  213. }
  214. this.docUpdates = [this.firstDocUpdate, this.secondDocUpdate]
  215. this.firstFileUpdate = {
  216. id: 3,
  217. url: 'filestore.example.com/2'
  218. }
  219. this.secondFileUpdate = {
  220. id: 4,
  221. url: 'filestore.example.com/3'
  222. }
  223. this.fileUpdates = [this.firstFileUpdate, this.secondFileUpdate]
  224. return (this.ProjectHistoryRedisManager.queueAddEntity = sinon
  225. .stub()
  226. .yields())
  227. })
  228. describe('successfully', function () {
  229. beforeEach(function () {
  230. return this.ProjectManager.updateProjectWithLocks(
  231. this.project_id,
  232. this.projectHistoryId,
  233. this.user_id,
  234. this.docUpdates,
  235. this.fileUpdates,
  236. this.version,
  237. this.callback
  238. )
  239. })
  240. it('should add the docs in the updates', function () {
  241. const firstDocUpdateWithVersion = _.extend({}, this.firstDocUpdate, {
  242. version: `${this.version}.0`
  243. })
  244. const secondDocUpdateWithVersion = _.extend(
  245. {},
  246. this.secondDocUpdate,
  247. { version: `${this.version}.1` }
  248. )
  249. this.ProjectHistoryRedisManager.queueAddEntity
  250. .getCall(0)
  251. .calledWith(
  252. this.project_id,
  253. this.projectHistoryId,
  254. 'doc',
  255. this.firstDocUpdate.id,
  256. this.user_id,
  257. firstDocUpdateWithVersion
  258. )
  259. .should.equal(true)
  260. return this.ProjectHistoryRedisManager.queueAddEntity
  261. .getCall(1)
  262. .calledWith(
  263. this.project_id,
  264. this.projectHistoryId,
  265. 'doc',
  266. this.secondDocUpdate.id,
  267. this.user_id,
  268. secondDocUpdateWithVersion
  269. )
  270. .should.equal(true)
  271. })
  272. it('should add the files in the updates', function () {
  273. const firstFileUpdateWithVersion = _.extend(
  274. {},
  275. this.firstFileUpdate,
  276. { version: `${this.version}.2` }
  277. )
  278. const secondFileUpdateWithVersion = _.extend(
  279. {},
  280. this.secondFileUpdate,
  281. { version: `${this.version}.3` }
  282. )
  283. this.ProjectHistoryRedisManager.queueAddEntity
  284. .getCall(2)
  285. .calledWith(
  286. this.project_id,
  287. this.projectHistoryId,
  288. 'file',
  289. this.firstFileUpdate.id,
  290. this.user_id,
  291. firstFileUpdateWithVersion
  292. )
  293. .should.equal(true)
  294. return this.ProjectHistoryRedisManager.queueAddEntity
  295. .getCall(3)
  296. .calledWith(
  297. this.project_id,
  298. this.projectHistoryId,
  299. 'file',
  300. this.secondFileUpdate.id,
  301. this.user_id,
  302. secondFileUpdateWithVersion
  303. )
  304. .should.equal(true)
  305. })
  306. it('should not flush the history', function () {
  307. return this.HistoryManager.flushProjectChangesAsync
  308. .calledWith(this.project_id)
  309. .should.equal(false)
  310. })
  311. return it('should call the callback', function () {
  312. return this.callback.called.should.equal(true)
  313. })
  314. })
  315. describe('when adding a doc fails', function () {
  316. beforeEach(function () {
  317. this.error = new Error('error')
  318. this.ProjectHistoryRedisManager.queueAddEntity = sinon
  319. .stub()
  320. .yields(this.error)
  321. return this.ProjectManager.updateProjectWithLocks(
  322. this.project_id,
  323. this.projectHistoryId,
  324. this.user_id,
  325. this.docUpdates,
  326. this.fileUpdates,
  327. this.version,
  328. this.callback
  329. )
  330. })
  331. return it('should call the callback with the error', function () {
  332. return this.callback.calledWith(this.error).should.equal(true)
  333. })
  334. })
  335. describe('when adding a file fails', function () {
  336. beforeEach(function () {
  337. this.error = new Error('error')
  338. this.ProjectHistoryRedisManager.queueAddEntity = sinon
  339. .stub()
  340. .yields(this.error)
  341. return this.ProjectManager.updateProjectWithLocks(
  342. this.project_id,
  343. this.projectHistoryId,
  344. this.user_id,
  345. this.docUpdates,
  346. this.fileUpdates,
  347. this.version,
  348. this.callback
  349. )
  350. })
  351. return it('should call the callback with the error', function () {
  352. return this.callback.calledWith(this.error).should.equal(true)
  353. })
  354. })
  355. return describe('with enough ops to flush', function () {
  356. beforeEach(function () {
  357. this.HistoryManager.shouldFlushHistoryOps = sinon.stub().returns(true)
  358. return this.ProjectManager.updateProjectWithLocks(
  359. this.project_id,
  360. this.projectHistoryId,
  361. this.user_id,
  362. this.docUpdates,
  363. this.fileUpdates,
  364. this.version,
  365. this.callback
  366. )
  367. })
  368. return it('should flush the history', function () {
  369. return this.HistoryManager.flushProjectChangesAsync
  370. .calledWith(this.project_id)
  371. .should.equal(true)
  372. })
  373. })
  374. })
  375. })
  376. })