GetChangesInChunkSince.js 4.6 KB

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