diff --git a/TODO.md b/TODO.md index 0d5dc4c..b487621 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,11 @@ Tag v1.0.0. # Completed Steps +- 2026-09-23: Tested the live-photo hash check's error paths (issue 117). Tests + download a live photo whose ZIP names an unknown compression method, one whose + ZIP has no image entry and one with no video entry, and check that nothing is + stored and the error names the file ID; the unreadable one is not retried. + - 2026-09-23: `quak logout` ends the session on the server (issue 108). It calls `POST /users/logout` through the new `Client.logoutOnServer()`, then deletes `session.json` even when that call fails, says so and exits 1. It prints the diff --git a/test/download/download.test.ts b/test/download/download.test.ts index 304dbd9..4e7e03e 100644 --- a/test/download/download.test.ts +++ b/test/download/download.test.ts @@ -1747,4 +1747,42 @@ describe("downloadFile content hash", () => { expect(readdirSync(t.dir)).toEqual([]); }); + + it("rejects a live photo that is not a readable ZIP and does not retry", async () => { + // Bytes 8-9 of a ZIP entry's local header name its compression + // method; 99 is one no reader knows, so the entry cannot be read. + const zip = livePhotoZip.slice(); + zip[8] = 99; + zip[9] = 0; + const t = setup(zip, { fileType: "livePhoto", hash: livePhotoHash }); + + await expect(t.run()).rejects.toThrow( + /file 999: live photo is not a readable ZIP/, + ); + + expect(readdirSync(t.dir)).toEqual([]); + expect(t.requests()).toBe(1); + }); + + it("rejects a live photo ZIP with no image entry", async () => { + const zip = zipSync({ "video.mov": patternBytes(900, 82) }); + const t = setup(zip, { fileType: "livePhoto", hash: livePhotoHash }); + + await expect(t.run()).rejects.toThrow( + /file 999: live photo ZIP does not hold both an image and a video/, + ); + + expect(readdirSync(t.dir)).toEqual([]); + }); + + it("rejects a live photo ZIP with no video entry", async () => { + const zip = zipSync({ "image.heic": patternBytes(500, 81) }); + const t = setup(zip, { fileType: "livePhoto", hash: livePhotoHash }); + + await expect(t.run()).rejects.toThrow( + /file 999: live photo ZIP does not hold both an image and a video/, + ); + + expect(readdirSync(t.dir)).toEqual([]); + }); });