From da2c3bbec95c79c73f26041f536da84b320f1fc0 Mon Sep 17 00:00:00 2001 From: Ayobami Adegoke Date: Fri, 17 Jul 2026 20:56:13 +0100 Subject: [PATCH] test(cli): cover Jobindex and Jobnet error contracts (#172) Extend the offline CLI contract tests to the two remaining Danish portal CLIs. Both implement the documented error contract (JSON errors on stderr, exit 1) but had no test locking it in: - jobindex-search: search without --query, detail without an ID, and bunli numeric-option validation (--page not-a-number) - jobnet-search: detail without an ID, occupations without --search-string, suggestions without --query, and numeric-option validation All asserted paths exit before any network request, matching the no-live-portal-requests CI policy. Assertions were written against observed CLI output, not assumed shapes. --- .../cli/tests/cli-contract.test.ts | 38 ++++++++++++++ .../cli/tests/cli-contract.test.ts | 49 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 .agents/skills/jobindex-search/cli/tests/cli-contract.test.ts create mode 100644 .agents/skills/jobnet-search/cli/tests/cli-contract.test.ts diff --git a/.agents/skills/jobindex-search/cli/tests/cli-contract.test.ts b/.agents/skills/jobindex-search/cli/tests/cli-contract.test.ts new file mode 100644 index 0000000..68d1cfa --- /dev/null +++ b/.agents/skills/jobindex-search/cli/tests/cli-contract.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, test } from "bun:test"; +import { runCLI } from "./helpers"; + +describe("Jobindex CLI error contract", () => { + test("search without a query fails with JSON on stderr", async () => { + const result = await runCLI(["search"]); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(JSON.parse(result.stderr)).toEqual({ + error: "--query is required", + code: "MISSING_REQUIRED", + }); + }); + + test("detail without an ID fails before making a request", async () => { + const result = await runCLI(["detail"]); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(JSON.parse(result.stderr)).toEqual({ + error: "Job ID or URL is required", + code: "MISSING_REQUIRED", + }); + }); + + test("an invalid numeric option fails before making a request", async () => { + const result = await runCLI(["search", "--query", "test", "--page", "not-a-number"]); + const error = JSON.parse(result.stderr); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(error.ok).toBe(false); + expect(error.error.kind).toBe("validation"); + expect(error.error.option).toBe("page"); + expect(error.error.message).toContain("Expected number"); + }); +}); diff --git a/.agents/skills/jobnet-search/cli/tests/cli-contract.test.ts b/.agents/skills/jobnet-search/cli/tests/cli-contract.test.ts new file mode 100644 index 0000000..0dd1c67 --- /dev/null +++ b/.agents/skills/jobnet-search/cli/tests/cli-contract.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, test } from "bun:test"; +import { runCLI } from "./helpers"; + +describe("Jobnet CLI error contract", () => { + test("detail without an ID fails with JSON on stderr", async () => { + const result = await runCLI(["detail"]); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(JSON.parse(result.stderr)).toEqual({ + error: "Job ad ID is required", + code: "MISSING_REQUIRED", + }); + }); + + test("occupations without a search string fails before making a request", async () => { + const result = await runCLI(["occupations"]); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(JSON.parse(result.stderr)).toEqual({ + error: "--search-string is required", + code: "MISSING_REQUIRED", + }); + }); + + test("suggestions without a query fails before making a request", async () => { + const result = await runCLI(["suggestions"]); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(JSON.parse(result.stderr)).toEqual({ + error: "--query is required", + code: "MISSING_REQUIRED", + }); + }); + + test("an invalid numeric option fails before making a request", async () => { + const result = await runCLI(["search", "--page", "not-a-number"]); + const error = JSON.parse(result.stderr); + + expect(result.exitCode).toBe(1); + expect(result.stdout).toBe(""); + expect(error.ok).toBe(false); + expect(error.error.kind).toBe("validation"); + expect(error.error.option).toBe("page"); + expect(error.error.message).toContain("Expected number"); + }); +});