private.spec.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. ///<reference path="server/index.d.ts"/>
  2. import { expect, test } from "@playwright/test";
  3. const url = "http://localhost:8085";
  4. test.beforeEach(async ({ page }) => {
  5. await page.goto(url);
  6. await page.waitForFunction(() => window.setup != null);
  7. });
  8. test.describe("PrivateDirectory", () => {
  9. test("lookupNode can fetch file added to directory", async ({ page }) => {
  10. const result = await page.evaluate(async () => {
  11. const {
  12. wnfs: { PrivateDirectory, PrivateForest },
  13. mock: { MemoryBlockStore, Rng },
  14. } = await window.setup();
  15. const rng = new Rng();
  16. const store = new MemoryBlockStore();
  17. const initialForest = new PrivateForest(rng);
  18. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  19. var { rootDir, forest } = await root.write(
  20. ["text.txt"],
  21. true,
  22. new Uint8Array([1, 2, 3, 4, 5]),
  23. new Date(),
  24. initialForest,
  25. store,
  26. rng,
  27. );
  28. return await rootDir.lookupNode("text.txt", true, forest, store);
  29. });
  30. expect(result).toBeDefined();
  31. });
  32. test("getNode can fetch node from root dir", async ({ page }) => {
  33. const [result0, result1] = await page.evaluate(async (): Promise<any[]> => {
  34. const {
  35. wnfs: { PrivateDirectory, PrivateForest },
  36. mock: { MemoryBlockStore, Rng },
  37. } = await window.setup();
  38. const rng = new Rng();
  39. const store = new MemoryBlockStore();
  40. const initialForest = new PrivateForest(rng);
  41. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  42. var { rootDir, forest } = await root.mkdir(
  43. ["pictures", "dogs"],
  44. true,
  45. new Date(),
  46. initialForest,
  47. store,
  48. rng,
  49. );
  50. var { rootDir, forest } = await rootDir.write(
  51. ["pictures", "cats", "tabby.png"],
  52. true,
  53. new Uint8Array([1, 2, 3, 4, 5]),
  54. new Date(),
  55. forest,
  56. store,
  57. rng,
  58. );
  59. var result0 = await rootDir.getNode(["pictures", "cats", "tabby.png"], true, forest, store);
  60. var result1 = await rootDir.getNode(["pictures", "dogs", "bingo.png"], true, forest, store);
  61. console.log(result0);
  62. console.log(result1);
  63. return [result0, result1];
  64. });
  65. expect(result0).toBeDefined();
  66. expect(result1).toBeUndefined();
  67. });
  68. test("lookupNode cannot fetch file not added to directory", async ({ page }) => {
  69. const result = await page.evaluate(async () => {
  70. const {
  71. wnfs: { PrivateDirectory, PrivateForest },
  72. mock: { MemoryBlockStore, Rng },
  73. } = await window.setup();
  74. const rng = new Rng();
  75. const store = new MemoryBlockStore();
  76. const initialForest = new PrivateForest(rng);
  77. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  78. return await root.lookupNode("Unknown", true, initialForest, store);
  79. });
  80. expect(result).toBe(undefined);
  81. });
  82. test("mkdir can create new directory", async ({ page }) => {
  83. const result = await page.evaluate(async () => {
  84. const {
  85. wnfs: { PrivateDirectory, PrivateForest },
  86. mock: { MemoryBlockStore, Rng },
  87. } = await window.setup();
  88. const rng = new Rng();
  89. const store = new MemoryBlockStore();
  90. const initialForest = new PrivateForest(rng);
  91. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  92. var { rootDir, forest } = await root.mkdir(
  93. ["pictures", "cats"],
  94. true,
  95. new Date(),
  96. initialForest,
  97. store,
  98. rng,
  99. );
  100. var { rootDir, forest } = await rootDir.write(
  101. ["pictures", "cats", "tabby.png"],
  102. true,
  103. new Uint8Array([1, 2, 3, 4, 5]),
  104. new Date(),
  105. forest,
  106. store,
  107. rng,
  108. );
  109. var result = await rootDir.getNode(["pictures", "cats", "tabby.png"], true, forest, store);
  110. return result;
  111. });
  112. expect(result).toBeDefined();
  113. });
  114. test("ls can list children under directory", async ({ page }) => {
  115. const result = await page.evaluate(async () => {
  116. const {
  117. wnfs: { PrivateDirectory, PrivateForest },
  118. mock: { MemoryBlockStore, Rng },
  119. } = await window.setup();
  120. const rng = new Rng();
  121. const store = new MemoryBlockStore();
  122. const initialForest = new PrivateForest(rng);
  123. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  124. var { rootDir, forest } = await root.mkdir(
  125. ["pictures", "dogs"],
  126. true,
  127. new Date(),
  128. initialForest,
  129. store,
  130. rng,
  131. );
  132. var { rootDir, forest } = await rootDir.write(
  133. ["pictures", "cats", "tabby.png"],
  134. true,
  135. new Uint8Array([1, 2, 3, 4, 5]),
  136. new Date(),
  137. forest,
  138. store,
  139. rng,
  140. );
  141. var { result } = await rootDir.ls(["pictures"], true, forest, store);
  142. return result;
  143. });
  144. expect(result.length).toBe(2);
  145. expect(result[0].name).toBe("cats");
  146. expect(result[1].name).toBe("dogs");
  147. });
  148. test("rm can remove children from directory", async ({ page }) => {
  149. const result = await page.evaluate(async () => {
  150. const {
  151. wnfs: { PrivateDirectory, PrivateForest },
  152. mock: { MemoryBlockStore, Rng },
  153. } = await window.setup();
  154. const rng = new Rng();
  155. const store = new MemoryBlockStore();
  156. const initialForest = new PrivateForest(rng);
  157. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  158. var { rootDir, forest } = await root.write(
  159. ["pictures", "dogs", "billie.jpeg"],
  160. true,
  161. new Uint8Array([1, 2, 3, 4, 5]),
  162. new Date(),
  163. initialForest,
  164. store,
  165. rng,
  166. );
  167. var { rootDir, forest } = await rootDir.write(
  168. ["pictures", "cats", "tabby.png"],
  169. true,
  170. new Uint8Array([1, 2, 3, 4, 5]),
  171. new Date(),
  172. forest,
  173. store,
  174. rng,
  175. );
  176. var { rootDir, forest } = await rootDir.rm(["pictures", "cats"], true, forest, store, rng);
  177. var { result } = await rootDir.ls(["pictures"], true, forest, store);
  178. return result;
  179. });
  180. expect(result.length).toEqual(1);
  181. expect(result[0].name).toEqual("dogs");
  182. });
  183. test("basicMv can move content between directories", async ({ page }) => {
  184. const [imagesContent, picturesContent] = await page.evaluate(async () => {
  185. const {
  186. wnfs: { PrivateDirectory, PrivateForest },
  187. mock: { MemoryBlockStore, Rng },
  188. } = await window.setup();
  189. const rng = new Rng();
  190. const store = new MemoryBlockStore();
  191. const initialForest = new PrivateForest(rng);
  192. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  193. var { rootDir, forest } = await root.write(
  194. ["pictures", "cats", "luna.jpeg"],
  195. true,
  196. new Uint8Array([1, 2, 3, 4, 5]),
  197. new Date(),
  198. initialForest,
  199. store,
  200. rng,
  201. );
  202. var { rootDir, forest } = await rootDir.write(
  203. ["pictures", "cats", "tabby.png"],
  204. true,
  205. new Uint8Array([1, 2, 3, 4, 5]),
  206. new Date(),
  207. forest,
  208. store,
  209. rng,
  210. );
  211. var { rootDir, forest } = await rootDir.mkdir(
  212. ["images"],
  213. true,
  214. new Date(),
  215. forest,
  216. store,
  217. rng,
  218. );
  219. var { rootDir, forest } = await rootDir.basicMv(
  220. ["pictures", "cats"],
  221. ["images", "cats"],
  222. true,
  223. new Date(),
  224. forest,
  225. store,
  226. rng,
  227. );
  228. var { result: imagesContent, forest } = await rootDir.ls(["images"], true, forest, store);
  229. var { result: picturesContent, forest } = await rootDir.ls(["pictures"], true, forest, store);
  230. return [imagesContent, picturesContent];
  231. });
  232. expect(imagesContent.length).toEqual(1);
  233. expect(picturesContent.length).toEqual(0);
  234. expect(imagesContent[0].name).toEqual("cats");
  235. });
  236. test("cp can copy content between directories", async ({ page }) => {
  237. const [imagesContent, picturesContent] = await page.evaluate(async () => {
  238. const {
  239. wnfs: { PrivateDirectory, PrivateForest },
  240. mock: { MemoryBlockStore, Rng },
  241. } = await window.setup();
  242. const rng = new Rng();
  243. const store = new MemoryBlockStore();
  244. const initialForest = new PrivateForest(rng);
  245. const root = new PrivateDirectory(initialForest.emptyName(), new Date(), rng);
  246. var { rootDir, forest } = await root.write(
  247. ["pictures", "cats", "luna.jpeg"],
  248. true,
  249. new Uint8Array([1, 2, 3, 4, 5]),
  250. new Date(),
  251. initialForest,
  252. store,
  253. rng,
  254. );
  255. var { rootDir, forest } = await rootDir.write(
  256. ["pictures", "cats", "tabby.png"],
  257. true,
  258. new Uint8Array([1, 2, 3, 4, 5]),
  259. new Date(),
  260. forest,
  261. store,
  262. rng,
  263. );
  264. var { rootDir, forest } = await rootDir.mkdir(
  265. ["images"],
  266. true,
  267. new Date(),
  268. forest,
  269. store,
  270. rng,
  271. );
  272. var { rootDir, forest } = await rootDir.cp(
  273. ["pictures", "cats"],
  274. ["images", "cats"],
  275. true,
  276. new Date(),
  277. forest,
  278. store,
  279. rng,
  280. );
  281. var { result: imagesContent, forest } = await rootDir.ls(["images"], true, forest, store);
  282. var { result: picturesContent, forest } = await rootDir.ls(["pictures"], true, forest, store);
  283. return [imagesContent, picturesContent];
  284. });
  285. expect(imagesContent.length).toEqual(1);
  286. expect(picturesContent.length).toEqual(1);
  287. expect(imagesContent[0].name).toEqual("cats");
  288. expect(picturesContent[0].name).toEqual("cats");
  289. });
  290. });
  291. test.describe("PrivateFile", () => {
  292. test("empty can create empty file", async ({ page }) => {
  293. const result = await page.evaluate(async () => {
  294. const {
  295. wnfs: { PrivateFile, PrivateForest },
  296. mock: { Rng },
  297. } = await window.setup();
  298. const rng = new Rng();
  299. const forest = new PrivateForest(rng);
  300. const file = new PrivateFile(forest.emptyName(), new Date(), rng);
  301. return file.getId();
  302. });
  303. expect(result).toBeDefined();
  304. });
  305. test("withContent can create file with content", async ({ page }) => {
  306. const result = await page.evaluate(async () => {
  307. const {
  308. wnfs: { PrivateFile, PrivateForest },
  309. mock: { MemoryBlockStore, Rng },
  310. } = await window.setup();
  311. const rng = new Rng();
  312. const forest = new PrivateForest(rng);
  313. const store = new MemoryBlockStore();
  314. const [file] = await PrivateFile.withContent(
  315. forest.emptyName(),
  316. new Date(),
  317. new Uint8Array([1, 2, 3, 4, 5]),
  318. forest,
  319. store,
  320. rng,
  321. );
  322. return file.getId();
  323. });
  324. expect(result).toBeDefined();
  325. });
  326. test("getContent can fetch file's entire content", async ({ page }) => {
  327. const [length, type] = await page.evaluate(async () => {
  328. const {
  329. wnfs: { PrivateFile, PrivateForest },
  330. mock: { MemoryBlockStore, Rng },
  331. } = await window.setup();
  332. const rng = new Rng();
  333. const initialForest = new PrivateForest(rng);
  334. const store = new MemoryBlockStore();
  335. var [file, forest] = await PrivateFile.withContent(
  336. initialForest.emptyName(),
  337. new Date(),
  338. new Uint8Array([1, 2, 3, 4, 5]),
  339. initialForest,
  340. store,
  341. rng,
  342. );
  343. let content = await file.getContent(forest, store);
  344. return [content.length, content.constructor.name, content];
  345. });
  346. expect(length).toEqual(5);
  347. expect(type).toEqual("Uint8Array");
  348. });
  349. test("readAt can fetch file's partial content", async ({ page }) => {
  350. const [length, type, content] = await page.evaluate(async () => {
  351. const {
  352. wnfs: { PrivateFile, PrivateForest },
  353. mock: { MemoryBlockStore, Rng },
  354. } = await window.setup();
  355. const rng = new Rng();
  356. const initialForest = new PrivateForest(rng);
  357. const store = new MemoryBlockStore();
  358. var [file, forest] = await PrivateFile.withContent(
  359. initialForest.emptyName(),
  360. new Date(),
  361. new Uint8Array([1, 2, 3, 4, 5]),
  362. initialForest,
  363. store,
  364. rng,
  365. );
  366. let content = await file.readAt(1, 3, forest, store);
  367. return [content.length, content.constructor.name, content];
  368. });
  369. expect(length).toEqual(3);
  370. expect(type).toEqual("Uint8Array");
  371. expect(new Uint8Array(Object.values(content))).toEqual(new Uint8Array([2, 3, 4]));
  372. });
  373. test("getSize returns the exact content size", async ({ page }) => {
  374. const size = await page.evaluate(async () => {
  375. const {
  376. wnfs: { PrivateFile, PrivateForest },
  377. mock: { MemoryBlockStore, Rng },
  378. } = await window.setup();
  379. const rng = new Rng();
  380. const initialForest = new PrivateForest(rng);
  381. const store = new MemoryBlockStore();
  382. var [file, forest] = await PrivateFile.withContent(
  383. initialForest.emptyName(),
  384. new Date(),
  385. new Uint8Array(2 * 1024 * 1024),
  386. initialForest,
  387. store,
  388. rng,
  389. );
  390. return await file.getSize(forest, store);
  391. });
  392. expect(size).toEqual(2 * 1024 * 1024);
  393. });
  394. test("A PrivateDirectory has the correct metadata", async ({ page }) => {
  395. const result = await page.evaluate(async () => {
  396. const {
  397. wnfs: { PrivateDirectory, PrivateForest },
  398. mock: { Rng },
  399. } = await window.setup();
  400. const rng = new Rng();
  401. const forest = new PrivateForest(rng);
  402. const time = new Date();
  403. return new PrivateDirectory(forest.emptyName(), time, rng).metadata();
  404. });
  405. expect(result.created).not.toBeUndefined();
  406. });
  407. test("A PrivateFile has the correct metadata", async ({ page }) => {
  408. const result = await page.evaluate(async () => {
  409. const {
  410. wnfs: { PrivateFile, PrivateForest },
  411. mock: { Rng },
  412. } = await window.setup();
  413. const rng = new Rng();
  414. const forest = new PrivateForest(rng);
  415. const time = new Date();
  416. return new PrivateFile(forest.emptyName(), time, rng).metadata();
  417. });
  418. expect(result.created).not.toBeUndefined();
  419. });
  420. });
  421. test.describe("PrivateNode", () => {
  422. test("load returns what was stored", async ({ page }) => {
  423. const [metadataBefore, metadataAfter] = await page.evaluate(async () => {
  424. const {
  425. wnfs: { PrivateFile, PrivateNode, PrivateForest },
  426. mock: { MemoryBlockStore, Rng },
  427. } = await window.setup();
  428. const rng = new Rng();
  429. const store = new MemoryBlockStore();
  430. const forest = new PrivateForest(rng);
  431. const time = new Date();
  432. const file = new PrivateFile(forest.emptyName(), time, rng);
  433. const node = file.asNode();
  434. const [privateRef, newForest] = await node.store(forest, store, rng);
  435. const fetched = await PrivateNode.load(privateRef, newForest, store);
  436. const metadataBefore = node.asFile().metadata();
  437. const metadataAfter = fetched.asFile().metadata();
  438. return [metadataBefore, metadataAfter];
  439. });
  440. expect(metadataBefore).toBeDefined();
  441. expect(metadataAfter).toBeDefined();
  442. expect(metadataBefore.created).toEqual(metadataAfter.created);
  443. expect(metadataBefore.modified).toEqual(metadataAfter.modified);
  444. });
  445. test("searchLatest finds latest", async ({ page }) => {
  446. const [lsResultBefore, lsResultAfter] = await page.evaluate(async () => {
  447. const {
  448. wnfs: { PrivateDirectory, PrivateNode, PrivateForest },
  449. mock: { MemoryBlockStore, Rng },
  450. } = await window.setup();
  451. const rng = new Rng();
  452. const store = new MemoryBlockStore();
  453. const forest0 = new PrivateForest(rng);
  454. const time = new Date();
  455. // Create a root directory and store
  456. const rootDir0 = new PrivateDirectory(forest0.emptyName(), time, rng);
  457. const [accessKey, forest1] = await rootDir0.store(forest0, store, rng);
  458. // Write something to the directory and store it
  459. const { rootDir: rootDir1, forest: forest2 } = await rootDir0.write(
  460. ["some", "file.txt"],
  461. true,
  462. new Uint8Array([0]),
  463. time,
  464. forest1,
  465. store,
  466. rng,
  467. );
  468. const [_, forest3] = await rootDir1.asNode().store(forest2, store, rng);
  469. // loading back the *old* directory using its access key should give an empty directory:
  470. const oldNode = await PrivateNode.load(accessKey, forest3, store);
  471. const { result: lsResultBefore } = await oldNode.asDir().ls([], false, forest3, store);
  472. // loading back the directory with search latest should work:
  473. const latestNode = await oldNode.searchLatest(forest3, store);
  474. const { result: lsResultAfter } = await latestNode.asDir().ls([], false, forest3, store);
  475. return [lsResultBefore, lsResultAfter];
  476. });
  477. expect(lsResultBefore).toBeDefined();
  478. expect(lsResultAfter).toBeDefined();
  479. expect(lsResultBefore).toEqual([]);
  480. expect(lsResultAfter.length).toEqual(1);
  481. expect(lsResultAfter[0].name).toEqual("some");
  482. });
  483. });
  484. test.describe("PrivateForest", () => {
  485. test("store returns a PrivateRef", async ({ page }) => {
  486. const result = await page.evaluate(async () => {
  487. const {
  488. wnfs: { PrivateFile, PrivateForest },
  489. mock: { MemoryBlockStore, Rng, CID },
  490. } = await window.setup();
  491. const rng = new Rng();
  492. const store = new MemoryBlockStore();
  493. const forest = new PrivateForest(rng);
  494. const time = new Date();
  495. const file = new PrivateFile(forest.emptyName(), time, rng);
  496. const node = file.asNode();
  497. const [privateRef, _] = await node.store(forest, store, rng);
  498. return {
  499. // Need to be converted to arrays so they can be passed as JSON
  500. label: Array.from(privateRef.getLabel()),
  501. temporalKey: Array.from(privateRef.getTemporalKey()),
  502. contentCid: CID.decode(privateRef.getContentCid()).toString(),
  503. };
  504. });
  505. expect(result.label.length).toEqual(32);
  506. expect(result.temporalKey.length).toEqual(32);
  507. expect(result.contentCid).toBeDefined();
  508. });
  509. test("diff gets changes in forests", async ({ page }) => {
  510. const changes = await page.evaluate(async () => {
  511. const {
  512. wnfs: { PrivateFile, PrivateDirectory, PrivateForest },
  513. mock: { MemoryBlockStore, Rng },
  514. } = await window.setup();
  515. const rng = new Rng();
  516. const store = new MemoryBlockStore();
  517. const time = new Date();
  518. var mainForest: any = new PrivateForest(rng);
  519. var otherForest: any = mainForest;
  520. const file = new PrivateFile(mainForest.emptyName(), time, rng).asNode();
  521. const dir = new PrivateDirectory(mainForest.emptyName(), time, rng).asNode();
  522. var [_, mainForest] = await file.store(mainForest, store, rng);
  523. var [_, otherForest] = await dir.store(otherForest, store, rng);
  524. const diff = await mainForest.diff(otherForest, store);
  525. return diff.map((change: any) => change.getChangeType());
  526. });
  527. expect(changes.length).toEqual(2);
  528. expect(changes).toContain("add");
  529. expect(changes).toContain("remove");
  530. });
  531. test("merge combines changes in forests", async ({ page }) => {
  532. const result = await page.evaluate(async () => {
  533. const {
  534. wnfs: { PrivateFile, PrivateDirectory, PrivateForest, PrivateNode },
  535. mock: { MemoryBlockStore, Rng },
  536. } = await window.setup();
  537. const rng = new Rng();
  538. const store = new MemoryBlockStore();
  539. const time = new Date();
  540. var mainForest: any = new PrivateForest(rng);
  541. var otherForest: any = mainForest;
  542. const file = new PrivateFile(mainForest.emptyName(), time, rng).asNode();
  543. const dir = new PrivateDirectory(mainForest.emptyName(), time, rng).asNode();
  544. var [_, mainForest] = await file.store(mainForest, store, rng);
  545. var [privateRef, otherForest] = await dir.store(otherForest, store, rng);
  546. const mergeForest = await mainForest.merge(otherForest, store);
  547. return await PrivateNode.load(privateRef, mergeForest, store);
  548. });
  549. expect(result).toBeDefined();
  550. });
  551. });
  552. test.describe("AccessKey", () => {
  553. test("can encode / decode an access key", async ({ page }) => {
  554. const [metadataBefore, metadataAfter] = await page.evaluate(async () => {
  555. const {
  556. wnfs: { AccessKey, PrivateFile, PrivateNode, PrivateForest },
  557. mock: { MemoryBlockStore, Rng },
  558. } = await window.setup();
  559. const rng = new Rng();
  560. const store = new MemoryBlockStore();
  561. const forest = new PrivateForest(rng);
  562. const time = new Date();
  563. const file = new PrivateFile(forest.emptyName(), time, rng);
  564. const node = file.asNode();
  565. const [accessKey, newForest] = await node.store(forest, store, rng);
  566. const encodedAccessKey = accessKey.toBytes();
  567. const decodedAccessKey = AccessKey.fromBytes(encodedAccessKey);
  568. const fetched = await PrivateNode.load(decodedAccessKey, newForest, store);
  569. const metadataBefore = node.asFile().metadata();
  570. const metadataAfter = fetched.asFile().metadata();
  571. return [metadataBefore, metadataAfter];
  572. });
  573. expect(metadataBefore).toBeDefined();
  574. expect(metadataAfter).toBeDefined();
  575. expect(metadataBefore.created).toEqual(metadataAfter.created);
  576. expect(metadataBefore.modified).toEqual(metadataAfter.modified);
  577. });
  578. });