TpdsProjectFlusherTests.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. const { expect } = require('chai')
  2. const sinon = require('sinon')
  3. const SandboxedModule = require('sandboxed-module')
  4. const { ObjectId } = require('mongodb')
  5. const { Project } = require('../helpers/models/Project')
  6. const MODULE_PATH =
  7. '../../../../app/src/Features/ThirdPartyDataStore/TpdsProjectFlusher'
  8. describe('TpdsProjectFlusher', function() {
  9. beforeEach(function() {
  10. this.project = { _id: ObjectId() }
  11. this.docs = {
  12. '/doc/one': { _id: 'mock-doc-1', lines: ['one'], rev: 5 },
  13. '/doc/two': { _id: 'mock-doc-2', lines: ['two'], rev: 6 }
  14. }
  15. this.files = {
  16. '/file/one': { _id: 'mock-file-1', rev: 7 },
  17. '/file/two': { _id: 'mock-file-2', rev: 8 }
  18. }
  19. this.DocumentUpdaterHandler = {
  20. promises: {
  21. flushProjectToMongo: sinon.stub().resolves()
  22. }
  23. }
  24. this.ProjectGetter = {
  25. promises: {
  26. getProject: sinon.stub().resolves(this.project)
  27. }
  28. }
  29. this.ProjectEntityHandler = {
  30. promises: {
  31. getAllDocs: sinon
  32. .stub()
  33. .withArgs(this.project._id)
  34. .resolves(this.docs),
  35. getAllFiles: sinon
  36. .stub()
  37. .withArgs(this.project._id)
  38. .resolves(this.files)
  39. }
  40. }
  41. this.TpdsUpdateSender = {
  42. promises: {
  43. addDoc: sinon.stub().resolves(),
  44. addFile: sinon.stub().resolves()
  45. }
  46. }
  47. this.ProjectMock = sinon.mock(Project)
  48. this.TpdsProjectFlusher = SandboxedModule.require(MODULE_PATH, {
  49. requires: {
  50. 'logger-sharelatex': { debug() {} },
  51. '../DocumentUpdater/DocumentUpdaterHandler': this
  52. .DocumentUpdaterHandler,
  53. '../Project/ProjectGetter': this.ProjectGetter,
  54. '../Project/ProjectEntityHandler': this.ProjectEntityHandler,
  55. '../../models/Project': { Project },
  56. './TpdsUpdateSender': this.TpdsUpdateSender
  57. }
  58. })
  59. })
  60. afterEach(function() {
  61. this.ProjectMock.restore()
  62. })
  63. describe('flushProjectToTpds', function() {
  64. describe('usually', function() {
  65. beforeEach(async function() {
  66. await this.TpdsProjectFlusher.promises.flushProjectToTpds(
  67. this.project._id
  68. )
  69. })
  70. it('should flush the project from the doc updater', function() {
  71. expect(
  72. this.DocumentUpdaterHandler.promises.flushProjectToMongo
  73. ).to.have.been.calledWith(this.project._id)
  74. })
  75. it('should flush each doc to the TPDS', function() {
  76. for (const [path, doc] of Object.entries(this.docs)) {
  77. expect(this.TpdsUpdateSender.promises.addDoc).to.have.been.calledWith(
  78. {
  79. project_id: this.project._id,
  80. doc_id: doc._id,
  81. project_name: this.project.name,
  82. rev: doc.rev,
  83. path
  84. }
  85. )
  86. }
  87. })
  88. it('should flush each file to the TPDS', function() {
  89. for (const [path, file] of Object.entries(this.files)) {
  90. expect(
  91. this.TpdsUpdateSender.promises.addFile
  92. ).to.have.been.calledWith({
  93. project_id: this.project._id,
  94. file_id: file._id,
  95. project_name: this.project.name,
  96. rev: file.rev,
  97. path
  98. })
  99. }
  100. })
  101. })
  102. describe('when a TPDS flush is pending', function() {
  103. beforeEach(async function() {
  104. this.project.deferredTpdsFlushCounter = 2
  105. this.ProjectMock.expects('updateOne')
  106. .withArgs(
  107. {
  108. _id: this.project._id,
  109. deferredTpdsFlushCounter: { $lte: 2 }
  110. },
  111. { $set: { deferredTpdsFlushCounter: 0 } }
  112. )
  113. .chain('exec')
  114. .resolves()
  115. await this.TpdsProjectFlusher.promises.flushProjectToTpds(
  116. this.project._id
  117. )
  118. })
  119. it('resets the deferred flush counter', function() {
  120. this.ProjectMock.verify()
  121. })
  122. })
  123. })
  124. describe('deferProjectFlushToTpds', function() {
  125. beforeEach(async function() {
  126. this.ProjectMock.expects('updateOne')
  127. .withArgs(
  128. {
  129. _id: this.project._id
  130. },
  131. { $inc: { deferredTpdsFlushCounter: 1 } }
  132. )
  133. .chain('exec')
  134. .resolves()
  135. await this.TpdsProjectFlusher.promises.deferProjectFlushToTpds(
  136. this.project._id
  137. )
  138. })
  139. it('increments the deferred flush counter', function() {
  140. this.ProjectMock.verify()
  141. })
  142. })
  143. describe('flushProjectToTpdsIfNeeded', function() {
  144. let cases = [0, undefined]
  145. cases.forEach(counterValue => {
  146. describe(`when the deferred flush counter is ${counterValue}`, function() {
  147. beforeEach(async function() {
  148. this.project.deferredTpdsFlushCounter = counterValue
  149. await this.TpdsProjectFlusher.promises.flushProjectToTpdsIfNeeded(
  150. this.project._id
  151. )
  152. })
  153. it("doesn't flush the project from the doc updater", function() {
  154. expect(this.DocumentUpdaterHandler.promises.flushProjectToMongo).not
  155. .to.have.been.called
  156. })
  157. it("doesn't flush any doc", function() {
  158. expect(this.TpdsUpdateSender.promises.addDoc).not.to.have.been.called
  159. })
  160. it("doesn't flush any file", function() {
  161. expect(this.TpdsUpdateSender.promises.addFile).not.to.have.been.called
  162. })
  163. })
  164. })
  165. cases = [1, 2]
  166. cases.forEach(counterValue => {
  167. describe(`when the deferred flush counter is ${counterValue}`, function() {
  168. beforeEach(async function() {
  169. this.project.deferredTpdsFlushCounter = counterValue
  170. this.ProjectMock.expects('updateOne')
  171. .withArgs(
  172. {
  173. _id: this.project._id,
  174. deferredTpdsFlushCounter: { $lte: counterValue }
  175. },
  176. { $set: { deferredTpdsFlushCounter: 0 } }
  177. )
  178. .chain('exec')
  179. .resolves()
  180. await this.TpdsProjectFlusher.promises.flushProjectToTpdsIfNeeded(
  181. this.project._id
  182. )
  183. })
  184. it('flushes the project from the doc updater', function() {
  185. expect(
  186. this.DocumentUpdaterHandler.promises.flushProjectToMongo
  187. ).to.have.been.calledWith(this.project._id)
  188. })
  189. it('flushes each doc to the TPDS', function() {
  190. for (const [path, doc] of Object.entries(this.docs)) {
  191. expect(
  192. this.TpdsUpdateSender.promises.addDoc
  193. ).to.have.been.calledWith({
  194. project_id: this.project._id,
  195. doc_id: doc._id,
  196. project_name: this.project.name,
  197. rev: doc.rev,
  198. path
  199. })
  200. }
  201. })
  202. it('flushes each file to the TPDS', function() {
  203. for (const [path, file] of Object.entries(this.files)) {
  204. expect(
  205. this.TpdsUpdateSender.promises.addFile
  206. ).to.have.been.calledWith({
  207. project_id: this.project._id,
  208. file_id: file._id,
  209. project_name: this.project.name,
  210. rev: file.rev,
  211. path
  212. })
  213. }
  214. })
  215. it('resets the deferred flush counter', function() {
  216. this.ProjectMock.verify()
  217. })
  218. })
  219. })
  220. })
  221. })