mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-11-02 13:12:50 +00:00
fix(freehire-search): reject --description-format on detail instead of ignoring it (#648)
* fix(freehire-search): reject --description-format on detail instead of ignoring it The flag was on detail's known-flag list, but runDetail never reads it and always prints the detail endpoint's HTML body stripped to plain text, so detail <slug> --description-format markdown passed the unknown-flag guard, exited 0 and printed text. Drop it from detail's list so it fails with UNKNOWN_FLAG before any request; search keeps the flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changelog: place the #648 entry under [Unreleased] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Mads Lorentzen <madslorentzen_17@hotmail.com>
This commit is contained in:
3 files changed
+84
-2
No files matched your search
@@ -131,13 +131,15 @@ function parseIntFlag(name: string, raw: string | boolean | string[]): number |
|
||||
|
||||
// Long-form flag names each command accepts (parseFlags resolves the short
|
||||
// aliases q/n to these before validation). "help"/"h" pass so `search --help`
|
||||
// still prints usage.
|
||||
// still prints usage. detail takes no --description-format: it always prints
|
||||
// the detail endpoint's HTML body stripped to text, so accepting the flag
|
||||
// there ignored it silently.
|
||||
const KNOWN_FLAGS: Record<string, Set<string>> = {
|
||||
search: new Set([
|
||||
"query", "category", "city", "company", "country", "facet", "format", "jobage", "limit",
|
||||
"page", "region", "remote", "seniority", "skill", "description-format", "no-description", "help", "h",
|
||||
]),
|
||||
detail: new Set(["format", "description-format", "help", "h"]),
|
||||
detail: new Set(["format", "help", "h"]),
|
||||
}
|
||||
|
||||
async function main(): Promise<number> {
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { runCLI } from "./helpers";
|
||||
|
||||
// `detail` listed --description-format among its known flags, but runDetail
|
||||
// never reads it: the detail endpoint's HTML body is always stripped to plain
|
||||
// text. So `detail <slug> --description-format markdown` passed the
|
||||
// unknown-flag guard, exited 0 and printed plain text - the silently ignored
|
||||
// flag that guard exists to prevent. The CLI runs against a loopback API
|
||||
// through its supported FREEHIRE_API_URL override; requests are counted.
|
||||
const JOB = {
|
||||
public_slug: "backend-engineer-acme-ab12cd34",
|
||||
source: "greenhouse",
|
||||
external_id: "1",
|
||||
url: "https://boards.example/acme/1",
|
||||
title: "Backend Engineer",
|
||||
company: "Acme",
|
||||
company_slug: "acme",
|
||||
location: "Berlin, Germany",
|
||||
description: "<p>Build things</p><ul><li>Go</li><li>Postgres</li></ul>",
|
||||
skills: ["go"],
|
||||
regions: ["eu"],
|
||||
countries: ["DE"],
|
||||
cities: ["Berlin"],
|
||||
posted_at: "2026-10-01T00:00:00Z",
|
||||
created_at: "2026-10-01T00:00:00Z",
|
||||
enrichment: {},
|
||||
};
|
||||
|
||||
async function detail(args: string[]) {
|
||||
const requests: URL[] = [];
|
||||
const server = Bun.serve({
|
||||
hostname: "127.0.0.1",
|
||||
port: 0,
|
||||
fetch(request) {
|
||||
requests.push(new URL(request.url));
|
||||
return Response.json({ data: JOB });
|
||||
},
|
||||
});
|
||||
try {
|
||||
const result = await runCLI(["detail", JOB.public_slug, ...args], { FREEHIRE_API_URL: server.url.origin });
|
||||
return { result, requests };
|
||||
} finally {
|
||||
server.stop(true);
|
||||
}
|
||||
}
|
||||
|
||||
describe("detail flags", () => {
|
||||
for (const value of ["markdown", "text", "html"]) {
|
||||
test(`--description-format ${value} is rejected as an unknown detail flag before any request`, async () => {
|
||||
const { result, requests } = await detail(["--description-format", value]);
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.stdout).toBe("");
|
||||
const error = JSON.parse(result.stderr);
|
||||
expect(error.code).toBe("UNKNOWN_FLAG");
|
||||
expect(error.error).toContain("--description-format");
|
||||
expect(requests).toHaveLength(0);
|
||||
});
|
||||
}
|
||||
|
||||
test("--format plain still prints the detail", async () => {
|
||||
const { result, requests } = await detail(["--format", "plain"]);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(requests).toHaveLength(1);
|
||||
expect(requests[0].pathname).toBe(`/api/v1/jobs/${JOB.public_slug}`);
|
||||
expect(result.stdout).toContain("Backend Engineer");
|
||||
});
|
||||
|
||||
test("search still validates its own --description-format", async () => {
|
||||
const result = await runCLI(["search", "--description-format", "tekst"]);
|
||||
expect(JSON.parse(result.stderr).code).toBe("BAD_ARG");
|
||||
});
|
||||
});
|
||||
@@ -30,6 +30,14 @@ per-file diff commands.
|
||||
handler-level test that pastes the search fixture's own apply URL into `detail` with fetch
|
||||
stubbed; the off-host cases fail on master.
|
||||
|
||||
- **`freehire-search detail` rejects `--description-format` instead of ignoring it**
|
||||
(#648, `.agents/skills/freehire-search/cli/src/cli.ts`, `tests/detail-flags.test.ts`) - the flag
|
||||
was on `detail`'s known-flag list, but `runDetail` never reads it and always prints the detail
|
||||
endpoint's HTML body stripped to plain text. So `detail <slug> --description-format markdown`
|
||||
passed the unknown-flag guard, exited 0 and printed text, which is the silently ignored flag
|
||||
that guard exists to prevent. `detail` now answers it with `UNKNOWN_FLAG` before any request;
|
||||
`search`, where the flag is documented and used, is unchanged.
|
||||
|
||||
## [1.7.3] - 2026-10-19
|
||||
|
||||
### Added
|
||||
|
||||
Reference in new issue
Block a user