LinkedFilesTests.coffee 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. async = require "async"
  2. expect = require("chai").expect
  3. _ = require 'underscore'
  4. mkdirp = require "mkdirp"
  5. Settings = require "settings-sharelatex"
  6. MockFileStoreApi = require './helpers/MockFileStoreApi'
  7. request = require "./helpers/request"
  8. User = require "./helpers/User"
  9. MockClsiApi = require "./helpers/MockClsiApi"
  10. express = require("express")
  11. LinkedUrlProxy = express()
  12. LinkedUrlProxy.get "/", (req, res, next) =>
  13. if req.query.url == 'http://example.com/foo'
  14. res.send('foo foo foo')
  15. else if req.query.url == 'http://example.com/bar'
  16. res.send('bar bar bar')
  17. else
  18. res.sendStatus(404)
  19. describe "LinkedFiles", ->
  20. before (done) ->
  21. LinkedUrlProxy.listen 6543, (error) =>
  22. return done(error) if error?
  23. @owner = new User()
  24. @owner.login ->
  25. mkdirp Settings.path.dumpFolder, done
  26. describe "creating a project linked file", ->
  27. before (done) ->
  28. @source_doc_name = 'test.txt'
  29. async.series [
  30. (cb) =>
  31. @owner.createProject 'plf-test-one', {template: 'blank'}, (error, project_id) =>
  32. @project_one_id = project_id
  33. cb(error)
  34. (cb) =>
  35. @owner.getProject @project_one_id, (error, project) =>
  36. @project_one = project
  37. @project_one_root_folder_id = project.rootFolder[0]._id.toString()
  38. cb(error)
  39. (cb) =>
  40. @owner.createProject 'plf-test-two', {template: 'blank'}, (error, project_id) =>
  41. @project_two_id = project_id
  42. cb(error)
  43. (cb) =>
  44. @owner.getProject @project_two_id, (error, project) =>
  45. @project_two = project
  46. @project_two_root_folder_id = project.rootFolder[0]._id.toString()
  47. cb(error)
  48. (cb) =>
  49. @owner.createDocInProject @project_two_id,
  50. @project_two_root_folder_id,
  51. @source_doc_name,
  52. (error, doc_id) =>
  53. @source_doc_id = doc_id
  54. cb(error)
  55. (cb) =>
  56. @owner.createDocInProject @project_two_id,
  57. @project_two_root_folder_id,
  58. 'some-harmless-doc.txt',
  59. (error, doc_id) =>
  60. cb(error)
  61. ], done
  62. it 'should produce a list of the users projects', (done) ->
  63. @owner.request.get {
  64. url: "/user/projects",
  65. json: true
  66. }, (err, response, body) =>
  67. expect(err).to.not.exist
  68. expect(body).to.deep.equal {
  69. projects: [
  70. { _id: @project_one_id, name: 'plf-test-one', accessLevel: 'owner' },
  71. { _id: @project_two_id, name: 'plf-test-two', accessLevel: 'owner' }
  72. ]
  73. }
  74. done()
  75. it 'should produce a list of entities in the project', (done) ->
  76. @owner.request.get {
  77. url: "/project/#{@project_two_id}/entities",
  78. json: true
  79. }, (err, response, body) =>
  80. expect(err).to.not.exist
  81. expect(body).to.deep.equal {
  82. project_id: @project_two_id,
  83. entities: [
  84. { path: '/main.tex', type: 'doc' },
  85. { path: '/some-harmless-doc.txt', type: 'doc' },
  86. { path: '/test.txt', type: 'doc' }
  87. ]
  88. }
  89. done()
  90. it 'should import a file from the source project', (done) ->
  91. @owner.request.post {
  92. url: "/project/#{@project_one_id}/linked_file",
  93. json:
  94. name: 'test-link.txt',
  95. parent_folder_id: @project_one_root_folder_id,
  96. provider: 'project_file',
  97. data:
  98. source_project_id: @project_two_id,
  99. source_entity_path: "/#{@source_doc_name}",
  100. }, (error, response, body) =>
  101. expect(response.statusCode).to.equal 200
  102. new_file_id = body.new_file_id
  103. @existing_file_id = new_file_id
  104. expect(new_file_id).to.exist
  105. @owner.getProject @project_one_id, (error, project) =>
  106. return done(error) if error?
  107. firstFile = project.rootFolder[0].fileRefs[0]
  108. expect(firstFile._id.toString()).to.equal(new_file_id.toString())
  109. expect(firstFile.linkedFileData).to.deep.equal {
  110. provider: 'project_file',
  111. source_project_id: @project_two_id,
  112. source_entity_path: "/#{@source_doc_name}",
  113. }
  114. expect(firstFile.name).to.equal('test-link.txt')
  115. done()
  116. it 'should refresh the file', (done) ->
  117. @owner.request.post {
  118. url: "/project/#{@project_one_id}/linked_file/#{@existing_file_id}/refresh",
  119. json: true
  120. }, (error, response, body) =>
  121. expect(response.statusCode).to.equal 200
  122. new_file_id = body.new_file_id
  123. expect(new_file_id).to.exist
  124. expect(new_file_id).to.not.equal @existing_file_id
  125. @refreshed_file_id = new_file_id
  126. @owner.getProject @project_one_id, (error, project) =>
  127. return done(error) if error?
  128. firstFile = project.rootFolder[0].fileRefs[0]
  129. expect(firstFile._id.toString()).to.equal(new_file_id.toString())
  130. expect(firstFile.name).to.equal('test-link.txt')
  131. done()
  132. it 'should not allow to create a linked-file with v1 id', (done) ->
  133. @owner.request.post {
  134. url: "/project/#{@project_one_id}/linked_file",
  135. json:
  136. name: 'test-link-should-not-work.txt',
  137. parent_folder_id: @project_one_root_folder_id,
  138. provider: 'project_file',
  139. data:
  140. v1_source_doc_id: 1234
  141. source_entity_path: "/#{@source_doc_name}",
  142. }, (error, response, body) =>
  143. expect(response.statusCode).to.equal 403
  144. expect(body).to.equal 'You do not have access to this project'
  145. done()
  146. describe "with a linked project_file from a v1 project that has not been imported", ->
  147. before (done) ->
  148. async.series [
  149. (cb) =>
  150. @owner.createProject 'plf-v1-test-one', {template: 'blank'}, (error, project_id) =>
  151. @project_one_id = project_id
  152. cb(error)
  153. (cb) =>
  154. @owner.getProject @project_one_id, (error, project) =>
  155. @project_one = project
  156. @project_one_root_folder_id = project.rootFolder[0]._id.toString()
  157. @project_one.rootFolder[0].fileRefs.push {
  158. linkedFileData: {
  159. provider: "project_file",
  160. v1_source_doc_id: 9999999, # We won't find this id in the database
  161. source_entity_path: "example.jpeg"
  162. },
  163. _id: "abcd",
  164. rev: 0,
  165. created: new Date(),
  166. name: "example.jpeg"
  167. }
  168. @owner.saveProject @project_one, cb
  169. ], done
  170. it 'should refuse to refresh', (done) ->
  171. @owner.request.post {
  172. url: "/project/#{@project_one_id}/linked_file/abcd/refresh",
  173. json: true
  174. }, (error, response, body) =>
  175. expect(response.statusCode).to.equal 409
  176. expect(body).to.equal "Sorry, the source project is not yet imported to Overleaf v2. Please import it to Overleaf v2 to refresh this file"
  177. done()
  178. describe "creating a URL based linked file", ->
  179. before (done) ->
  180. @owner.createProject "url-linked-files-project", {template: "blank"}, (error, project_id) =>
  181. throw error if error?
  182. @project_id = project_id
  183. @owner.getProject project_id, (error, project) =>
  184. throw error if error?
  185. @project = project
  186. @root_folder_id = project.rootFolder[0]._id.toString()
  187. done()
  188. it "should download the URL and create a file with the contents and linkedFileData", (done) ->
  189. @owner.request.post {
  190. url: "/project/#{@project_id}/linked_file",
  191. json:
  192. provider: 'url'
  193. data: {
  194. url: 'http://example.com/foo'
  195. }
  196. parent_folder_id: @root_folder_id
  197. name: 'url-test-file-1'
  198. }, (error, response, body) =>
  199. throw error if error?
  200. expect(response.statusCode).to.equal 200
  201. @owner.getProject @project_id, (error, project) =>
  202. throw error if error?
  203. file = project.rootFolder[0].fileRefs[0]
  204. expect(file.linkedFileData).to.deep.equal({
  205. provider: 'url'
  206. url: 'http://example.com/foo'
  207. })
  208. @owner.request.get "/project/#{@project_id}/file/#{file._id}", (error, response, body) ->
  209. throw error if error?
  210. expect(response.statusCode).to.equal 200
  211. expect(body).to.equal "foo foo foo"
  212. done()
  213. it "should replace and update a URL based linked file", (done) ->
  214. @owner.request.post {
  215. url: "/project/#{@project_id}/linked_file",
  216. json:
  217. provider: 'url'
  218. data: {
  219. url: 'http://example.com/foo'
  220. }
  221. parent_folder_id: @root_folder_id
  222. name: 'url-test-file-2'
  223. }, (error, response, body) =>
  224. throw error if error?
  225. expect(response.statusCode).to.equal 200
  226. @owner.request.post {
  227. url: "/project/#{@project_id}/linked_file",
  228. json:
  229. provider: 'url'
  230. data: {
  231. url: 'http://example.com/bar'
  232. }
  233. parent_folder_id: @root_folder_id
  234. name: 'url-test-file-2'
  235. }, (error, response, body) =>
  236. throw error if error?
  237. expect(response.statusCode).to.equal 200
  238. @owner.getProject @project_id, (error, project) =>
  239. throw error if error?
  240. file = project.rootFolder[0].fileRefs[1]
  241. expect(file.linkedFileData).to.deep.equal({
  242. provider: 'url'
  243. url: 'http://example.com/bar'
  244. })
  245. @owner.request.get "/project/#{@project_id}/file/#{file._id}", (error, response, body) ->
  246. throw error if error?
  247. expect(response.statusCode).to.equal 200
  248. expect(body).to.equal "bar bar bar"
  249. done()
  250. it "should return an error if the URL does not succeed", (done) ->
  251. @owner.request.post {
  252. url: "/project/#{@project_id}/linked_file",
  253. json:
  254. provider: 'url'
  255. data: {
  256. url: 'http://example.com/does-not-exist'
  257. }
  258. parent_folder_id: @root_folder_id
  259. name: 'url-test-file-3'
  260. }, (error, response, body) =>
  261. throw error if error?
  262. expect(response.statusCode).to.equal 422 # unprocessable
  263. expect(body).to.equal(
  264. "Your URL could not be reached (404 status code). Please check it and try again."
  265. )
  266. done()
  267. it "should return an error if the URL is invalid", (done) ->
  268. @owner.request.post {
  269. url: "/project/#{@project_id}/linked_file",
  270. json:
  271. provider: 'url'
  272. data: {
  273. url: "!^$%"
  274. }
  275. parent_folder_id: @root_folder_id
  276. name: 'url-test-file-4'
  277. }, (error, response, body) =>
  278. throw error if error?
  279. expect(response.statusCode).to.equal 422 # unprocessable
  280. expect(body).to.equal(
  281. "Your URL is not valid. Please check it and try again."
  282. )
  283. done()
  284. it "should return an error if the URL uses a non-http protocol", (done) ->
  285. @owner.request.post {
  286. url: "/project/#{@project_id}/linked_file",
  287. json:
  288. provider: 'url'
  289. data: {
  290. url: "ftp://localhost"
  291. }
  292. parent_folder_id: @root_folder_id
  293. name: 'url-test-file-5'
  294. }, (error, response, body) =>
  295. throw error if error?
  296. expect(response.statusCode).to.equal 422 # unprocessable
  297. expect(body).to.equal(
  298. "Your URL is not valid. Please check it and try again."
  299. )
  300. done()
  301. it "should accept a URL withuot a leading http://, and add it", (done) ->
  302. @owner.request.post {
  303. url: "/project/#{@project_id}/linked_file",
  304. json:
  305. provider: 'url'
  306. data: {
  307. url: 'example.com/foo'
  308. }
  309. parent_folder_id: @root_folder_id
  310. name: 'url-test-file-6'
  311. }, (error, response, body) =>
  312. throw error if error?
  313. expect(response.statusCode).to.equal 200
  314. @owner.getProject @project_id, (error, project) =>
  315. throw error if error?
  316. file = _.find project.rootFolder[0].fileRefs, (file) ->
  317. file.name == 'url-test-file-6'
  318. expect(file.linkedFileData).to.deep.equal({
  319. provider: 'url'
  320. url: 'http://example.com/foo'
  321. })
  322. @owner.request.get "/project/#{@project_id}/file/#{file._id}", (error, response, body) ->
  323. throw error if error?
  324. expect(response.statusCode).to.equal 200
  325. expect(body).to.equal "foo foo foo"
  326. done()
  327. # TODO: Add test for asking for host that return ENOTFOUND
  328. # (This will probably end up handled by the proxy)
  329. describe "creating a linked output file", ->
  330. before (done) ->
  331. async.series [
  332. (cb) =>
  333. @owner.createProject 'output-test-one', {template: 'blank'}, (error, project_id) =>
  334. @project_one_id = project_id
  335. cb(error)
  336. (cb) =>
  337. @owner.getProject @project_one_id, (error, project) =>
  338. @project_one = project
  339. @project_one_root_folder_id = project.rootFolder[0]._id.toString()
  340. cb(error)
  341. (cb) =>
  342. @owner.createProject 'output-test-two', {template: 'blank'}, (error, project_id) =>
  343. @project_two_id = project_id
  344. cb(error)
  345. (cb) =>
  346. @owner.getProject @project_two_id, (error, project) =>
  347. @project_two = project
  348. @project_two_root_folder_id = project.rootFolder[0]._id.toString()
  349. cb(error)
  350. ], done
  351. it 'should import the project.pdf file from the source project', (done) ->
  352. @owner.request.post {
  353. url: "/project/#{@project_one_id}/linked_file",
  354. json:
  355. name: 'test.pdf',
  356. parent_folder_id: @project_one_root_folder_id,
  357. provider: 'project_output_file',
  358. data:
  359. source_project_id: @project_two_id,
  360. source_output_file_path: "project.pdf",
  361. build_id: '1234-abcd'
  362. }, (error, response, body) =>
  363. new_file_id = body.new_file_id
  364. @existing_file_id = new_file_id
  365. expect(new_file_id).to.exist
  366. @owner.getProject @project_one_id, (error, project) =>
  367. return done(error) if error?
  368. firstFile = project.rootFolder[0].fileRefs[0]
  369. expect(firstFile._id.toString()).to.equal(new_file_id.toString())
  370. expect(firstFile.linkedFileData).to.deep.equal {
  371. provider: 'project_output_file',
  372. source_project_id: @project_two_id,
  373. source_output_file_path: "project.pdf",
  374. build_id: '1234-abcd'
  375. }
  376. expect(firstFile.name).to.equal('test.pdf')
  377. done()
  378. it 'should refresh the file', (done) ->
  379. @owner.request.post {
  380. url: "/project/#{@project_one_id}/linked_file/#{@existing_file_id}/refresh",
  381. json: true
  382. }, (error, response, body) =>
  383. new_file_id = body.new_file_id
  384. expect(new_file_id).to.exist
  385. expect(new_file_id).to.not.equal @existing_file_id
  386. @refreshed_file_id = new_file_id
  387. @owner.getProject @project_one_id, (error, project) =>
  388. return done(error) if error?
  389. firstFile = project.rootFolder[0].fileRefs[0]
  390. expect(firstFile._id.toString()).to.equal(new_file_id.toString())
  391. expect(firstFile.name).to.equal('test.pdf')
  392. done()
  393. describe "with a linked project_output_file from a v1 project that has not been imported", ->
  394. before (done) ->
  395. async.series [
  396. (cb) =>
  397. @owner.createProject 'output-v1-test-one', {template: 'blank'}, (error, project_id) =>
  398. @project_one_id = project_id
  399. cb(error)
  400. (cb) =>
  401. @owner.getProject @project_one_id, (error, project) =>
  402. @project_one = project
  403. @project_one_root_folder_id = project.rootFolder[0]._id.toString()
  404. @project_one.rootFolder[0].fileRefs.push {
  405. linkedFileData: {
  406. provider: "project_output_file",
  407. v1_source_doc_id: 9999999, # We won't find this id in the database
  408. source_output_file_path: "project.pdf"
  409. },
  410. _id: "abcdef",
  411. rev: 0,
  412. created: new Date(),
  413. name: "whatever.pdf"
  414. }
  415. @owner.saveProject @project_one, cb
  416. ], done
  417. it 'should refuse to refresh', (done) ->
  418. @owner.request.post {
  419. url: "/project/#{@project_one_id}/linked_file/abcdef/refresh",
  420. json: true
  421. }, (error, response, body) =>
  422. expect(response.statusCode).to.equal 409
  423. expect(body).to.equal "Sorry, the source project is not yet imported to Overleaf v2. Please import it to Overleaf v2 to refresh this file"
  424. done()