mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 21:22:51 +00:00
fix(portal-clis): reject invalid --format flags with BAD_ARG in linkedin-search and freehire-search (#555) (#556)
This commit is contained in:
1 parent
a644df0625
commit
c922b0c757
5 files changed
+75
-8
No files matched your search
@@ -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"]);
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@ per-file diff commands.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`linkedin-search` and `freehire-search` reject invalid `--format` flags with `BAD_ARG`** (#555,
|
||||
`.agents/skills/linkedin-search/cli/src/cli.ts`, `.agents/skills/freehire-search/cli/src/cli.ts`) -
|
||||
unrecognized `--format` flags previously fell back silently to `"json"` with exit code 0, masking
|
||||
formatting typos and violating the CLI contract. Both CLIs now validate format arguments strictly
|
||||
(`json|table|plain` for search, `json|plain` for detail) and exit 1 with `{ error, code: "BAD_ARG" }`
|
||||
on invalid options.
|
||||
|
||||
- **`jobdanmark-search detail` no longer keeps a malformed closing tag as text on the
|
||||
rendered-HTML branch** (#525, `detail.ts`, `tests/detail-parsing.test.ts`) - a live page wrote
|
||||
its Jobtype item as `Fuldtid </ li >`, and because `node-html-parser` does not recognise a
|
||||
|
||||
Reference in new issue
Block a user