mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-08 07:57:40 +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") {
|
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
|
// 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
|
// 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,
|
jobage: flags.jobage ? parseInt(flags.jobage as string, 10) : 9999,
|
||||||
page: flags.page ? Math.max(1, parseInt(flags.page as string, 10)) : 1,
|
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,
|
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,
|
descriptionFormat: descFmt as DescriptionFormat,
|
||||||
includeDescription: flags["no-description"] === undefined,
|
includeDescription: flags["no-description"] === undefined,
|
||||||
regions: commaList(flags.region),
|
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")
|
process.stderr.write(JSON.stringify({ error: "detail requires a <slug|url>", code: "NO_ID" }) + "\n")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
const fmt = (flags.format as string) || "json"
|
const fmt = flags.format !== undefined ? String(flags.format) : "json"
|
||||||
const opts: DetailOpts = { id, format: fmt === "plain" ? "plain" : "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)
|
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", () => {
|
describe("detail argument validation", () => {
|
||||||
test("missing slug exits 1 with NO_ID", async () => {
|
test("missing slug exits 1 with NO_ID", async () => {
|
||||||
const result = await runCLI(["detail"]);
|
const result = await runCLI(["detail"]);
|
||||||
|
|||||||
@@ -113,7 +113,13 @@ async function main(): Promise<number> {
|
|||||||
)
|
)
|
||||||
return 1
|
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) {
|
if (flags.jobage !== undefined && flags["jobage-minutes"] !== undefined) {
|
||||||
process.stderr.write(
|
process.stderr.write(
|
||||||
@@ -168,7 +174,7 @@ async function main(): Promise<number> {
|
|||||||
remote: typeof flags.remote === "string" ? flags.remote : undefined,
|
remote: typeof flags.remote === "string" ? flags.remote : undefined,
|
||||||
page: flags.page ? Math.max(1, parseInt(flags.page as string, 10)) : 1,
|
page: flags.page ? Math.max(1, parseInt(flags.page as string, 10)) : 1,
|
||||||
limit: flags.limit ? parseInt(flags.limit as string, 10) : undefined,
|
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)
|
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")
|
process.stderr.write(JSON.stringify({ error: "detail requires an <id|url>", code: "NO_ID" }) + "\n")
|
||||||
return 1
|
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 = {
|
const opts: DetailOpts = {
|
||||||
id,
|
id,
|
||||||
format: (fmt === "plain" ? "plain" : "json") as DetailOpts["format"],
|
format: fmt as DetailOpts["format"],
|
||||||
}
|
}
|
||||||
return runDetail(opts)
|
return runDetail(opts)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,6 +134,24 @@ describe("LinkedIn CLI flag validation", () => {
|
|||||||
expect(err.code).not.toBe("BAD_ARG");
|
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
|
### 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
|
- **`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
|
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
|
its Jobtype item as `Fuldtid </ li >`, and because `node-html-parser` does not recognise a
|
||||||
|
|||||||
Reference in new issue
Block a user