ArchiveDocsTests.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931
  1. import Settings from '@overleaf/settings'
  2. import { expect } from 'chai'
  3. import mongodb from '../../../app/js/mongodb.js'
  4. import async from 'async'
  5. import DocstoreApp from './helpers/DocstoreApp.js'
  6. import DocstoreClient from './helpers/DocstoreClient.js'
  7. import { Storage } from '@google-cloud/storage'
  8. import Persistor from '../../../app/js/PersistorManager.js'
  9. import { ReadableString } from '@overleaf/stream-utils'
  10. import { callbackify } from 'node:util'
  11. import Crypto from 'node:crypto'
  12. const { db, ObjectId } = mongodb
  13. async function uploadContent(path, json) {
  14. const stream = new ReadableString(JSON.stringify(json))
  15. await Persistor.sendStream(Settings.docstore.bucket, path, stream)
  16. }
  17. describe('Archiving', function () {
  18. before(async function () {
  19. await DocstoreApp.ensureRunning()
  20. })
  21. before(async function () {
  22. const storage = new Storage(Settings.docstore.gcs.endpoint)
  23. await storage.createBucket(Settings.docstore.bucket)
  24. await storage.createBucket(`${Settings.docstore.bucket}-deleted`)
  25. })
  26. after(async function () {
  27. // Tear down the buckets created above
  28. const storage = new Storage(Settings.docstore.gcs.endpoint)
  29. await storage.bucket(Settings.docstore.bucket).deleteFiles()
  30. await storage.bucket(Settings.docstore.bucket).delete()
  31. await storage.bucket(`${Settings.docstore.bucket}-deleted`).deleteFiles()
  32. await storage.bucket(`${Settings.docstore.bucket}-deleted`).delete()
  33. })
  34. describe('multiple docs in a project', function () {
  35. before(function (done) {
  36. this.project_id = new ObjectId()
  37. this.docs = [
  38. {
  39. _id: new ObjectId(),
  40. lines: ['one', 'two', 'three'],
  41. ranges: {},
  42. version: 2,
  43. },
  44. {
  45. _id: new ObjectId(),
  46. lines: ['aaa', 'bbb', 'ccc'],
  47. ranges: {},
  48. version: 4,
  49. },
  50. ]
  51. const jobs = this.docs.map(doc =>
  52. (doc => callback => {
  53. callbackify(DocstoreClient.createDoc)(
  54. this.project_id,
  55. doc._id,
  56. doc.lines,
  57. doc.version,
  58. doc.ranges,
  59. callback
  60. )
  61. })(doc)
  62. )
  63. async.series(jobs, error => {
  64. if (error != null) {
  65. throw error
  66. }
  67. DocstoreClient.archiveAllDoc(this.project_id)
  68. .then(res => {
  69. this.res = res
  70. done()
  71. })
  72. .catch(done)
  73. })
  74. })
  75. it('should archive all the docs', function () {
  76. this.res.status.should.equal(204)
  77. })
  78. it('should set inS3 and unset lines and ranges in each doc', function (done) {
  79. const jobs = this.docs.map(doc =>
  80. (
  81. doc => callback =>
  82. db.docs.findOne({ _id: doc._id }, (error, doc) => {
  83. if (error) return callback(error)
  84. expect(doc.lines).not.to.exist
  85. expect(doc.ranges).not.to.exist
  86. doc.inS3.should.equal(true)
  87. callback()
  88. })
  89. )(doc)
  90. )
  91. async.series(jobs, done)
  92. })
  93. it('should set the docs in s3 correctly', function (done) {
  94. const jobs = this.docs.map(doc =>
  95. (
  96. doc => callback =>
  97. DocstoreClient.getS3Doc(this.project_id, doc._id)
  98. .then(s3Doc => {
  99. s3Doc.lines.should.deep.equal(doc.lines)
  100. s3Doc.ranges.should.deep.equal(doc.ranges)
  101. callback()
  102. })
  103. .catch(callback)
  104. )(doc)
  105. )
  106. async.series(jobs, done)
  107. })
  108. describe('after unarchiving from a request for the project', function () {
  109. before(async function () {
  110. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  111. })
  112. it('should return the docs', function () {
  113. for (let i = 0; i < this.fetched_docs.length; i++) {
  114. const doc = this.fetched_docs[i]
  115. doc.lines.should.deep.equal(this.docs[i].lines)
  116. }
  117. })
  118. it('should restore the docs to mongo', function (done) {
  119. const jobs = this.docs.map((doc, i) =>
  120. (
  121. (doc, i) => callback =>
  122. db.docs.findOne({ _id: doc._id }, (error, doc) => {
  123. if (error) return callback(error)
  124. doc.lines.should.deep.equal(this.docs[i].lines)
  125. doc.ranges.should.deep.equal(this.docs[i].ranges)
  126. expect(doc.inS3).not.to.exist
  127. callback()
  128. })
  129. )(doc, i)
  130. )
  131. async.series(jobs, done)
  132. })
  133. })
  134. })
  135. describe('a deleted doc', function () {
  136. beforeEach(async function () {
  137. this.project_id = new ObjectId()
  138. this.doc = {
  139. _id: new ObjectId(),
  140. lines: ['one', 'two', 'three'],
  141. ranges: {},
  142. version: 2,
  143. }
  144. await DocstoreClient.createDoc(
  145. this.project_id,
  146. this.doc._id,
  147. this.doc.lines,
  148. this.doc.version,
  149. this.doc.ranges
  150. )
  151. await DocstoreClient.deleteDoc(this.project_id, this.doc._id)
  152. this.res = await DocstoreClient.archiveAllDoc(this.project_id)
  153. })
  154. it('should successully archive the docs', function () {
  155. this.res.status.should.equal(204)
  156. })
  157. it('should set inS3 and unset lines and ranges in each doc', async function () {
  158. const doc = await db.docs.findOne({ _id: this.doc._id })
  159. expect(doc.lines).not.to.exist
  160. expect(doc.ranges).not.to.exist
  161. doc.inS3.should.equal(true)
  162. doc.deleted.should.equal(true)
  163. })
  164. it('should set the doc in s3 correctly', async function () {
  165. const s3Doc = await DocstoreClient.getS3Doc(this.project_id, this.doc._id)
  166. s3Doc.lines.should.deep.equal(this.doc.lines)
  167. s3Doc.ranges.should.deep.equal(this.doc.ranges)
  168. })
  169. describe('after unarchiving from a request for the project', function () {
  170. beforeEach(async function () {
  171. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  172. })
  173. it('should not included the deleted', function () {
  174. this.fetched_docs.length.should.equal(0)
  175. })
  176. it('should restore the doc to mongo', async function () {
  177. const doc = await db.docs.findOne({ _id: this.doc._id })
  178. doc.lines.should.deep.equal(this.doc.lines)
  179. doc.ranges.should.deep.equal(this.doc.ranges)
  180. expect(doc.inS3).not.to.exist
  181. doc.deleted.should.equal(true)
  182. })
  183. })
  184. describe('when keepSoftDeletedDocsArchived is enabled', function () {
  185. let keepSoftDeletedDocsArchived
  186. beforeEach(function overwriteSetting() {
  187. keepSoftDeletedDocsArchived =
  188. Settings.docstore.keepSoftDeletedDocsArchived
  189. Settings.docstore.keepSoftDeletedDocsArchived = true
  190. })
  191. afterEach(function restoreSetting() {
  192. Settings.docstore.keepSoftDeletedDocsArchived =
  193. keepSoftDeletedDocsArchived
  194. })
  195. describe('after unarchiving from a request for the project', function () {
  196. beforeEach(async function () {
  197. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  198. })
  199. it('should not included the deleted', function () {
  200. this.fetched_docs.length.should.equal(0)
  201. })
  202. it('should not have restored the deleted doc to mongo', async function () {
  203. const doc = await db.docs.findOne({ _id: this.doc._id })
  204. expect(doc.lines).to.not.exist
  205. expect(doc.ranges).to.not.exist
  206. expect(doc.inS3).to.equal(true)
  207. expect(doc.deleted).to.equal(true)
  208. })
  209. })
  210. })
  211. })
  212. describe('archiving a single doc', function () {
  213. before(async function () {
  214. this.project_id = new ObjectId()
  215. this.timeout(1000 * 30)
  216. this.doc = {
  217. _id: new ObjectId(),
  218. lines: ['foo', 'bar'],
  219. ranges: {},
  220. version: 2,
  221. }
  222. await DocstoreClient.createDoc(
  223. this.project_id,
  224. this.doc._id,
  225. this.doc.lines,
  226. this.doc.version,
  227. this.doc.ranges
  228. )
  229. this.res = await DocstoreClient.archiveDoc(this.project_id, this.doc._id)
  230. })
  231. it('should successully archive the doc', function () {
  232. this.res.status.should.equal(204)
  233. })
  234. it('should set inS3 and unset lines and ranges in the doc', async function () {
  235. const doc = await db.docs.findOne({ _id: this.doc._id })
  236. expect(doc.lines).not.to.exist
  237. expect(doc.ranges).not.to.exist
  238. doc.inS3.should.equal(true)
  239. })
  240. it('should set the doc in s3 correctly', async function () {
  241. const s3Doc = await DocstoreClient.getS3Doc(this.project_id, this.doc._id)
  242. s3Doc.lines.should.deep.equal(this.doc.lines)
  243. s3Doc.ranges.should.deep.equal(this.doc.ranges)
  244. })
  245. })
  246. describe('a doc with large lines', function () {
  247. before(async function () {
  248. this.project_id = new ObjectId()
  249. this.timeout(1000 * 30)
  250. const quarterMegInBytes = 250000
  251. const bigLine = Crypto.randomBytes(quarterMegInBytes).toString('hex')
  252. this.doc = {
  253. _id: new ObjectId(),
  254. lines: [bigLine, bigLine, bigLine, bigLine],
  255. ranges: {},
  256. version: 2,
  257. }
  258. await DocstoreClient.createDoc(
  259. this.project_id,
  260. this.doc._id,
  261. this.doc.lines,
  262. this.doc.version,
  263. this.doc.ranges
  264. )
  265. this.res = await DocstoreClient.archiveAllDoc(this.project_id)
  266. })
  267. it('should successully archive the docs', function () {
  268. this.res.status.should.equal(204)
  269. })
  270. it('should set inS3 and unset lines and ranges in each doc', async function () {
  271. const doc = await db.docs.findOne({ _id: this.doc._id })
  272. expect(doc.lines).not.to.exist
  273. expect(doc.ranges).not.to.exist
  274. doc.inS3.should.equal(true)
  275. })
  276. it('should set the doc in s3 correctly', async function () {
  277. const s3Doc = await DocstoreClient.getS3Doc(this.project_id, this.doc._id)
  278. s3Doc.lines.should.deep.equal(this.doc.lines)
  279. s3Doc.ranges.should.deep.equal(this.doc.ranges)
  280. })
  281. describe('after unarchiving from a request for the project', function () {
  282. before(async function () {
  283. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  284. })
  285. it('should restore the doc to mongo', async function () {
  286. const doc = await db.docs.findOne({ _id: this.doc._id })
  287. doc.lines.should.deep.equal(this.doc.lines)
  288. doc.ranges.should.deep.equal(this.doc.ranges)
  289. expect(doc.inS3).not.to.exist
  290. })
  291. })
  292. })
  293. describe('a doc with naughty strings', function () {
  294. before(async function () {
  295. this.project_id = new ObjectId()
  296. this.doc = {
  297. _id: new ObjectId(),
  298. lines: [
  299. '',
  300. 'undefined',
  301. 'undef',
  302. 'null',
  303. 'NULL',
  304. '(null)',
  305. 'nil',
  306. 'NIL',
  307. 'true',
  308. 'false',
  309. 'True',
  310. 'False',
  311. 'None',
  312. '\\',
  313. '\\\\',
  314. '0',
  315. '1',
  316. '1.00',
  317. '$1.00',
  318. '1/2',
  319. '1E2',
  320. '1E02',
  321. '1E+02',
  322. '-1',
  323. '-1.00',
  324. '-$1.00',
  325. '-1/2',
  326. '-1E2',
  327. '-1E02',
  328. '-1E+02',
  329. '1/0',
  330. '0/0',
  331. '-2147483648/-1',
  332. '-9223372036854775808/-1',
  333. '0.00',
  334. '0..0',
  335. '.',
  336. '0.0.0',
  337. '0,00',
  338. '0,,0',
  339. ',',
  340. '0,0,0',
  341. '0.0/0',
  342. '1.0/0.0',
  343. '0.0/0.0',
  344. '1,0/0,0',
  345. '0,0/0,0',
  346. '--1',
  347. '-',
  348. '-.',
  349. '-,',
  350. '999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999',
  351. 'NaN',
  352. 'Infinity',
  353. '-Infinity',
  354. '0x0',
  355. '0xffffffff',
  356. '0xffffffffffffffff',
  357. '0xabad1dea',
  358. '123456789012345678901234567890123456789',
  359. '1,000.00',
  360. '1 000.00',
  361. "1'000.00",
  362. '1,000,000.00',
  363. '1 000 000.00',
  364. "1'000'000.00",
  365. '1.000,00',
  366. '1 000,00',
  367. "1'000,00",
  368. '1.000.000,00',
  369. '1 000i̳̞v̢͇ḙ͎͟-҉̭̩̼͔m̤̭̫i͕͇̝̦n̗͙ḍ̟ ̯̲͕͞ǫ̟̯̰̲͙̻̝f ̪̰̰̗̖̭̘͘c̦͍̲̞͍̩̙ḥ͚a̮͎̟̙͜ơ̩̹͎s̤.̝̝ ҉Z̡̖̜͖̰̣͉̜a͖̰͙̬͡l̲̫̳͍̩g̡̟̼̱͚̞̬ͅo̗͜.̟',
  370. '̦H̬̤̗̤͝e͜ ̜̥̝̻͍̟́w̕h̖̯͓o̝͙̖͎̱̮ ҉̺̙̞̟͈W̷̼̭a̺̪͍į͈͕̭͙̯̜t̶̼̮s̘͙͖̕ ̠̫̠B̻͍͙͉̳ͅe̵h̵̬͇̫͙i̹͓̳̳̮͎̫̕n͟d̴̪̜̖ ̰͉̩͇͙̲͞ͅT͖̼͓̪͢h͏͓̮̻e̬̝̟ͅ ̤̹̝W͙̞̝͔͇͝ͅa͏͓͔̹̼̣l̴͔̰̤̟͔ḽ̫.͕',
  371. 'Z̮̞̠͙͔ͅḀ̗̞͈̻̗Ḷ͙͎̯̹̞͓G̻O̭̗̮',
  372. "˙ɐnbᴉlɐ ɐuƃɐɯ ǝɹolop ʇǝ ǝɹoqɐl ʇn ʇunpᴉpᴉɔuᴉ ɹodɯǝʇ poɯsnᴉǝ op pǝs 'ʇᴉlǝ ƃuᴉɔsᴉdᴉpɐ ɹnʇǝʇɔǝsuoɔ 'ʇǝɯɐ ʇᴉs ɹolop ɯnsdᴉ ɯǝɹo˥",
  373. '00˙Ɩ$-',
  374. 'The quick brown fox jumps over the lazy dog',
  375. '𝐓𝐡𝐞 𝐪𝐮𝐢𝐜𝐤 𝐛𝐫𝐨𝐰𝐧 𝐟𝐨𝐱 𝐣𝐮𝐦𝐩𝐬 𝐨𝐯𝐞𝐫 𝐭𝐡𝐞 𝐥𝐚𝐳𝐲 𝐝𝐨𝐠',
  376. '𝕿𝖍𝖊 𝖖𝖚𝖎𝖈𝖐 𝖇𝖗𝖔𝖜𝖓 𝖋𝖔𝖝 𝖏𝖚𝖒𝖕𝖘 𝖔𝖛𝖊𝖗 𝖙𝖍𝖊 𝖑𝖆𝖟𝖞 𝖉𝖔𝖌',
  377. '𝑻𝒉𝒆 𝒒𝒖𝒊𝒄𝒌 𝒃𝒓𝒐𝒘𝒏 𝒇𝒐𝒙 𝒋𝒖𝒎𝒑𝒔 𝒐𝒗𝒆𝒓 𝒕𝒉𝒆 𝒍𝒂𝒛𝒚 𝒅𝒐𝒈',
  378. '𝓣𝓱𝓮 𝓺𝓾𝓲𝓬𝓴 𝓫𝓻𝓸𝔀𝓷 𝓯𝓸𝔁 𝓳𝓾𝓶𝓹𝓼 𝓸𝓿𝓮𝓻 𝓽𝓱𝓮 𝓵𝓪𝔃𝔂 𝓭𝓸𝓰',
  379. '𝕋𝕙𝕖 𝕢𝕦𝕚𝕔𝕜 𝕓𝕣𝕠𝕨𝕟 𝕗𝕠𝕩 𝕛𝕦𝕞𝕡𝕤 𝕠𝕧𝕖𝕣 𝕥𝕙𝕖 𝕝𝕒𝕫𝕪 𝕕𝕠𝕘',
  380. '𝚃𝚑𝚎 𝚚𝚞𝚒𝚌𝚔 𝚋𝚛𝚘𝚠𝚗 𝚏𝚘𝚡 𝚓𝚞𝚖𝚙𝚜 𝚘𝚟𝚎𝚛 𝚝𝚑𝚎 𝚕𝚊𝚣𝚢 𝚍𝚘𝚐',
  381. '⒯⒣⒠ ⒬⒰⒤⒞⒦ ⒝⒭⒪⒲⒩ ⒡⒪⒳ ⒥⒰⒨⒫⒮ ⒪⒱⒠⒭ ⒯⒣⒠ ⒧⒜⒵⒴ ⒟⒪⒢',
  382. '<script>alert(123)</script>',
  383. '&lt;script&gt;alert(&#39;123&#39;);&lt;/script&gt;',
  384. '<img src=x onerror=alert(123) />',
  385. '<svg><script>123<1>alert(123)</script> ',
  386. '"><script>alert(123)</script>',
  387. "'><script>alert(123)</script>",
  388. '><script>alert(123)</script>',
  389. '</script><script>alert(123)</script>',
  390. '< / script >< script >alert(123)< / script >',
  391. ' onfocus=JaVaSCript:alert(123) autofocus ',
  392. '" onfocus=JaVaSCript:alert(123) autofocus ',
  393. "' onfocus=JaVaSCript:alert(123) autofocus ",
  394. '<script>alert(123)</script>',
  395. '<sc<script>ript>alert(123)</sc</script>ript>',
  396. '--><script>alert(123)</script>',
  397. '";alert(123);t="',
  398. "';alert(123);t='",
  399. 'JavaSCript:alert(123)',
  400. ';alert(123);',
  401. 'src=JaVaSCript:prompt(132)',
  402. '"><script>alert(123);</script x="',
  403. "'><script>alert(123);</script x='",
  404. '><script>alert(123);</script x=',
  405. '" autofocus onkeyup="javascript:alert(123)',
  406. "' autofocus onkeyup='javascript:alert(123)",
  407. '<script\\x20type="text/javascript">javascript:alert(1);</script>',
  408. '<script\\x3Etype="text/javascript">javascript:alert(1);</script>',
  409. '<script\\x0Dtype="text/javascript">javascript:alert(1);</script>',
  410. '<script\\x09type="text/javascript">javascript:alert(1);</script>',
  411. '<script\\x0Ctype="text/javascript">javascript:alert(1);</script>',
  412. '<script\\x2Ftype="text/javascript">javascript:alert(1);</script>',
  413. '<script\\x0Atype="text/javascript">javascript:alert(1);</script>',
  414. '\'`"><\\x3Cscript>javascript:alert(1)</script> ',
  415. '\'`"><\\x00script>javascript:alert(1)</script>',
  416. 'ABC<div style="x\\x3Aexpression(javascript:alert(1)">DEF',
  417. 'ABC<div style="x:expression\\x5C(javascript:alert(1)">DEF',
  418. 'ABC<div style="x:expression\\x00(javascript:alert(1)">DEF',
  419. 'ABC<div style="x:exp\\x00ression(javascript:alert(1)">DEF',
  420. 'ABC<div style="x:exp\\x5Cression(javascript:alert(1)">DEF',
  421. 'ABC<div style="x:\\x0Aexpression(javascript:alert(1)">DEF',
  422. 'ABC<div style="x:\\x09expression(javascript:alert(1)">DEF',
  423. 'ABC<div style="x:\\xE3\\x80\\x80expression(javascript:alert(1)">DEF',
  424. 'ABC<div style="x:\\xE2\\x80\\x84expression(javascript:alert(1)">DEF',
  425. 'ABC<div style="x:\\xC2\\xA0expression(javascript:alert(1)">DEF',
  426. 'ABC<div style="x:\\xE2\\x80\\x80expression(javascript:alert(1)">DEF',
  427. 'ABC<div style="x:\\xE2\\x80\\x8Aexpression(javascript:alert(1)">DEF',
  428. 'ABC<div style="x:\\x0Dexpression(javascript:alert(1)">DEF',
  429. 'ABC<div style="x:\\x0Cexpression(javascript:alert(1)">DEF',
  430. 'ABC<div style="x:\\xE2\\x80\\x87expression(javascript:alert(1)">DEF',
  431. 'ABC<div style="x:\\xEF\\xBB\\xBFexpression(javascript:alert(1)">DEF',
  432. 'ABC<div style="x:\\x20expression(javascript:alert(1)">DEF',
  433. 'ABC<div style="x:\\xE2\\x80\\x88expression(javascript:alert(1)">DEF',
  434. 'ABC<div style="x:\\x00expression(javascript:alert(1)">DEF',
  435. 'ABC<div style="x:\\xE2\\x80\\x8Bexpression(javascript:alert(1)">DEF',
  436. 'ABC<div style="x:\\xE2\\x80\\x86expression(javascript:alert(1)">DEF',
  437. 'ABC<div style="x:\\xE2\\x80\\x85expression(javascript:alert(1)">DEF',
  438. 'ABC<div style="x:\\xE2\\x80\\x82expression(javascript:alert(1)">DEF',
  439. 'ABC<div style="x:\\x0Bexpression(javascript:alert(1)">DEF',
  440. 'ABC<div style="x:\\xE2\\x80\\x81expression(javascript:alert(1)">DEF',
  441. 'ABC<div style="x:\\xE2\\x80\\x83expression(javascript:alert(1)">DEF',
  442. 'ABC<div style="x:\\xE2\\x80\\x89expression(javascript:alert(1)">DEF',
  443. '<a href="\\x0Bjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  444. '<a href="\\x0Fjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  445. '<a href="\\xC2\\xA0javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  446. '<a href="\\x05javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  447. '<a href="\\xE1\\xA0\\x8Ejavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  448. '<a href="\\x18javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  449. '<a href="\\x11javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  450. '<a href="\\xE2\\x80\\x88javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  451. '<a href="\\xE2\\x80\\x89javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  452. '<a href="\\xE2\\x80\\x80javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  453. '<a href="\\x17javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  454. '<a href="\\x03javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  455. '<a href="\\x0Ejavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  456. '<a href="\\x1Ajavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  457. '<a href="\\x00javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  458. '<a href="\\x10javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  459. '<a href="\\xE2\\x80\\x82javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  460. '<a href="\\x20javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  461. '<a href="\\x13javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  462. '<a href="\\x09javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  463. '<a href="\\xE2\\x80\\x8Ajavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  464. '<a href="\\x14javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  465. '<a href="\\x19javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  466. '<a href="\\xE2\\x80\\xAFjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  467. '<a href="\\x1Fjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  468. '<a href="\\xE2\\x80\\x81javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  469. '<a href="\\x1Djavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  470. '<a href="\\xE2\\x80\\x87javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  471. '<a href="\\x07javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  472. '<a href="\\xE1\\x9A\\x80javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  473. '<a href="\\xE2\\x80\\x83javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  474. '<a href="\\x04javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  475. '<a href="\\x01javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  476. '<a href="\\x08javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  477. '<a href="\\xE2\\x80\\x84javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  478. '<a href="\\xE2\\x80\\x86javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  479. '<a href="\\xE3\\x80\\x80javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  480. '<a href="\\x12javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  481. '<a href="\\x0Djavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  482. '<a href="\\x0Ajavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  483. '<a href="\\x0Cjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  484. '<a href="\\x15javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  485. '<a href="\\xE2\\x80\\xA8javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  486. '<a href="\\x16javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  487. '<a href="\\x02javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  488. '<a href="\\x1Bjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  489. '<a href="\\x06javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  490. '<a href="\\xE2\\x80\\xA9javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  491. '<a href="\\xE2\\x80\\x85javascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  492. '<a href="\\x1Ejavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  493. '<a href="\\xE2\\x81\\x9Fjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  494. '<a href="\\x1Cjavascript:javascript:alert(1)" id="fuzzelement1">test</a>',
  495. '<a href="javascript\\x00:javascript:alert(1)" id="fuzzelement1">test</a>',
  496. '<a href="javascript\\x3A:javascript:alert(1)" id="fuzzelement1">test</a>',
  497. '<a href="javascript\\x09:javascript:alert(1)" id="fuzzelement1">test</a>',
  498. '<a href="javascript\\x0D:javascript:alert(1)" id="fuzzelement1">test</a>',
  499. '<a href="javascript\\x0A:javascript:alert(1)" id="fuzzelement1">test</a>',
  500. '`"\'><img src=xxx:x \\x0Aonerror=javascript:alert(1)>',
  501. '`"\'><img src=xxx:x \\x22onerror=javascript:alert(1)>',
  502. '`"\'><img src=xxx:x \\x0Bonerror=javascript:alert(1)>',
  503. '`"\'><img src=xxx:x \\x0Donerror=javascript:alert(1)>',
  504. '`"\'><img src=xxx:x \\x2Fonerror=javascript:alert(1)>',
  505. '`"\'><img src=xxx:x \\x09onerror=javascript:alert(1)>',
  506. '`"\'><img src=xxx:x \\x0Conerror=javascript:alert(1)>',
  507. '`"\'><img src=xxx:x \\x00onerror=javascript:alert(1)>',
  508. '`"\'><img src=xxx:x \\x27onerror=javascript:alert(1)>',
  509. '`"\'><img src=xxx:x \\x20onerror=javascript:alert(1)>',
  510. '"`\'><script>\\x3Bjavascript:alert(1)</script>',
  511. '"`\'><script>\\x0Djavascript:alert(1)</script>',
  512. '"`\'><script>\\xEF\\xBB\\xBFjavascript:alert(1)</script>',
  513. '"`\'><script>\\xE2\\x80\\x81javascript:alert(1)</script>',
  514. '"`\'><script>\\xE2\\x80\\x84javascript:alert(1)</script>',
  515. '"`\'><script>\\xE3\\x80\\x80javascript:alert(1)</script>',
  516. '"`\'><script>\\x09javascript:alert(1)</script>',
  517. '"`\'><script>\\xE2\\x80\\x89javascript:alert(1)</script>',
  518. '"`\'><script>\\xE2\\x80\\x85javascript:alert(1)</script>',
  519. '"`\'><script>\\xE2\\x80\\x88javascript:alert(1)</script>',
  520. '"`\'><script>\\x00javascript:alert(1)</script>',
  521. '"`\'><script>\\xE2\\x80\\xA8javascript:alert(1)</script>',
  522. '"`\'><script>\\xE2\\x80\\x8Ajavascript:alert(1)</script>',
  523. '"`\'><script>\\xE1\\x9A\\x80javascript:alert(1)</script>',
  524. '"`\'><script>\\x0Cjavascript:alert(1)</script>',
  525. '"`\'><script>\\x2Bjavascript:alert(1)</script>',
  526. '"`\'><script>\\xF0\\x90\\x96\\x9Ajavascript:alert(1)</script>',
  527. '"`\'><script>-javascript:alert(1)</script>',
  528. '"`\'><script>\\x0Ajavascript:alert(1)</script>',
  529. '"`\'><script>\\xE2\\x80\\xAFjavascript:alert(1)</script>',
  530. '"`\'><script>\\x7Ejavascript:alert(1)</script>',
  531. '"`\'><script>\\xE2\\x80\\x87javascript:alert(1)</script>',
  532. '"`\'><script>\\xE2\\x81\\x9Fjavascript:alert(1)</script>',
  533. '"`\'><script>\\xE2\\x80\\xA9javascript:alert(1)</script>',
  534. '"`\'><script>\\xC2\\x85javascript:alert(1)</script>',
  535. '"`\'><script>\\xEF\\xBF\\xAEjavascript:alert(1)</script>',
  536. '"`\'><script>\\xE2\\x80\\x83javascript:alert(1)</script>',
  537. '"`\'><script>\\xE2\\x80\\x8Bjavascript:alert(1)</script>',
  538. '"`\'><script>\\xEF\\xBF\\xBEjavascript:alert(1)</script>',
  539. '"`\'><script>\\xE2\\x80\\x80javascript:alert(1)</script>',
  540. '"`\'><script>\\x21javascript:alert(1)</script>',
  541. '"`\'><script>\\xE2\\x80\\x82javascript:alert(1)</script>',
  542. '"`\'><script>\\xE2\\x80\\x86javascript:alert(1)</script>',
  543. '"`\'><script>\\xE1\\xA0\\x8Ejavascript:alert(1)</script>',
  544. '"`\'><script>\\x0Bjavascript:alert(1)</script>',
  545. '"`\'><script>\\x20javascript:alert(1)</script>',
  546. '"`\'><script>\\xC2\\xA0javascript:alert(1)</script>',
  547. '<img \\x00src=x onerror="alert(1)">',
  548. '<img \\x47src=x onerror="javascript:alert(1)">',
  549. '<img \\x11src=x onerror="javascript:alert(1)">',
  550. '<img \\x12src=x onerror="javascript:alert(1)">',
  551. '<img\\x47src=x onerror="javascript:alert(1)">',
  552. '<img\\x10src=x onerror="javascript:alert(1)">',
  553. '<img\\x13src=x onerror="javascript:alert(1)">',
  554. '<img\\x32src=x onerror="javascript:alert(1)">',
  555. '<img\\x47src=x onerror="javascript:alert(1)">',
  556. '<img\\x11src=x onerror="javascript:alert(1)">',
  557. '<img \\x47src=x onerror="javascript:alert(1)">',
  558. '<img \\x34src=x onerror="javascript:alert(1)">',
  559. '<img \\x39src=x onerror="javascript:alert(1)">',
  560. '<img \\x00src=x onerror="javascript:alert(1)">',
  561. '<img src\\x09=x onerror="javascript:alert(1)">',
  562. '<img src\\x10=x onerror="javascript:alert(1)">',
  563. '<img src\\x13=x onerror="javascript:alert(1)">',
  564. '<img src\\x32=x onerror="javascript:alert(1)">',
  565. '<img src\\x12=x onerror="javascript:alert(1)">',
  566. '<img src\\x11=x onerror="javascript:alert(1)">',
  567. '<img src\\x00=x onerror="javascript:alert(1)">',
  568. '<img src\\x47=x onerror="javascript:alert(1)">',
  569. '<img src=x\\x09onerror="javascript:alert(1)">',
  570. '<img src=x\\x10onerror="javascript:alert(1)">',
  571. '<img src=x\\x11onerror="javascript:alert(1)">',
  572. '<img src=x\\x12onerror="javascript:alert(1)">',
  573. '<img src=x\\x13onerror="javascript:alert(1)">',
  574. '<img[a][b][c]src[d]=x[e]onerror=[f]"alert(1)">',
  575. '<img src=x onerror=\\x09"javascript:alert(1)">',
  576. '<img src=x onerror=\\x10"javascript:alert(1)">',
  577. '<img src=x onerror=\\x11"javascript:alert(1)">',
  578. '<img src=x onerror=\\x12"javascript:alert(1)">',
  579. '<img src=x onerror=\\x32"javascript:alert(1)">',
  580. '<img src=x onerror=\\x00"javascript:alert(1)">',
  581. '<a href=java&#1&#2&#3&#4&#5&#6&#7&#8&#11&#12script:javascript:alert(1)>XXX</a>',
  582. '<img src="x` `<script>javascript:alert(1)</script>"` `>',
  583. '<img src onerror /" \'"= alt=javascript:alert(1)//">',
  584. '<title onpropertychange=javascript:alert(1)></title><title title=>',
  585. '<a href=http://foo.bar/#x=`y></a><img alt="`><img src=x:x onerror=javascript:alert(1)></a>">',
  586. '<!--[if]><script>javascript:alert(1)</script -->',
  587. '<!--[if<img src=x onerror=javascript:alert(1)//]> -->',
  588. '<script src="/\\%(jscript)s"></script>',
  589. '<script src="\\\\%(jscript)s"></script>',
  590. '<IMG """><SCRIPT>alert("XSS")</SCRIPT>">',
  591. '<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>',
  592. '<IMG SRC=# onmouseover="alert(\'xxs\')">',
  593. '<IMG SRC= onmouseover="alert(\'xxs\')">',
  594. '<IMG onmouseover="alert(\'xxs\')">',
  595. '<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>',
  596. '<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>',
  597. '<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>',
  598. '<IMG SRC="jav ascript:alert(\'XSS\');">',
  599. '<IMG SRC="jav&#x09;ascript:alert(\'XSS\');">',
  600. '<IMG SRC="jav&#x0A;ascript:alert(\'XSS\');">',
  601. '<IMG SRC="jav&#x0D;ascript:alert(\'XSS\');">',
  602. 'perl -e \'print "<IMG SRC=java\\0script:alert(\\"XSS\\")>";\' > out',
  603. '<IMG SRC=" &#14; javascript:alert(\'XSS\');">',
  604. '<SCRIPT/XSS SRC="http://ha.ckers.org/xss.js"></SCRIPT>',
  605. '<BODY onload!#$%&()*~+-_.,:;?@[/|\\]^`=alert("XSS")>',
  606. '<SCRIPT/SRC="http://ha.ckers.org/xss.js"></SCRIPT>',
  607. '<<SCRIPT>alert("XSS");//<</SCRIPT>',
  608. '<SCRIPT SRC=http://ha.ckers.org/xss.js?< B >',
  609. '<SCRIPT SRC=//ha.ckers.org/.j>',
  610. '<IMG SRC="javascript:alert(\'XSS\')"',
  611. '<iframe src=http://ha.ckers.org/scriptlet.html <',
  612. "\\\";alert('XSS');//",
  613. '<plaintext>',
  614. '1;DROP TABLE users',
  615. "1'; DROP TABLE users-- 1",
  616. "' OR 1=1 -- 1",
  617. "' OR '1'='1",
  618. '-',
  619. '--',
  620. '--version',
  621. '--help',
  622. '$USER',
  623. '/dev/null; touch /tmp/blns.fail ; echo',
  624. '`touch /tmp/blns.fail`',
  625. '$(touch /tmp/blns.fail)',
  626. '@{[system "touch /tmp/blns.fail"]}',
  627. 'eval("puts \'hello world\'")',
  628. 'System("ls -al /")',
  629. '`ls -al /`',
  630. 'Kernel.exec("ls -al /")',
  631. 'Kernel.exit(1)',
  632. "%x('ls -al /')",
  633. '<?xml version="1.0" encoding="ISO-8859-1"?><!DOCTYPE foo [ <!ELEMENT foo ANY ><!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>',
  634. '$HOME',
  635. "$ENV{'HOME'}",
  636. '%d',
  637. '%s',
  638. '%*.*s',
  639. '../../../../../../../../../../../etc/passwd%00',
  640. '../../../../../../../../../../../etc/hosts',
  641. '() { 0; }; touch /tmp/blns.shellshock1.fail;',
  642. '() { _; } >_[$($())] { touch /tmp/blns.shellshock2.fail; }',
  643. 'CON',
  644. 'PRN',
  645. 'AUX',
  646. 'CLOCK$',
  647. 'NUL',
  648. 'A:',
  649. 'ZZ:',
  650. 'COM1',
  651. 'LPT1',
  652. 'LPT2',
  653. 'LPT3',
  654. 'COM2',
  655. 'COM3',
  656. 'COM4',
  657. 'Scunthorpe General Hospital',
  658. 'Penistone Community Church',
  659. 'Lightwater Country Park',
  660. 'Jimmy Clitheroe',
  661. 'Horniman Museum',
  662. 'shitake mushrooms',
  663. 'RomansInSussex.co.uk',
  664. 'http://www.cum.qc.ca/',
  665. 'Craig Cockburn, Software Specialist',
  666. 'Linda Callahan',
  667. 'Dr. Herman I. Libshitz',
  668. 'magna cum laude',
  669. 'Super Bowl XXX',
  670. 'medieval erection of parapets',
  671. 'evaluate',
  672. 'mocha',
  673. 'expression',
  674. 'Arsenal canal',
  675. 'classic',
  676. 'Tyson Gay',
  677. "If you're reading this, you've been in a coma for almost 20 years now. We're trying a new technique. We don't know where this message will end up in your dream, but we hope it works. Please wake up, we miss you.",
  678. 'Roses are \u001b[0;31mred\u001b[0m, violets are \u001b[0;34mblue. Hope you enjoy terminal hue',
  679. 'But now...\u001b[20Cfor my greatest trick...\u001b[8m',
  680. 'The quic\b\b\b\b\b\bk brown fo\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007\u0007x... [Beeeep]',
  681. 'Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗',
  682. ],
  683. ranges: {},
  684. version: 2,
  685. }
  686. await DocstoreClient.createDoc(
  687. this.project_id,
  688. this.doc._id,
  689. this.doc.lines,
  690. this.doc.version,
  691. this.doc.ranges
  692. )
  693. this.res = await DocstoreClient.archiveAllDoc(this.project_id)
  694. })
  695. it('should successully archive the docs', function () {
  696. this.res.status.should.equal(204)
  697. })
  698. it('should set inS3 and unset lines and ranges in each doc', async function () {
  699. const doc = await db.docs.findOne({ _id: this.doc._id })
  700. expect(doc.lines).not.to.exist
  701. expect(doc.ranges).not.to.exist
  702. doc.inS3.should.equal(true)
  703. })
  704. it('should set the doc in s3 correctly', async function () {
  705. const s3Doc = await DocstoreClient.getS3Doc(this.project_id, this.doc._id)
  706. s3Doc.lines.should.deep.equal(this.doc.lines)
  707. s3Doc.ranges.should.deep.equal(this.doc.ranges)
  708. })
  709. describe('after unarchiving from a request for the project', function () {
  710. before(async function () {
  711. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  712. })
  713. it('should restore the doc to mongo', async function () {
  714. const doc = await db.docs.findOne({ _id: this.doc._id })
  715. doc.lines.should.deep.equal(this.doc.lines)
  716. doc.ranges.should.deep.equal(this.doc.ranges)
  717. })
  718. })
  719. })
  720. describe('a doc with ranges', function () {
  721. before(async function () {
  722. this.project_id = new ObjectId()
  723. this.doc = {
  724. _id: new ObjectId(),
  725. lines: ['one', 'two', 'three'],
  726. ranges: {
  727. changes: [
  728. {
  729. id: new ObjectId(),
  730. op: { i: 'foo', p: 24 },
  731. metadata: {
  732. user_id: new ObjectId(),
  733. ts: new Date('2017-01-27T16:10:44.194Z'),
  734. },
  735. },
  736. {
  737. id: new ObjectId(),
  738. op: { d: 'bar', p: 50 },
  739. metadata: {
  740. user_id: new ObjectId(),
  741. ts: new Date('2017-01-27T18:10:44.194Z'),
  742. },
  743. },
  744. ],
  745. comments: [
  746. {
  747. id: new ObjectId(),
  748. op: { c: 'comment', p: 284, t: new ObjectId() },
  749. metadata: {
  750. user_id: new ObjectId(),
  751. ts: new Date('2017-01-26T14:22:04.869Z'),
  752. },
  753. },
  754. ],
  755. },
  756. version: 2,
  757. }
  758. this.fixedRanges = {
  759. ...this.doc.ranges,
  760. comments: [
  761. {
  762. ...this.doc.ranges.comments[0],
  763. id: this.doc.ranges.comments[0].op.t,
  764. },
  765. ],
  766. }
  767. await DocstoreClient.createDoc(
  768. this.project_id,
  769. this.doc._id,
  770. this.doc.lines,
  771. this.doc.version,
  772. this.doc.ranges
  773. )
  774. this.res = await DocstoreClient.archiveAllDoc(this.project_id)
  775. })
  776. it('should successully archive the docs', function () {
  777. this.res.status.should.equal(204)
  778. })
  779. it('should set inS3 and unset lines and ranges in each doc', async function () {
  780. const doc = await db.docs.findOne({ _id: this.doc._id })
  781. expect(doc.lines).not.to.exist
  782. expect(doc.ranges).not.to.exist
  783. doc.inS3.should.equal(true)
  784. })
  785. it('should set the doc in s3 correctly', async function () {
  786. const s3Doc = await DocstoreClient.getS3Doc(this.project_id, this.doc._id)
  787. s3Doc.lines.should.deep.equal(this.doc.lines)
  788. const ranges = JSON.parse(JSON.stringify(this.fixedRanges)) // ObjectId -> String
  789. s3Doc.ranges.should.deep.equal(ranges)
  790. })
  791. describe('after unarchiving from a request for the project', function () {
  792. before(async function () {
  793. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  794. })
  795. it('should restore the doc to mongo', async function () {
  796. const doc = await db.docs.findOne({ _id: this.doc._id })
  797. doc.lines.should.deep.equal(this.doc.lines)
  798. doc.ranges.should.deep.equal(this.fixedRanges)
  799. expect(doc.inS3).not.to.exist
  800. })
  801. })
  802. })
  803. describe('a doc that is archived twice', function () {
  804. before(async function () {
  805. this.project_id = new ObjectId()
  806. this.doc = {
  807. _id: new ObjectId(),
  808. lines: ['abc', 'def', 'ghi'],
  809. ranges: {},
  810. version: 2,
  811. }
  812. await DocstoreClient.createDoc(
  813. this.project_id,
  814. this.doc._id,
  815. this.doc.lines,
  816. this.doc.version,
  817. this.doc.ranges
  818. )
  819. this.res = await DocstoreClient.archiveAllDoc(this.project_id)
  820. this.res.status.should.equal(204)
  821. this.res = await DocstoreClient.archiveAllDoc(this.project_id)
  822. this.res.status.should.equal(204)
  823. })
  824. it('should set inS3 and unset lines and ranges in each doc', async function () {
  825. const doc = await db.docs.findOne({ _id: this.doc._id })
  826. expect(doc.lines).not.to.exist
  827. expect(doc.ranges).not.to.exist
  828. doc.inS3.should.equal(true)
  829. })
  830. it('should set the doc in s3 correctly', async function () {
  831. const s3Doc = await DocstoreClient.getS3Doc(this.project_id, this.doc._id)
  832. s3Doc.lines.should.deep.equal(this.doc.lines)
  833. s3Doc.ranges.should.deep.equal(this.doc.ranges)
  834. })
  835. describe('after unarchiving from a request for the project', function () {
  836. before(async function () {
  837. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  838. })
  839. it('should restore the doc to mongo', async function () {
  840. const doc = await db.docs.findOne({ _id: this.doc._id })
  841. doc.lines.should.deep.equal(this.doc.lines)
  842. doc.ranges.should.deep.equal(this.doc.ranges)
  843. expect(doc.inS3).not.to.exist
  844. })
  845. })
  846. })
  847. describe('a doc with the old schema (just an array of lines)', function () {
  848. before(async function () {
  849. this.project_id = new ObjectId()
  850. this.doc = {
  851. _id: new ObjectId(),
  852. lines: ['abc', 'def', 'ghi'],
  853. ranges: {},
  854. version: 2,
  855. }
  856. await uploadContent(`${this.project_id}/${this.doc._id}`, this.doc.lines)
  857. await db.docs.insertOne({
  858. project_id: this.project_id,
  859. _id: this.doc._id,
  860. rev: this.doc.version,
  861. inS3: true,
  862. })
  863. this.fetched_docs = await DocstoreClient.getAllDocs(this.project_id)
  864. })
  865. it('should restore the doc to mongo', async function () {
  866. const doc = await db.docs.findOne({ _id: this.doc._id })
  867. doc.lines.should.deep.equal(this.doc.lines)
  868. expect(doc.inS3).not.to.exist
  869. })
  870. it('should return the doc', function () {
  871. this.fetched_docs[0].lines.should.deep.equal(this.doc.lines)
  872. })
  873. })
  874. })