RestoringFilesTest.mjs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. import { expect } from 'chai'
  2. import _ from 'lodash'
  3. import fs from 'node:fs'
  4. import { fileURLToPath } from 'node:url'
  5. import Path from 'node:path'
  6. import User from '../../../../../test/acceptance/src/helpers/User.mjs'
  7. import MockProjectHistoryApiClass from '../../../../../test/acceptance/src/mocks/MockProjectHistoryApi.mjs'
  8. import MockDocstoreApiClass from '../../../../../test/acceptance/src/mocks/MockDocstoreApi.mjs'
  9. import MockFilestoreApiClass from '../../../../../test/acceptance/src/mocks/MockFilestoreApi.mjs'
  10. import MockV1HistoryApiClass from '../../../../../test/acceptance/src/mocks/MockV1HistoryApi.mjs'
  11. import Features from '../../../../../app/src/infrastructure/Features.js'
  12. let MockProjectHistoryApi, MockDocstoreApi, MockFilestoreApi, MockV1HistoryApi
  13. const __dirname = fileURLToPath(new URL('.', import.meta.url))
  14. before(function () {
  15. MockProjectHistoryApi = MockProjectHistoryApiClass.instance()
  16. MockDocstoreApi = MockDocstoreApiClass.instance()
  17. MockFilestoreApi = MockFilestoreApiClass.instance()
  18. MockV1HistoryApi = MockV1HistoryApiClass.instance()
  19. })
  20. describe('RestoringFiles', function () {
  21. beforeEach(function (done) {
  22. this.owner = new User()
  23. this.owner.login(error => {
  24. if (error) {
  25. throw error
  26. }
  27. this.owner.createProject(
  28. 'example-project',
  29. { template: 'example' },
  30. (error, projectId) => {
  31. this.project_id = projectId
  32. if (error) {
  33. throw error
  34. }
  35. done()
  36. }
  37. )
  38. })
  39. })
  40. describe('restoring from v2 history', function () {
  41. describe('restoring a text file', function () {
  42. beforeEach(function (done) {
  43. MockProjectHistoryApi.addOldFile(
  44. this.project_id,
  45. 42,
  46. 'foo.tex',
  47. 'hello world, this is foo.tex!'
  48. )
  49. this.owner.request(
  50. {
  51. method: 'POST',
  52. url: `/project/${this.project_id}/restore_file`,
  53. json: {
  54. pathname: 'foo.tex',
  55. version: 42,
  56. },
  57. },
  58. (error, response, body) => {
  59. if (error) {
  60. throw error
  61. }
  62. expect(response.statusCode).to.equal(200)
  63. done()
  64. }
  65. )
  66. })
  67. it('should have created a doc', function (done) {
  68. this.owner.getProject(this.project_id, (error, project) => {
  69. if (error) {
  70. throw error
  71. }
  72. let doc = _.find(
  73. project.rootFolder[0].docs,
  74. doc => doc.name === 'foo.tex'
  75. )
  76. doc = MockDocstoreApi.docs[this.project_id][doc._id]
  77. expect(doc.lines).to.deep.equal(['hello world, this is foo.tex!'])
  78. done()
  79. })
  80. })
  81. })
  82. describe('restoring a binary file', function () {
  83. beforeEach(function (done) {
  84. this.pngData = fs.readFileSync(
  85. Path.resolve(
  86. __dirname,
  87. '../../../../../test/acceptance/files/1pixel.png'
  88. ),
  89. 'binary'
  90. )
  91. MockProjectHistoryApi.addOldFile(
  92. this.project_id,
  93. 42,
  94. 'image.png',
  95. this.pngData
  96. )
  97. this.owner.request(
  98. {
  99. method: 'POST',
  100. url: `/project/${this.project_id}/restore_file`,
  101. json: {
  102. pathname: 'image.png',
  103. version: 42,
  104. },
  105. },
  106. (error, response, body) => {
  107. if (error) {
  108. throw error
  109. }
  110. expect(response.statusCode).to.equal(200)
  111. done()
  112. }
  113. )
  114. })
  115. if (Features.hasFeature('project-history-blobs')) {
  116. it('should have created a file in history-v1', function (done) {
  117. this.owner.getProject(this.project_id, (error, project) => {
  118. if (error) {
  119. throw error
  120. }
  121. let file = _.find(
  122. project.rootFolder[0].fileRefs,
  123. file => file.name === 'image.png'
  124. )
  125. file =
  126. MockV1HistoryApi.blobs[project.overleaf.history.id.toString()][
  127. file.hash
  128. ]
  129. expect(file).to.deep.equal(Buffer.from(this.pngData))
  130. done()
  131. })
  132. })
  133. }
  134. if (Features.hasFeature('filestore')) {
  135. it('should have created a file in filestore', function (done) {
  136. this.owner.getProject(this.project_id, (error, project) => {
  137. if (error) {
  138. throw error
  139. }
  140. let file = _.find(
  141. project.rootFolder[0].fileRefs,
  142. file => file.name === 'image.png'
  143. )
  144. file = MockFilestoreApi.getFile(this.project_id, file._id)
  145. expect(file).to.deep.equal(this.pngData)
  146. done()
  147. })
  148. })
  149. }
  150. })
  151. describe('restoring to a directory that exists', function () {
  152. beforeEach(function (done) {
  153. MockProjectHistoryApi.addOldFile(
  154. this.project_id,
  155. 42,
  156. 'foldername/foo2.tex',
  157. 'hello world, this is foo-2.tex!'
  158. )
  159. this.owner.request.post(
  160. {
  161. uri: `project/${this.project_id}/folder`,
  162. json: {
  163. name: 'foldername',
  164. },
  165. },
  166. (error, response, body) => {
  167. if (error) {
  168. throw error
  169. }
  170. expect(response.statusCode).to.equal(200)
  171. this.owner.request(
  172. {
  173. method: 'POST',
  174. url: `/project/${this.project_id}/restore_file`,
  175. json: {
  176. pathname: 'foldername/foo2.tex',
  177. version: 42,
  178. },
  179. },
  180. (error, response, body) => {
  181. if (error) {
  182. throw error
  183. }
  184. expect(response.statusCode).to.equal(200)
  185. done()
  186. }
  187. )
  188. }
  189. )
  190. })
  191. it('should have created the doc in the named folder', function (done) {
  192. this.owner.getProject(this.project_id, (error, project) => {
  193. if (error) {
  194. throw error
  195. }
  196. const folder = _.find(
  197. project.rootFolder[0].folders,
  198. folder => folder.name === 'foldername'
  199. )
  200. let doc = _.find(folder.docs, doc => doc.name === 'foo2.tex')
  201. doc = MockDocstoreApi.docs[this.project_id][doc._id]
  202. expect(doc.lines).to.deep.equal(['hello world, this is foo-2.tex!'])
  203. done()
  204. })
  205. })
  206. })
  207. describe('restoring to a directory that no longer exists', function () {
  208. beforeEach(function (done) {
  209. MockProjectHistoryApi.addOldFile(
  210. this.project_id,
  211. 42,
  212. 'nothere/foo3.tex',
  213. 'hello world, this is foo-3.tex!'
  214. )
  215. this.owner.request(
  216. {
  217. method: 'POST',
  218. url: `/project/${this.project_id}/restore_file`,
  219. json: {
  220. pathname: 'nothere/foo3.tex',
  221. version: 42,
  222. },
  223. },
  224. (error, response, body) => {
  225. if (error) {
  226. throw error
  227. }
  228. expect(response.statusCode).to.equal(200)
  229. done()
  230. }
  231. )
  232. })
  233. it('should have created the folder and restored the doc to it', function (done) {
  234. this.owner.getProject(this.project_id, (error, project) => {
  235. if (error) {
  236. throw error
  237. }
  238. const folder = _.find(
  239. project.rootFolder[0].folders,
  240. folder => folder.name === 'nothere'
  241. )
  242. expect(folder).to.exist
  243. let doc = _.find(folder.docs, doc => doc.name === 'foo3.tex')
  244. doc = MockDocstoreApi.docs[this.project_id][doc._id]
  245. expect(doc.lines).to.deep.equal(['hello world, this is foo-3.tex!'])
  246. done()
  247. })
  248. })
  249. })
  250. describe('restoring to a filename that already exists', function () {
  251. beforeEach(function (done) {
  252. MockProjectHistoryApi.addOldFile(
  253. this.project_id,
  254. 42,
  255. 'main.tex',
  256. 'hello world, this is main.tex!'
  257. )
  258. this.owner.request(
  259. {
  260. method: 'POST',
  261. url: `/project/${this.project_id}/restore_file`,
  262. json: {
  263. pathname: 'main.tex',
  264. version: 42,
  265. },
  266. },
  267. (error, response, body) => {
  268. if (error) {
  269. throw error
  270. }
  271. expect(response.statusCode).to.equal(200)
  272. done()
  273. }
  274. )
  275. })
  276. it('should have created the doc in the root folder', function (done) {
  277. this.owner.getProject(this.project_id, (error, project) => {
  278. if (error) {
  279. throw error
  280. }
  281. let doc = _.find(project.rootFolder[0].docs, doc =>
  282. doc.name.match(/main \(Restored on/)
  283. )
  284. expect(doc).to.exist
  285. doc = MockDocstoreApi.docs[this.project_id][doc._id]
  286. expect(doc.lines).to.deep.equal(['hello world, this is main.tex!'])
  287. done()
  288. })
  289. })
  290. })
  291. })
  292. })