ProjectDuplicateNameTests.mjs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. import { expect } from 'chai'
  2. import sinon from 'sinon'
  3. import Path from 'node:path'
  4. import fs from 'node:fs'
  5. import _ from 'lodash'
  6. import User from './helpers/User.js'
  7. import UserHelper from './helpers/UserHelper.js'
  8. import MockDocstoreApiClass from './mocks/MockDocstoreApi.js'
  9. import MockFilestoreApiClass from './mocks/MockFilestoreApi.js'
  10. import { fileURLToPath } from 'node:url'
  11. let MockDocstoreApi, MockFilestoreApi
  12. const __dirname = fileURLToPath(new URL('.', import.meta.url))
  13. before(function () {
  14. MockDocstoreApi = MockDocstoreApiClass.instance()
  15. MockFilestoreApi = MockFilestoreApiClass.instance()
  16. })
  17. describe('ProjectDuplicateNames', function () {
  18. beforeEach(function (done) {
  19. this.owner = new User()
  20. this.owner.login(done)
  21. this.project = {}
  22. this.callback = sinon.stub()
  23. })
  24. describe('creating a project from the example template', function () {
  25. beforeEach(function (done) {
  26. this.owner.createProject(
  27. 'example-project',
  28. { template: 'example' },
  29. (error, projectId) => {
  30. expect(error).to.not.exist
  31. this.example_project_id = projectId
  32. this.owner.getProject(projectId, (error, project) => {
  33. expect(error).to.not.exist
  34. this.project = project
  35. this.mainTexDoc = _.find(
  36. project.rootFolder[0].docs,
  37. doc => doc.name === 'main.tex'
  38. )
  39. this.refBibDoc = _.find(
  40. project.rootFolder[0].docs,
  41. doc => doc.name === 'sample.bib'
  42. )
  43. this.imageFile = _.find(
  44. project.rootFolder[0].fileRefs,
  45. file => file.name === 'frog.jpg'
  46. )
  47. this.rootFolderId = project.rootFolder[0]._id.toString()
  48. // create a folder called 'testfolder'
  49. this.owner.request.post(
  50. {
  51. uri: `/project/${this.example_project_id}/folder`,
  52. json: {
  53. name: 'testfolder',
  54. parent_folder_id: this.rootFolderId,
  55. },
  56. },
  57. (err, res, body) => {
  58. expect(err).to.not.exist
  59. this.testFolderId = body._id
  60. done()
  61. }
  62. )
  63. })
  64. }
  65. )
  66. })
  67. it('should create a project', function () {
  68. expect(this.project.rootFolder[0].docs.length).to.equal(2)
  69. expect(this.project.rootFolder[0].fileRefs.length).to.equal(1)
  70. })
  71. it('should create two docs in the docstore', function () {
  72. const docs = MockDocstoreApi.docs[this.example_project_id]
  73. expect(Object.keys(docs).length).to.equal(2)
  74. })
  75. it('should create one file in the filestore', function () {
  76. const files = MockFilestoreApi.files[this.example_project_id]
  77. expect(Object.keys(files).length).to.equal(1)
  78. })
  79. describe('for an existing doc', function () {
  80. describe('trying to add a doc with the same name', function () {
  81. beforeEach(function (done) {
  82. this.owner.request.post(
  83. {
  84. uri: `/project/${this.example_project_id}/doc`,
  85. json: {
  86. name: 'main.tex',
  87. parent_folder_id: this.rootFolderId,
  88. },
  89. },
  90. (err, res, body) => {
  91. expect(err).to.not.exist
  92. this.res = res
  93. done()
  94. }
  95. )
  96. })
  97. it('should respond with 400 error status', function () {
  98. expect(this.res.statusCode).to.equal(400)
  99. })
  100. })
  101. describe('trying to add a folder with the same name', function () {
  102. beforeEach(function (done) {
  103. this.owner.request.post(
  104. {
  105. uri: `/project/${this.example_project_id}/folder`,
  106. json: {
  107. name: 'main.tex',
  108. parent_folder_id: this.rootFolderId,
  109. },
  110. },
  111. (err, res, body) => {
  112. expect(err).to.not.exist
  113. this.res = res
  114. done()
  115. }
  116. )
  117. })
  118. it('should respond with 400 error status', function () {
  119. expect(this.res.statusCode).to.equal(400)
  120. })
  121. })
  122. })
  123. describe('for an existing file', function () {
  124. describe('trying to add a doc with the same name', function () {
  125. beforeEach(function (done) {
  126. this.owner.request.post(
  127. {
  128. uri: `/project/${this.example_project_id}/doc`,
  129. json: {
  130. name: 'frog.jpg',
  131. parent_folder_id: this.rootFolderId,
  132. },
  133. },
  134. (err, res, body) => {
  135. expect(err).to.not.exist
  136. this.res = res
  137. done()
  138. }
  139. )
  140. })
  141. it('should respond with 400 error status', function () {
  142. expect(this.res.statusCode).to.equal(400)
  143. })
  144. })
  145. describe('trying to add a folder with the same name', function () {
  146. beforeEach(function (done) {
  147. this.owner.request.post(
  148. {
  149. uri: `/project/${this.example_project_id}/folder`,
  150. json: {
  151. name: 'frog.jpg',
  152. parent_folder_id: this.rootFolderId,
  153. },
  154. },
  155. (err, res, body) => {
  156. expect(err).to.not.exist
  157. this.res = res
  158. done()
  159. }
  160. )
  161. })
  162. it('should respond with 400 error status', function () {
  163. expect(this.res.statusCode).to.equal(400)
  164. })
  165. })
  166. describe('trying to upload a file with the same name', function () {
  167. beforeEach(function (done) {
  168. this.owner.request.post(
  169. {
  170. uri: `/project/${this.example_project_id}/upload`,
  171. json: true,
  172. qs: {
  173. folder_id: this.rootFolderId,
  174. qqfilename: 'frog.jpg',
  175. },
  176. formData: {
  177. name: 'frog.jpg',
  178. qqfile: {
  179. value: fs.createReadStream(
  180. Path.join(__dirname, '/../files/1pixel.png')
  181. ),
  182. options: {
  183. filename: 'frog.jpg',
  184. contentType: 'image/jpeg',
  185. },
  186. },
  187. },
  188. },
  189. (err, res, body) => {
  190. expect(err).to.not.exist
  191. this.body = body
  192. // update the image id because we have replaced the file
  193. this.imageFile._id = this.body.entity_id
  194. done()
  195. }
  196. )
  197. })
  198. it('should succeed (overwriting the file)', function () {
  199. expect(this.body.success).to.equal(true)
  200. })
  201. })
  202. })
  203. // at this point the @imageFile._id has changed
  204. describe('for an existing folder', function () {
  205. describe('trying to add a doc with the same name', function () {
  206. beforeEach(function (done) {
  207. this.owner.request.post(
  208. {
  209. uri: `/project/${this.example_project_id}/doc`,
  210. json: {
  211. name: 'testfolder',
  212. parent_folder_id: this.rootFolderId,
  213. },
  214. },
  215. (err, res, body) => {
  216. expect(err).to.not.exist
  217. this.res = res
  218. done()
  219. }
  220. )
  221. })
  222. it('should respond with 400 error status', function () {
  223. expect(this.res.statusCode).to.equal(400)
  224. })
  225. })
  226. describe('trying to add a folder with the same name', function () {
  227. beforeEach(function (done) {
  228. this.owner.request.post(
  229. {
  230. uri: `/project/${this.example_project_id}/folder`,
  231. json: {
  232. name: 'testfolder',
  233. parent_folder_id: this.rootFolderId,
  234. },
  235. },
  236. (err, res, body) => {
  237. expect(err).to.not.exist
  238. this.res = res
  239. done()
  240. }
  241. )
  242. })
  243. it('should respond with 400 error status', function () {
  244. expect(this.res.statusCode).to.equal(400)
  245. })
  246. })
  247. describe('trying to upload a file with the same name', function () {
  248. beforeEach(function (done) {
  249. this.owner.request.post(
  250. {
  251. uri: `/project/${this.example_project_id}/upload`,
  252. json: true,
  253. qs: {
  254. folder_id: this.rootFolderId,
  255. qqfilename: 'frog.jpg',
  256. },
  257. formData: {
  258. qqfile: {
  259. value: fs.createReadStream(
  260. Path.join(__dirname, '/../files/1pixel.png')
  261. ),
  262. options: {
  263. filename: 'testfolder',
  264. contentType: 'image/jpeg',
  265. },
  266. },
  267. },
  268. },
  269. (err, res, body) => {
  270. expect(err).to.not.exist
  271. this.body = body
  272. done()
  273. }
  274. )
  275. })
  276. it('should respond with failure status', function () {
  277. expect(this.body.success).to.equal(false)
  278. })
  279. })
  280. })
  281. describe('rename for an existing doc', function () {
  282. describe('trying to rename a doc to the same name', function () {
  283. beforeEach(function (done) {
  284. this.owner.request.post(
  285. {
  286. uri: `/project/${this.example_project_id}/doc/${this.refBibDoc._id}/rename`,
  287. json: {
  288. name: 'main.tex',
  289. },
  290. },
  291. (err, res, body) => {
  292. expect(err).to.not.exist
  293. this.res = res
  294. done()
  295. }
  296. )
  297. })
  298. it('should respond with 400 error status', function () {
  299. expect(this.res.statusCode).to.equal(400)
  300. })
  301. })
  302. describe('trying to rename a folder to the same name', function () {
  303. beforeEach(function (done) {
  304. this.owner.request.post(
  305. {
  306. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/rename`,
  307. json: {
  308. name: 'main.tex',
  309. },
  310. },
  311. (err, res, body) => {
  312. expect(err).to.not.exist
  313. this.res = res
  314. done()
  315. }
  316. )
  317. })
  318. it('should respond with 400 error status', function () {
  319. expect(this.res.statusCode).to.equal(400)
  320. })
  321. })
  322. describe('trying to rename a file to the same name', function () {
  323. beforeEach(function (done) {
  324. this.owner.request.post(
  325. {
  326. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/rename`,
  327. json: {
  328. name: 'main.tex',
  329. },
  330. },
  331. (err, res, body) => {
  332. expect(err).to.not.exist
  333. this.res = res
  334. done()
  335. }
  336. )
  337. })
  338. it('should respond with failure status', function () {
  339. expect(this.res.statusCode).to.equal(400)
  340. })
  341. })
  342. })
  343. describe('rename for an existing file', function () {
  344. describe('trying to rename a doc to the same name', function () {
  345. beforeEach(function (done) {
  346. this.owner.request.post(
  347. {
  348. uri: `/project/${this.example_project_id}/doc/${this.refBibDoc._id}/rename`,
  349. json: {
  350. name: 'frog.jpg',
  351. },
  352. },
  353. (err, res, body) => {
  354. expect(err).to.not.exist
  355. this.res = res
  356. done()
  357. }
  358. )
  359. })
  360. it('should respond with 400 error status', function () {
  361. expect(this.res.statusCode).to.equal(400)
  362. })
  363. })
  364. describe('trying to rename a folder to the same name', function () {
  365. beforeEach(function (done) {
  366. this.owner.request.post(
  367. {
  368. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/rename`,
  369. json: {
  370. name: 'frog.jpg',
  371. },
  372. },
  373. (err, res, body) => {
  374. expect(err).to.not.exist
  375. this.res = res
  376. done()
  377. }
  378. )
  379. })
  380. it('should respond with 400 error status', function () {
  381. expect(this.res.statusCode).to.equal(400)
  382. })
  383. })
  384. describe('trying to rename a file to the same name', function () {
  385. beforeEach(function (done) {
  386. this.owner.request.post(
  387. {
  388. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/rename`,
  389. json: {
  390. name: 'frog.jpg',
  391. },
  392. },
  393. (err, res, body) => {
  394. expect(err).to.not.exist
  395. this.res = res
  396. done()
  397. }
  398. )
  399. })
  400. it('should respond with failure status', function () {
  401. expect(this.res.statusCode).to.equal(400)
  402. })
  403. })
  404. })
  405. describe('rename for an existing folder', function () {
  406. describe('trying to rename a doc to the same name', function () {
  407. beforeEach(function (done) {
  408. this.owner.request.post(
  409. {
  410. uri: `/project/${this.example_project_id}/doc/${this.refBibDoc._id}/rename`,
  411. json: {
  412. name: 'testfolder',
  413. },
  414. },
  415. (err, res, body) => {
  416. expect(err).to.not.exist
  417. this.res = res
  418. done()
  419. }
  420. )
  421. })
  422. it('should respond with 400 error status', function () {
  423. expect(this.res.statusCode).to.equal(400)
  424. })
  425. })
  426. describe('trying to rename a folder to the same name', function () {
  427. beforeEach(function (done) {
  428. this.owner.request.post(
  429. {
  430. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/rename`,
  431. json: {
  432. name: 'testfolder',
  433. },
  434. },
  435. (err, res, body) => {
  436. expect(err).to.not.exist
  437. this.res = res
  438. done()
  439. }
  440. )
  441. })
  442. it('should respond with 400 error status', function () {
  443. expect(this.res.statusCode).to.equal(400)
  444. })
  445. })
  446. describe('trying to rename a file to the same name', function () {
  447. beforeEach(function (done) {
  448. this.owner.request.post(
  449. {
  450. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/rename`,
  451. json: {
  452. name: 'testfolder',
  453. },
  454. },
  455. (err, res, body) => {
  456. expect(err).to.not.exist
  457. this.res = res
  458. done()
  459. }
  460. )
  461. })
  462. it('should respond with failure status', function () {
  463. expect(this.res.statusCode).to.equal(400)
  464. })
  465. })
  466. })
  467. describe('for an existing folder with a file with the same name', function () {
  468. beforeEach(function (done) {
  469. this.owner.request.post(
  470. {
  471. uri: `/project/${this.example_project_id}/doc`,
  472. json: {
  473. name: 'main.tex',
  474. parent_folder_id: this.testFolderId,
  475. },
  476. },
  477. (err, res, body) => {
  478. if (err) {
  479. throw err
  480. }
  481. this.owner.request.post(
  482. {
  483. uri: `/project/${this.example_project_id}/doc`,
  484. json: {
  485. name: 'frog.jpg',
  486. parent_folder_id: this.testFolderId,
  487. },
  488. },
  489. (err, res, body) => {
  490. if (err) {
  491. throw err
  492. }
  493. this.owner.request.post(
  494. {
  495. uri: `/project/${this.example_project_id}/folder`,
  496. json: {
  497. name: 'otherFolder',
  498. parent_folder_id: this.testFolderId,
  499. },
  500. },
  501. (err, res, body) => {
  502. if (err) {
  503. throw err
  504. }
  505. this.subFolderId = body._id
  506. this.owner.request.post(
  507. {
  508. uri: `/project/${this.example_project_id}/folder`,
  509. json: {
  510. name: 'otherFolder',
  511. parent_folder_id: this.rootFolderId,
  512. },
  513. },
  514. (err, res, body) => {
  515. if (err) {
  516. throw err
  517. }
  518. this.otherFolderId = body._id
  519. done()
  520. }
  521. )
  522. }
  523. )
  524. }
  525. )
  526. }
  527. )
  528. })
  529. describe('trying to move a doc into the folder', function () {
  530. beforeEach(function (done) {
  531. this.owner.request.post(
  532. {
  533. uri: `/project/${this.example_project_id}/doc/${this.mainTexDoc._id}/move`,
  534. json: {
  535. folder_id: this.testFolderId,
  536. },
  537. },
  538. (err, res, body) => {
  539. expect(err).to.not.exist
  540. this.res = res
  541. done()
  542. }
  543. )
  544. })
  545. it('should respond with 400 error status', function () {
  546. expect(this.res.statusCode).to.equal(400)
  547. })
  548. })
  549. describe('trying to move a file into the folder', function () {
  550. beforeEach(function (done) {
  551. this.owner.request.post(
  552. {
  553. uri: `/project/${this.example_project_id}/file/${this.imageFile._id}/move`,
  554. json: {
  555. folder_id: this.testFolderId,
  556. },
  557. },
  558. (err, res, body) => {
  559. expect(err).to.not.exist
  560. this.res = res
  561. done()
  562. }
  563. )
  564. })
  565. it('should respond with 400 error status', function () {
  566. expect(this.res.statusCode).to.equal(400)
  567. })
  568. })
  569. describe('trying to move a folder into the folder', function () {
  570. beforeEach(function (done) {
  571. this.owner.request.post(
  572. {
  573. uri: `/project/${this.example_project_id}/folder/${this.otherFolderId}/move`,
  574. json: {
  575. folder_id: this.testFolderId,
  576. },
  577. },
  578. (err, res, body) => {
  579. expect(err).to.not.exist
  580. this.res = res
  581. done()
  582. }
  583. )
  584. })
  585. it('should respond with 400 error status', function () {
  586. expect(this.res.statusCode).to.equal(400)
  587. })
  588. })
  589. describe('trying to move a folder into a subfolder of itself', function () {
  590. beforeEach(function (done) {
  591. this.owner.request.post(
  592. {
  593. uri: `/project/${this.example_project_id}/folder/${this.testFolderId}/move`,
  594. json: {
  595. folder_id: this.subFolderId,
  596. },
  597. },
  598. (err, res, body) => {
  599. expect(err).to.not.exist
  600. this.res = res
  601. done()
  602. }
  603. )
  604. })
  605. it('should respond with 400 error status', function () {
  606. expect(this.res.statusCode).to.equal(400)
  607. })
  608. })
  609. })
  610. })
  611. describe('regex characters in title', function () {
  612. let response, userHelper
  613. beforeEach(async function () {
  614. userHelper = new UserHelper()
  615. userHelper = await UserHelper.createUser()
  616. userHelper = await UserHelper.loginUser(
  617. userHelper.getDefaultEmailPassword()
  618. )
  619. })
  620. it('should handle characters that would cause an invalid regular expression', async function () {
  621. const projectName = 'Example (test'
  622. response = await userHelper.fetch('/project/new', {
  623. method: 'POST',
  624. body: new URLSearchParams([['projectName', projectName]]),
  625. })
  626. const body = await response.json()
  627. expect(response.status).to.equal(200) // can create project
  628. response = await userHelper.fetch(`/project/${body.project_id}`)
  629. expect(response.status).to.equal(200) // can open project
  630. })
  631. })
  632. })