fix(portal-clis): reject invalid --format flags with BAD_ARG in linkedin-search and freehire-search (#555) (#556)

This commit is contained in:
Oscar Madera authored and GitHub committed 2026-10-15 11:56:24 +02:00
1 parent a644df0625
commit c922b0c757
5 files changed
+75 -8

No files matched your search

+16 -4
View File
@@ -168,7 +168,13 @@ async function main(): Promise<number> {
}
if (cmd === "search") {
const fmt = (flags.format as string) || "json"
const fmt = flags.format !== undefined ? String(flags.format) : "json"
if (!["json", "table", "plain"].includes(fmt)) {
process.stderr.write(
JSON.stringify({ error: `--format must be one of json|table|plain, got "${fmt}"`, code: "BAD_ARG" }) + "\n",
)
return 1
}
// Validated here rather than server-side: the API answers an unrecognized
// format with raw HTML instead of an error, so a typo would silently change
@@ -209,7 +215,7 @@ async function main(): Promise<number> {
jobage: flags.jobage ? parseInt(flags.jobage as string, 10) : 9999,
page: flags.page ? Math.max(1, parseInt(flags.page as string, 10)) : 1,
limit: flags.limit ? Math.max(1, parseInt(flags.limit as string, 10)) : 25,
format: (["json", "table", "plain"].includes(fmt) ? fmt : "json") as SearchOpts["format"],
format: fmt as SearchOpts["format"],
descriptionFormat: descFmt as DescriptionFormat,
includeDescription: flags["no-description"] === undefined,
regions: commaList(flags.region),
@@ -232,8 +238,14 @@ async function main(): Promise<number> {
process.stderr.write(JSON.stringify({ error: "detail requires a <slug|url>", code: "NO_ID" }) + "\n")
return 1
}
const fmt = (flags.format as string) || "json"
const opts: DetailOpts = { id, format: fmt === "plain" ? "plain" : "json" }
const fmt = flags.format !== undefined ? String(flags.format) : "json"
if (!["json", "plain"].includes(fmt)) {
process.stderr.write(
JSON.stringify({ error: `--format must be one of json|plain, got "${fmt}"`, code: "BAD_ARG" }) + "\n",
)
return 1
}
const opts: DetailOpts = { id, format: fmt as DetailOpts["format"] }
return runDetail(opts)
}
@@ -75,6 +75,24 @@ describe("freehire CLI flag validation", () => {
});
});
describe("--format validation", () => {
test("search with an unsupported format exits 1 with BAD_ARG", async () => {
const result = await runCLI(["search", "--format", "invalid"]);
expect(result.exitCode).not.toBe(0);
const err = parsedStderr(result.stderr);
expect(err.code).toBe("BAD_ARG");
expect(err.error).toMatch(/format/);
});
test("detail with an unsupported format exits 1 with BAD_ARG", async () => {
const result = await runCLI(["detail", "example-slug", "--format", "table"]);
expect(result.exitCode).not.toBe(0);
const err = parsedStderr(result.stderr);
expect(err.code).toBe("BAD_ARG");
expect(err.error).toMatch(/format/);
});
});
describe("detail argument validation", () => {
test("missing slug exits 1 with NO_ID", async () => {
const result = await runCLI(["detail"]);
+16 -4
View File
@@ -113,7 +113,13 @@ async function main(): Promise<number> {
)
return 1
}
const fmt = (flags.format as string) || "json"
const fmt = flags.format !== undefined ? String(flags.format) : "json"
if (!["json", "table", "plain"].includes(fmt)) {
process.stderr.write(
JSON.stringify({ error: `--format must be one of json|table|plain, got "${fmt}"`, code: "BAD_ARG" }) + "\n",
)
return 1
}
if (flags.jobage !== undefined && flags["jobage-minutes"] !== undefined) {
process.stderr.write(
@@ -168,7 +174,7 @@ async function main(): Promise<number> {
remote: typeof flags.remote === "string" ? flags.remote : undefined,
page: flags.page ? Math.max(1, parseInt(flags.page as string, 10)) : 1,
limit: flags.limit ? parseInt(flags.limit as string, 10) : undefined,
format: (["json", "table", "plain"].includes(fmt) ? fmt : "json") as SearchOpts["format"],
format: fmt as SearchOpts["format"],
}
return runSearch(opts)
}
@@ -179,10 +185,16 @@ async function main(): Promise<number> {
process.stderr.write(JSON.stringify({ error: "detail requires an <id|url>", code: "NO_ID" }) + "\n")
return 1
}
const fmt = (flags.format as string) || "json"
const fmt = flags.format !== undefined ? String(flags.format) : "json"
if (!["json", "plain"].includes(fmt)) {
process.stderr.write(
JSON.stringify({ error: `--format must be one of json|plain, got "${fmt}"`, code: "BAD_ARG" }) + "\n",
)
return 1
}
const opts: DetailOpts = {
id,
format: (fmt === "plain" ? "plain" : "json") as DetailOpts["format"],
format: fmt as DetailOpts["format"],
}
return runDetail(opts)
}
@@ -134,6 +134,24 @@ describe("LinkedIn CLI flag validation", () => {
expect(err.code).not.toBe("BAD_ARG");
});
});
describe("--format validation", () => {
test("search with an unsupported format exits 1 with BAD_ARG", async () => {
const result = await runCLI(["search", "-l", LOCATION, "--format", "invalid"]);
expect(result.exitCode).not.toBe(0);
const err = parsedStderr(result.stderr);
expect(err.code).toBe("BAD_ARG");
expect(err.error).toMatch(/format/);
});
test("detail with an unsupported format exits 1 with BAD_ARG", async () => {
const result = await runCLI(["detail", "123456", "--format", "table"]);
expect(result.exitCode).not.toBe(0);
const err = parsedStderr(result.stderr);
expect(err.code).toBe("BAD_ARG");
expect(err.error).toMatch(/format/);
});
});
});