From df0f100174284597eff1cdf2da5e8a6014b5d896 Mon Sep 17 00:00:00 2001 From: Jiho Lee Date: Sun, 26 Jul 2026 06:42:26 +0900 Subject: [PATCH] fix: require pinHeader pinCount to be a positive integer (#756) --- lib/components/pin-header.ts | 2 +- tests/pin-header-pin-count.test.ts | 43 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/pin-header-pin-count.test.ts diff --git a/lib/components/pin-header.ts b/lib/components/pin-header.ts index 5c74e304..7480e54f 100644 --- a/lib/components/pin-header.ts +++ b/lib/components/pin-header.ts @@ -123,7 +123,7 @@ export interface PinHeaderProps extends CommonComponentProps { } export const pinHeaderProps = commonComponentProps.extend({ - pinCount: z.number(), + pinCount: z.number().int().positive(), pitch: distance.optional(), schFacingDirection: z.enum(["up", "down", "left", "right"]).optional(), gender: z.enum(["male", "female", "unpopulated"]).optional().default("male"), diff --git a/tests/pin-header-pin-count.test.ts b/tests/pin-header-pin-count.test.ts new file mode 100644 index 00000000..70891285 --- /dev/null +++ b/tests/pin-header-pin-count.test.ts @@ -0,0 +1,43 @@ +import { expect, test } from "bun:test" +import { pinHeaderProps } from "lib/components/pin-header" + +const parse = (pinCount: unknown) => + pinHeaderProps.safeParse({ name: "H1", pinCount }) + +test("pinCount accepts whole positive counts", () => { + expect(parse(1).success).toBe(true) + expect(parse(4).success).toBe(true) + expect(parse(40).success).toBe(true) +}) + +test("pinCount rejects a fractional count", () => { + // Previously accepted. A fractional count produced a header whose port count + // and pad count disagreed (pinCount 2.5 -> 2 source_ports but 3 pads) with no + // error raised anywhere. + expect(parse(2.5).success).toBe(false) + expect(parse(3.7).success).toBe(false) +}) + +test("pinCount rejects zero and negative counts", () => { + // A header with no pins is not a header; these previously reached the renderer + // and surfaced later as an unrelated pcb_missing_footprint_error. + expect(parse(0).success).toBe(false) + expect(parse(-4).success).toBe(false) +}) + +test("pinCount rejects non-finite counts", () => { + expect(parse(Number.NaN).success).toBe(false) + expect(parse(Number.POSITIVE_INFINITY).success).toBe(false) +}) + +test("pinCount is still required", () => { + // The guard must not turn a required prop into an optional one. + expect(pinHeaderProps.safeParse({ name: "H1" }).success).toBe(false) +}) + +test("a valid pinCount still parses to the same value", () => { + const result = pinHeaderProps.safeParse({ name: "H1", pinCount: 8 }) + + expect(result.success).toBe(true) + if (result.success) expect(result.data.pinCount).toBe(8) +})