GetChangesInChunkSince.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import { expect } from 'chai'
  2. import mongodb from 'mongodb-legacy'
  3. import nock from 'nock'
  4. import Core from 'overleaf-editor-core'
  5. import * as ProjectHistoryClient from './helpers/ProjectHistoryClient.js'
  6. import * as ProjectHistoryApp from './helpers/ProjectHistoryApp.js'
  7. import latestChunk from '../fixtures/chunks/7-8.json' with { type: 'json' }
  8. import previousChunk from '../fixtures/chunks/4-6.json' with { type: 'json' }
  9. import firstChunk from '../fixtures/chunks/0-3.json' with { type: 'json' }
  10. const { ObjectId } = mongodb
  11. const MockHistoryStore = () => nock('http://127.0.0.1:3100')
  12. const MockWeb = () => nock('http://127.0.0.1:3000')
  13. const fixture = path => new URL(`../fixtures/${path}`, import.meta.url)
  14. describe('GetChangesInChunkSince', function () {
  15. let projectId, historyId
  16. beforeEach(function (done) {
  17. projectId = new ObjectId().toString()
  18. historyId = new ObjectId().toString()
  19. ProjectHistoryApp.ensureRunning(error => {
  20. if (error) throw error
  21. MockHistoryStore().post('/api/projects').reply(200, {
  22. projectId: historyId,
  23. })
  24. ProjectHistoryClient.initializeProject(historyId, (error, olProject) => {
  25. if (error) throw error
  26. MockWeb()
  27. .get(`/project/${projectId}/details`)
  28. .reply(200, {
  29. name: 'Test Project',
  30. overleaf: { history: { id: olProject.id } },
  31. })
  32. MockHistoryStore()
  33. .get(`/api/projects/${historyId}/latest/history`)
  34. .replyWithFile(200, fixture('chunks/7-8.json'))
  35. MockHistoryStore()
  36. .get(`/api/projects/${historyId}/versions/7/history`)
  37. .replyWithFile(200, fixture('chunks/7-8.json'))
  38. MockHistoryStore()
  39. .get(`/api/projects/${historyId}/versions/6/history`)
  40. .replyWithFile(200, fixture('chunks/7-8.json'))
  41. MockHistoryStore()
  42. .get(`/api/projects/${historyId}/versions/5/history`)
  43. .replyWithFile(200, fixture('chunks/4-6.json'))
  44. MockHistoryStore()
  45. .get(`/api/projects/${historyId}/versions/4/history`)
  46. .replyWithFile(200, fixture('chunks/4-6.json'))
  47. MockHistoryStore()
  48. .get(`/api/projects/${historyId}/versions/3/history`)
  49. .replyWithFile(200, fixture('chunks/4-6.json'))
  50. MockHistoryStore()
  51. .get(`/api/projects/${historyId}/versions/2/history`)
  52. .replyWithFile(200, fixture('chunks/0-3.json'))
  53. MockHistoryStore()
  54. .get(`/api/projects/${historyId}/versions/1/history`)
  55. .replyWithFile(200, fixture('chunks/0-3.json'))
  56. MockHistoryStore()
  57. .get(`/api/projects/${historyId}/versions/0/history`)
  58. .replyWithFile(200, fixture('chunks/0-3.json'))
  59. done()
  60. })
  61. })
  62. })
  63. afterEach(function () {
  64. nock.cleanAll()
  65. })
  66. function expectChangesSince(version, n, changes, done) {
  67. ProjectHistoryClient.getChangesInChunkSince(
  68. projectId,
  69. version,
  70. {},
  71. (error, got) => {
  72. if (error) throw error
  73. expect(got.latestStartVersion).to.equal(6)
  74. expect(got.changes).to.have.length(n)
  75. expect(got.changes.map(c => Core.Change.fromRaw(c))).to.deep.equal(
  76. changes.map(c => Core.Change.fromRaw(c))
  77. )
  78. done()
  79. }
  80. )
  81. }
  82. const cases = {
  83. 8: {
  84. name: 'when up-to-date, return zero changes',
  85. n: 0,
  86. changes: [],
  87. },
  88. 7: {
  89. name: 'when one version behind, return one change',
  90. n: 1,
  91. changes: latestChunk.chunk.history.changes.slice(1),
  92. },
  93. 6: {
  94. name: 'when at current chunk boundary, return latest chunk in full',
  95. n: 2,
  96. changes: latestChunk.chunk.history.changes,
  97. },
  98. 5: {
  99. name: 'when one version behind last chunk, return one change',
  100. n: 1,
  101. changes: previousChunk.chunk.history.changes.slice(2),
  102. },
  103. 4: {
  104. name: 'when in last chunk, return two changes',
  105. n: 2,
  106. changes: previousChunk.chunk.history.changes.slice(1),
  107. },
  108. 3: {
  109. name: 'when at previous chunk boundary, return just the previous chunk',
  110. n: 3,
  111. changes: previousChunk.chunk.history.changes,
  112. },
  113. 2: {
  114. name: 'when at end of first chunk, return one change',
  115. n: 1,
  116. changes: firstChunk.chunk.history.changes.slice(2),
  117. },
  118. 1: {
  119. name: 'when in first chunk, return two changes',
  120. n: 2,
  121. changes: firstChunk.chunk.history.changes.slice(1),
  122. },
  123. 0: {
  124. name: 'when from zero, return just the first chunk',
  125. n: 3,
  126. changes: firstChunk.chunk.history.changes,
  127. },
  128. }
  129. for (const [since, { name, n, changes }] of Object.entries(cases)) {
  130. it(name, function (done) {
  131. expectChangesSince(since, n, changes, done)
  132. })
  133. }
  134. it('should return an error when past the end version', function (done) {
  135. ProjectHistoryClient.getChangesInChunkSince(
  136. projectId,
  137. 9,
  138. { allowErrors: true },
  139. (error, _body, statusCode) => {
  140. if (error) throw error
  141. expect(statusCode).to.equal(400)
  142. done()
  143. }
  144. )
  145. })
  146. })