ProjectRootDocManagerTests.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. no-useless-escape,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const chai = require('chai')
  15. const should = chai.should()
  16. const { expect } = chai
  17. const sinon = require('sinon')
  18. const modulePath =
  19. '../../../../app/src/Features/Project/ProjectRootDocManager.js'
  20. const SandboxedModule = require('sandboxed-module')
  21. describe('ProjectRootDocManager', function() {
  22. beforeEach(function() {
  23. this.project_id = 'project-123'
  24. this.docPaths = {
  25. 'doc-id-1': '/chapter1.tex',
  26. 'doc-id-2': '/main.tex',
  27. 'doc-id-3': '/nested/chapter1a.tex',
  28. 'doc-id-4': '/nested/chapter1b.tex'
  29. }
  30. this.sl_req_id = 'sl-req-id-123'
  31. this.callback = sinon.stub()
  32. this.globbyFiles = ['a.tex', 'b.tex', 'main.tex']
  33. this.globby = sinon.stub().returns(
  34. new Promise(resolve => {
  35. return resolve(this.globbyFiles)
  36. })
  37. )
  38. this.fs = {
  39. readFile: sinon.stub().callsArgWith(2, new Error('file not found')),
  40. stat: sinon.stub().callsArgWith(1, null, { size: 100 })
  41. }
  42. return (this.ProjectRootDocManager = SandboxedModule.require(modulePath, {
  43. requires: {
  44. './ProjectEntityHandler': (this.ProjectEntityHandler = {}),
  45. './ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {}),
  46. './ProjectGetter': (this.ProjectGetter = {}),
  47. globby: this.globby,
  48. fs: this.fs
  49. }
  50. }))
  51. })
  52. describe('setRootDocAutomatically', function() {
  53. describe('when there is a suitable root doc', function() {
  54. beforeEach(function(done) {
  55. this.docs = {
  56. '/chapter1.tex': {
  57. _id: 'doc-id-1',
  58. lines: [
  59. 'something else',
  60. '\\begin{document}',
  61. 'Hello world',
  62. '\\end{document}'
  63. ]
  64. },
  65. '/main.tex': {
  66. _id: 'doc-id-2',
  67. lines: [
  68. 'different line',
  69. '\\documentclass{article}',
  70. '\\input{chapter1}'
  71. ]
  72. },
  73. '/nested/chapter1a.tex': {
  74. _id: 'doc-id-3',
  75. lines: ['Hello world']
  76. },
  77. '/nested/chapter1b.tex': {
  78. _id: 'doc-id-4',
  79. lines: ['Hello world']
  80. }
  81. }
  82. this.ProjectEntityHandler.getAllDocs = sinon
  83. .stub()
  84. .callsArgWith(1, null, this.docs)
  85. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  86. .stub()
  87. .callsArgWith(2)
  88. return this.ProjectRootDocManager.setRootDocAutomatically(
  89. this.project_id,
  90. done
  91. )
  92. })
  93. it('should check the docs of the project', function() {
  94. return this.ProjectEntityHandler.getAllDocs
  95. .calledWith(this.project_id)
  96. .should.equal(true)
  97. })
  98. it('should set the root doc to the doc containing a documentclass', function() {
  99. return this.ProjectEntityUpdateHandler.setRootDoc
  100. .calledWith(this.project_id, 'doc-id-2')
  101. .should.equal(true)
  102. })
  103. })
  104. describe('when the root doc is an Rtex file', function() {
  105. beforeEach(function() {
  106. this.docs = {
  107. '/chapter1.tex': {
  108. _id: 'doc-id-1',
  109. lines: ['\\begin{document}', 'Hello world', '\\end{document}']
  110. },
  111. '/main.Rtex': {
  112. _id: 'doc-id-2',
  113. lines: ['\\documentclass{article}', '\\input{chapter1}']
  114. }
  115. }
  116. this.ProjectEntityHandler.getAllDocs = sinon
  117. .stub()
  118. .callsArgWith(1, null, this.docs)
  119. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  120. .stub()
  121. .callsArgWith(2)
  122. return this.ProjectRootDocManager.setRootDocAutomatically(
  123. this.project_id,
  124. this.callback
  125. )
  126. })
  127. it('should set the root doc to the doc containing a documentclass', function() {
  128. return this.ProjectEntityUpdateHandler.setRootDoc
  129. .calledWith(this.project_id, 'doc-id-2')
  130. .should.equal(true)
  131. })
  132. })
  133. describe('when there is no suitable root doc', function() {
  134. beforeEach(function(done) {
  135. this.docs = {
  136. '/chapter1.tex': {
  137. _id: 'doc-id-1',
  138. lines: ['\\begin{document}', 'Hello world', '\\end{document}']
  139. },
  140. '/style.bst': {
  141. _id: 'doc-id-2',
  142. lines: ['%Example: \\documentclass{article}']
  143. }
  144. }
  145. this.ProjectEntityHandler.getAllDocs = sinon
  146. .stub()
  147. .callsArgWith(1, null, this.docs)
  148. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  149. .stub()
  150. .callsArgWith(2)
  151. return this.ProjectRootDocManager.setRootDocAutomatically(
  152. this.project_id,
  153. done
  154. )
  155. })
  156. it('should not set the root doc to the doc containing a documentclass', function() {
  157. return this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(
  158. false
  159. )
  160. })
  161. })
  162. })
  163. describe('findRootDocFileFromDirectory', function() {
  164. beforeEach(function() {
  165. this.fs.readFile
  166. .withArgs('/foo/a.tex')
  167. .callsArgWith(2, null, 'Hello World!')
  168. this.fs.readFile
  169. .withArgs('/foo/b.tex')
  170. .callsArgWith(2, null, "I'm a little teapot, get me out of here.")
  171. this.fs.readFile
  172. .withArgs('/foo/main.tex')
  173. .callsArgWith(2, null, "Help, I'm trapped in a unit testing factory")
  174. this.fs.readFile
  175. .withArgs('/foo/c.tex')
  176. .callsArgWith(2, null, 'Tomato, tomahto.')
  177. this.fs.readFile
  178. .withArgs('/foo/a/a.tex')
  179. .callsArgWith(2, null, 'Potato? Potahto. Potootee!')
  180. return (this.documentclassContent = '% test\n\\documentclass\n% test')
  181. })
  182. describe('when there is a file in a subfolder', function() {
  183. beforeEach(function() {
  184. // have to splice globbyFiles weirdly because of the way the stubbed globby method handles references
  185. return this.globbyFiles.splice(
  186. 0,
  187. this.globbyFiles.length,
  188. 'c.tex',
  189. 'a.tex',
  190. 'a/a.tex',
  191. 'b.tex'
  192. )
  193. })
  194. it('processes the root folder files first, and then the subfolder, in alphabetical order', function(done) {
  195. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  196. '/foo',
  197. (error, path) => {
  198. expect(error).not.to.exist
  199. expect(path).not.to.exist
  200. sinon.assert.callOrder(
  201. this.fs.readFile.withArgs('/foo/a.tex'),
  202. this.fs.readFile.withArgs('/foo/b.tex'),
  203. this.fs.readFile.withArgs('/foo/c.tex'),
  204. this.fs.readFile.withArgs('/foo/a/a.tex')
  205. )
  206. return done()
  207. }
  208. )
  209. })
  210. it('processes smaller files first', function(done) {
  211. this.fs.stat.withArgs('/foo/c.tex').callsArgWith(1, null, { size: 1 })
  212. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  213. '/foo',
  214. (error, path) => {
  215. expect(error).not.to.exist
  216. expect(path).not.to.exist
  217. sinon.assert.callOrder(
  218. this.fs.readFile.withArgs('/foo/c.tex'),
  219. this.fs.readFile.withArgs('/foo/a.tex'),
  220. this.fs.readFile.withArgs('/foo/b.tex'),
  221. this.fs.readFile.withArgs('/foo/a/a.tex')
  222. )
  223. return done()
  224. }
  225. )
  226. })
  227. })
  228. describe('when main.tex contains a documentclass', function() {
  229. beforeEach(function() {
  230. return this.fs.readFile
  231. .withArgs('/foo/main.tex')
  232. .callsArgWith(2, null, this.documentclassContent)
  233. })
  234. it('returns main.tex', function(done) {
  235. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  236. '/foo',
  237. (error, path, content) => {
  238. expect(error).not.to.exist
  239. expect(path).to.equal('main.tex')
  240. expect(content).to.equal(this.documentclassContent)
  241. return done()
  242. }
  243. )
  244. })
  245. it('processes main.text first and stops processing when it finds the content', function(done) {
  246. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  247. '/foo',
  248. () => {
  249. expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
  250. expect(this.fs.readFile).not.to.be.calledWith('/foo/a.tex')
  251. return done()
  252. }
  253. )
  254. })
  255. })
  256. describe('when a.tex contains a documentclass', function() {
  257. beforeEach(function() {
  258. return this.fs.readFile
  259. .withArgs('/foo/a.tex')
  260. .callsArgWith(2, null, this.documentclassContent)
  261. })
  262. it('returns a.tex', function(done) {
  263. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  264. '/foo',
  265. (error, path, content) => {
  266. expect(error).not.to.exist
  267. expect(path).to.equal('a.tex')
  268. expect(content).to.equal(this.documentclassContent)
  269. return done()
  270. }
  271. )
  272. })
  273. it('processes main.text first and stops processing when it finds the content', function(done) {
  274. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  275. '/foo',
  276. () => {
  277. expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
  278. expect(this.fs.readFile).to.be.calledWith('/foo/a.tex')
  279. expect(this.fs.readFile).not.to.be.calledWith('/foo/b.tex')
  280. return done()
  281. }
  282. )
  283. })
  284. })
  285. describe('when there is no documentclass', function() {
  286. it('returns null with no error', function(done) {
  287. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  288. '/foo',
  289. (error, path, content) => {
  290. expect(error).not.to.exist
  291. expect(path).not.to.exist
  292. expect(content).not.to.exist
  293. return done()
  294. }
  295. )
  296. })
  297. it('processes all the files', function(done) {
  298. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  299. '/foo',
  300. () => {
  301. expect(this.fs.readFile).to.be.calledWith('/foo/main.tex')
  302. expect(this.fs.readFile).to.be.calledWith('/foo/a.tex')
  303. expect(this.fs.readFile).to.be.calledWith('/foo/b.tex')
  304. return done()
  305. }
  306. )
  307. })
  308. })
  309. describe('when there is an error reading a file', function() {
  310. beforeEach(function() {
  311. return this.fs.readFile
  312. .withArgs('/foo/a.tex')
  313. .callsArgWith(2, new Error('something went wrong'))
  314. })
  315. it('returns an error', function(done) {
  316. return this.ProjectRootDocManager.findRootDocFileFromDirectory(
  317. '/foo',
  318. (error, path, content) => {
  319. expect(error).to.exist
  320. expect(path).not.to.exist
  321. expect(content).not.to.exist
  322. return done()
  323. }
  324. )
  325. })
  326. })
  327. })
  328. describe('setRootDocFromName', function() {
  329. describe('when there is a suitable root doc', function() {
  330. beforeEach(function(done) {
  331. this.docPaths = {
  332. 'doc-id-1': '/chapter1.tex',
  333. 'doc-id-2': '/main.tex',
  334. 'doc-id-3': '/nested/chapter1a.tex',
  335. 'doc-id-4': '/nested/chapter1b.tex'
  336. }
  337. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  338. .stub()
  339. .callsArgWith(1, null, this.docPaths)
  340. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  341. .stub()
  342. .callsArgWith(2)
  343. return this.ProjectRootDocManager.setRootDocFromName(
  344. this.project_id,
  345. '/main.tex',
  346. done
  347. )
  348. })
  349. it('should check the docs of the project', function() {
  350. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  351. .calledWith(this.project_id)
  352. .should.equal(true)
  353. })
  354. it('should set the root doc to main.tex', function() {
  355. return this.ProjectEntityUpdateHandler.setRootDoc
  356. .calledWith(this.project_id, 'doc-id-2')
  357. .should.equal(true)
  358. })
  359. })
  360. describe('when there is a suitable root doc but the leading slash is missing', function() {
  361. beforeEach(function(done) {
  362. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  363. .stub()
  364. .callsArgWith(1, null, this.docPaths)
  365. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  366. .stub()
  367. .callsArgWith(2)
  368. return this.ProjectRootDocManager.setRootDocFromName(
  369. this.project_id,
  370. 'main.tex',
  371. done
  372. )
  373. })
  374. it('should check the docs of the project', function() {
  375. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  376. .calledWith(this.project_id)
  377. .should.equal(true)
  378. })
  379. it('should set the root doc to main.tex', function() {
  380. return this.ProjectEntityUpdateHandler.setRootDoc
  381. .calledWith(this.project_id, 'doc-id-2')
  382. .should.equal(true)
  383. })
  384. })
  385. describe('when there is a suitable root doc with a basename match', function() {
  386. beforeEach(function(done) {
  387. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  388. .stub()
  389. .callsArgWith(1, null, this.docPaths)
  390. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  391. .stub()
  392. .callsArgWith(2)
  393. return this.ProjectRootDocManager.setRootDocFromName(
  394. this.project_id,
  395. 'chapter1a.tex',
  396. done
  397. )
  398. })
  399. it('should check the docs of the project', function() {
  400. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  401. .calledWith(this.project_id)
  402. .should.equal(true)
  403. })
  404. it('should set the root doc using the basename', function() {
  405. return this.ProjectEntityUpdateHandler.setRootDoc
  406. .calledWith(this.project_id, 'doc-id-3')
  407. .should.equal(true)
  408. })
  409. })
  410. describe('when there is a suitable root doc but the filename is in quotes', function() {
  411. beforeEach(function(done) {
  412. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  413. .stub()
  414. .callsArgWith(1, null, this.docPaths)
  415. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  416. .stub()
  417. .callsArgWith(2)
  418. return this.ProjectRootDocManager.setRootDocFromName(
  419. this.project_id,
  420. "'main.tex'",
  421. done
  422. )
  423. })
  424. it('should check the docs of the project', function() {
  425. return this.ProjectEntityHandler.getAllDocPathsFromProjectById
  426. .calledWith(this.project_id)
  427. .should.equal(true)
  428. })
  429. it('should set the root doc to main.tex', function() {
  430. return this.ProjectEntityUpdateHandler.setRootDoc
  431. .calledWith(this.project_id, 'doc-id-2')
  432. .should.equal(true)
  433. })
  434. })
  435. describe('when there is no suitable root doc', function() {
  436. beforeEach(function(done) {
  437. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  438. .stub()
  439. .callsArgWith(1, null, this.docPaths)
  440. this.ProjectEntityUpdateHandler.setRootDoc = sinon
  441. .stub()
  442. .callsArgWith(2)
  443. return this.ProjectRootDocManager.setRootDocFromName(
  444. this.project_id,
  445. 'other.tex',
  446. done
  447. )
  448. })
  449. it('should not set the root doc', function() {
  450. return this.ProjectEntityUpdateHandler.setRootDoc.called.should.equal(
  451. false
  452. )
  453. })
  454. })
  455. })
  456. describe('ensureRootDocumentIsSet', function() {
  457. beforeEach(function() {
  458. this.project = {}
  459. this.ProjectGetter.getProject = sinon
  460. .stub()
  461. .callsArgWith(2, null, this.project)
  462. return (this.ProjectRootDocManager.setRootDocAutomatically = sinon
  463. .stub()
  464. .callsArgWith(1, null))
  465. })
  466. describe('when the root doc is set', function() {
  467. beforeEach(function() {
  468. this.project.rootDoc_id = 'root-doc-id'
  469. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  470. this.project_id,
  471. this.callback
  472. )
  473. })
  474. it('should find the project fetching only the rootDoc_id field', function() {
  475. return this.ProjectGetter.getProject
  476. .calledWith(this.project_id, { rootDoc_id: 1 })
  477. .should.equal(true)
  478. })
  479. it('should not try to update the project rootDoc_id', function() {
  480. return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
  481. false
  482. )
  483. })
  484. it('should call the callback', function() {
  485. return this.callback.called.should.equal(true)
  486. })
  487. })
  488. describe('when the root doc is not set', function() {
  489. beforeEach(function() {
  490. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  491. this.project_id,
  492. this.callback
  493. )
  494. })
  495. it('should find the project with only the rootDoc_id field', function() {
  496. return this.ProjectGetter.getProject
  497. .calledWith(this.project_id, { rootDoc_id: 1 })
  498. .should.equal(true)
  499. })
  500. it('should update the project rootDoc_id', function() {
  501. return this.ProjectRootDocManager.setRootDocAutomatically
  502. .calledWith(this.project_id)
  503. .should.equal(true)
  504. })
  505. it('should call the callback', function() {
  506. return this.callback.called.should.equal(true)
  507. })
  508. })
  509. describe('when the project does not exist', function() {
  510. beforeEach(function() {
  511. this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
  512. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  513. this.project_id,
  514. this.callback
  515. )
  516. })
  517. it('should call the callback with an error', function() {
  518. return this.callback
  519. .calledWith(new Error('project not found'))
  520. .should.equal(true)
  521. })
  522. })
  523. })
  524. describe('ensureRootDocumentIsValid', function() {
  525. beforeEach(function() {
  526. this.project = {}
  527. this.ProjectGetter.getProject = sinon
  528. .stub()
  529. .callsArgWith(2, null, this.project)
  530. this.ProjectEntityUpdateHandler.setRootDoc = sinon.stub().yields()
  531. this.ProjectEntityHandler.getAllDocPathsFromProjectById = sinon
  532. .stub()
  533. .callsArgWith(1, null, this.docPaths)
  534. return (this.ProjectRootDocManager.setRootDocAutomatically = sinon
  535. .stub()
  536. .callsArgWith(1, null))
  537. })
  538. describe('when the root doc is set', function() {
  539. describe('when the root doc is valid', function() {
  540. beforeEach(function() {
  541. this.project.rootDoc_id = 'doc-id-2'
  542. return this.ProjectRootDocManager.ensureRootDocumentIsValid(
  543. this.project_id,
  544. this.callback
  545. )
  546. })
  547. it('should find the project fetching only the rootDoc_id field', function() {
  548. return this.ProjectGetter.getProject
  549. .calledWith(this.project_id, { rootDoc_id: 1 })
  550. .should.equal(true)
  551. })
  552. it('should not try to update the project rootDoc_id', function() {
  553. return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
  554. false
  555. )
  556. })
  557. it('should call the callback', function() {
  558. return this.callback.called.should.equal(true)
  559. })
  560. })
  561. describe('when the root doc is not valid', function() {
  562. beforeEach(function() {
  563. this.project.rootDoc_id = 'bogus-doc-id'
  564. return this.ProjectRootDocManager.ensureRootDocumentIsValid(
  565. this.project_id,
  566. this.callback
  567. )
  568. })
  569. it('should find the project fetching only the rootDoc_id field', function() {
  570. return this.ProjectGetter.getProject
  571. .calledWith(this.project_id, { rootDoc_id: 1 })
  572. .should.equal(true)
  573. })
  574. it('should null the rootDoc_id field', function() {
  575. return this.ProjectEntityUpdateHandler.setRootDoc
  576. .calledWith(this.project_id, null)
  577. .should.equal(true)
  578. })
  579. it('should try to find a new rootDoc', function() {
  580. return this.ProjectRootDocManager.setRootDocAutomatically.called.should.equal(
  581. true
  582. )
  583. })
  584. it('should call the callback', function() {
  585. return this.callback.called.should.equal(true)
  586. })
  587. })
  588. })
  589. describe('when the root doc is not set', function() {
  590. beforeEach(function() {
  591. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  592. this.project_id,
  593. this.callback
  594. )
  595. })
  596. it('should find the project fetching only the rootDoc_id fiel', function() {
  597. return this.ProjectGetter.getProject
  598. .calledWith(this.project_id, { rootDoc_id: 1 })
  599. .should.equal(true)
  600. })
  601. it('should update the project rootDoc_id', function() {
  602. return this.ProjectRootDocManager.setRootDocAutomatically
  603. .calledWith(this.project_id)
  604. .should.equal(true)
  605. })
  606. it('should call the callback', function() {
  607. return this.callback.called.should.equal(true)
  608. })
  609. })
  610. describe('when the project does not exist', function() {
  611. beforeEach(function() {
  612. this.ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null)
  613. return this.ProjectRootDocManager.ensureRootDocumentIsSet(
  614. this.project_id,
  615. this.callback
  616. )
  617. })
  618. it('should call the callback with an error', function() {
  619. return this.callback
  620. .calledWith(new Error('project not found'))
  621. .should.equal(true)
  622. })
  623. })
  624. })
  625. })