diff --git a/test/cli/commands.test.ts b/test/cli/commands.test.ts index 8e068c8..09189a5 100644 --- a/test/cli/commands.test.ts +++ b/test/cli/commands.test.ts @@ -511,11 +511,6 @@ describe("files", () => { expect(await filesCommand(context(), { collection: "9" })).toBe(1); expect(stderr.text).toBe("Collection 9 not found\n"); }); - - it("exits 1 for a collection ID that is not a number", async () => { - expect(await filesCommand(context(), { collection: "abc" })).toBe(1); - expect(stderr.text).toBe("Invalid collection ID\n"); - }); }); describe("get and get-thumb", () => { @@ -546,12 +541,6 @@ describe("get and get-thumb", () => { expect(stderr.text).toBe("File 999 not found\n"); expect(existsSync(out)).toBe(false); }); - - it("both exit 1 for a file ID that is not a number", async () => { - expect(await getCommand(context(), "abc", {})).toBe(1); - expect(await getThumbCommand(context(), "abc", {})).toBe(1); - expect(stderr.text).toBe("Invalid file ID\nInvalid file ID\n"); - }); }); // A live photo, which Ente stores as one ZIP, is written as its image and its @@ -1035,3 +1024,130 @@ describe("backup-metadata and the thumbnail helpers refresh first", () => { expect(stdout.text).toBe(""); }); }); + +// `files --collection`, `get`, `get-thumb` and `helper fix-missing-thumbnails +// --file` take an ID only when it is written in decimal digits and is a safe +// integer. `Number()` alone reads `0x10` as 16, `1e3` as 1000 and an empty +// string as 0, so the command would act on a file the user never named. +// Anything else is refused with one line naming it, and exit 1, before any +// request. +// +// These run on a real client, read back from a saved session, over a fake Ente +// server holding an empty account, which records each request it receives. With +// a good ID the command goes on to ask the server, and finds nothing there. +describe("an ID given on the command line", () => { + const NOT_IDS = [ + "0x10", + "1e3", + "1.5", + "-1", + "", + "abc", + // One past Number.MAX_SAFE_INTEGER: from here on a number cannot + // hold every integer, so two IDs could read as one. + "9007199254740992", + ]; + + const requests: Request[] = []; + + const emptyAccount = (): CliContext => { + const ctx = context(); + const key = toBase64(new Uint8Array(32)); + saveSession(ctx.sessionDir, { + email: "cli@example.com", + userID: USER_ID, + token: "token", + masterKey: key, + secretKey: key, + publicKey: key, + }); + return { + ...ctx, + loadSession: (path) => + loadSession(path, { + fetch: async (url, init) => { + requests.push(new Request(url, init)); + return new Response( + JSON.stringify({ collections: [] }), + { headers: { "content-type": "application/json" } }, + ); + }, + }), + }; + }; + + beforeEach(() => { + requests.length = 0; + }); + + it("files looks up a decimal collection ID", async () => { + const ctx = emptyAccount(); + expect(await filesCommand(ctx, { collection: "1" })).toBe(1); + expect(stderr.text).toBe("Collection 1 not found\n"); + expect(requests).not.toHaveLength(0); + }); + + it.each(NOT_IDS)( + "files refuses --collection %j before any request", + async (arg) => { + const ctx = emptyAccount(); + expect(await filesCommand(ctx, { collection: arg })).toBe(1); + expect(stderr.text).toBe(`Invalid collection ID: ${arg}\n`); + expect(stdout.text).toBe(""); + expect(requests).toHaveLength(0); + }, + ); + + it("get looks up a decimal file ID", async () => { + expect(await getCommand(emptyAccount(), "100", {})).toBe(1); + expect(stderr.text).toBe("File 100 not found\n"); + expect(requests).not.toHaveLength(0); + }); + + it.each(NOT_IDS)("get refuses %j before any request", async (arg) => { + expect(await getCommand(emptyAccount(), arg, {})).toBe(1); + expect(stderr.text).toBe(`Invalid file ID: ${arg}\n`); + expect(stdout.text).toBe(""); + expect(requests).toHaveLength(0); + }); + + it("get-thumb looks up a decimal file ID", async () => { + expect(await getThumbCommand(emptyAccount(), "100", {})).toBe(1); + expect(stderr.text).toBe("File 100 not found\n"); + expect(requests).not.toHaveLength(0); + }); + + it.each(NOT_IDS)("get-thumb refuses %j before any request", async (arg) => { + expect(await getThumbCommand(emptyAccount(), arg, {})).toBe(1); + expect(stderr.text).toBe(`Invalid file ID: ${arg}\n`); + expect(stdout.text).toBe(""); + expect(requests).toHaveLength(0); + }); + + // The fixer reports each file it was given; on this account neither is + // found, which is a failure, so it exits 1. + it("fix-missing-thumbnails looks up each decimal --file ID", async () => { + const ctx = emptyAccount(); + const opts = { file: ["100", "200"], json: true }; + expect(await fixMissingThumbnailsCommand(ctx, opts)).toBe(1); + expect(JSON.parse(stdout.text)).toMatchObject([ + { fileID: 100, reason: "file not found in any collection" }, + { fileID: 200, reason: "file not found in any collection" }, + ]); + expect(requests).not.toHaveLength(0); + }); + + // One bad ID fails the whole command, so the good one before it is not + // fixed either. + it.each(NOT_IDS)( + "fix-missing-thumbnails refuses --file 100 %j before any request", + async (arg) => { + const ctx = emptyAccount(); + const opts = { file: ["100", arg] }; + expect(await fixMissingThumbnailsCommand(ctx, opts)).toBe(1); + expect(stderr.text).toBe(`Invalid file ID: ${arg}\n`); + expect(stdout.text).toBe(""); + expect(requests).toHaveLength(0); + }, + ); +});