diff --git a/test/cli/backup-undecryptable.test.ts b/test/cli/backup-undecryptable.test.ts index f7d2573..7344ba1 100644 --- a/test/cli/backup-undecryptable.test.ts +++ b/test/cli/backup-undecryptable.test.ts @@ -445,6 +445,44 @@ describe("quak backup with records it cannot decrypt", () => { expect(await backup(ente)).toBe(0); }); + it("removes what an earlier run linked for an album or a file that no longer decrypts, and keeps the originals", async () => { + // At time 1 everything decrypts: the first run links files 10 and 11 + // in Trip and file 20 in Family. + const ente = new FakeEnte(); + ente.putAlbum(1, "Trip", 1); + ente.putFile(1, 10, 1); + ente.putFile(1, 11, 1); + ente.putAlbum(2, "Family", 1); + ente.putFile(2, 20, 1); + expect(await backup(ente)).toBe(0); + const collections = join(backupDir(), "collections"); + expect(readdirSync(join(collections, "Trip")).sort()).toEqual([ + "photo-10.jpg", + "photo-11.jpg", + ]); + expect(readdirSync(join(collections, "Family"))).toEqual([ + "photo-20.jpg", + ]); + + // At time 2 the server's copy of file 11 in Trip, and album 2 itself, + // no longer decrypt. A run builds the album folders, their JSON and + // their links from what the library holds, so it removes them as if + // the album and the file had been deleted: Family's folder and JSON + // go, and Trip neither links nor lists file 11. The originals stay at + // their save paths, and the run exits 1. + ente.putFile(1, 11, 2, { corruptKey: true }); + ente.putAlbum(2, "Family", 2, true); + expect(await backup(ente)).toBe(1); + + expect(readdirSync(collections).sort()).toEqual(["Trip", "Trip.json"]); + expect(readdirSync(join(collections, "Trip"))).toEqual([ + "photo-10.jpg", + ]); + expect(albumFiles("Trip")).toEqual([10]); + expect([10, 11, 20].map(stored)).toEqual([true, true, true]); + expect(Object.keys(failures())).toEqual(["11"]); + }); + it("backs up a file that does not decrypt in one album but does in another, and does not report it", async () => { // File 30 is in albums 1 and 3, each holding its key sealed under the // album's own key. In album 1 that is corrupt; in album 3 it is not, diff --git a/test/library/store.test.ts b/test/library/store.test.ts index 21212b3..38e7e25 100644 --- a/test/library/store.test.ts +++ b/test/library/store.test.ts @@ -172,6 +172,29 @@ describe("MetadataStore", () => { expect(store.listCollections()).toEqual([]); }); + it("loads an empty store when the list of what the refresh could not decrypt is damaged", async () => { + // A backup reads that list on every run, so a damaged one that loaded + // would fail every backup until the cache was deleted. A string is + // damaged too, though JavaScript can loop over its characters. + mkdirSync(join(dir, "cache"), { recursive: true }); + for (const undecryptable of [{}, "not a list", [1]]) { + writeFileSync( + path, + JSON.stringify({ + schemaVersion: METADATA_SCHEMA_VERSION, + userID: 42, + collectionsSinceTime: 0, + collections: [], + files: [], + undecryptable, + }), + ); + const store = await MetadataStore.load(path); + expect(store.userID).toBe(0); + expect(store.listUndecryptable()).toEqual([]); + } + }); + it("creates the directory 0700 and the file 0600", async () => { const store = await MetadataStore.load(path); store.putCollection(sampleCollection());