diff --git a/src/declaration/network-port-number.ts b/src/declaration/network-port-number.ts index 21dd3a5..83900a9 100644 --- a/src/declaration/network-port-number.ts +++ b/src/declaration/network-port-number.ts @@ -1,3 +1,7 @@ +import type { + Constraint, + DeclarationConstraintOptions, +} from "../constraint.js"; import { createNetworkPortNumberConstraint } from "../constraint/network-port-number.js"; import { assertRangeSpec, @@ -23,6 +27,7 @@ import { resolve } from "../maybe.js"; import { ScalarSchema, createScalar, toString } from "../schema.js"; export type Options = DeclarationOptions & + DeclarationConstraintOptions & DeclarationExampleOptions & Partial>; @@ -62,7 +67,10 @@ function createSchema(name: string, options: Options): ScalarSchema { return Number(v); } - const constraints = [createNetworkPortNumberConstraint()]; + const { constraints: explicitConstraints = [] } = options; + const constraints: Constraint[] = [ + createNetworkPortNumberConstraint(), + ]; try { if (hasNumberRangeConstraint(options)) { @@ -73,7 +81,10 @@ function createSchema(name: string, options: Options): ScalarSchema { throw new SpecError(name, normalize(error)); } - return createScalar("port number", toString, unmarshal, constraints); + return createScalar("port number", toString, unmarshal, [ + ...constraints, + ...explicitConstraints, + ]); } function buildExamples(): Example[] { diff --git a/test/suite/declaration/network-port-number.spec.ts b/test/suite/declaration/network-port-number.spec.ts index d9b6962..fd64087 100644 --- a/test/suite/declaration/network-port-number.spec.ts +++ b/test/suite/declaration/network-port-number.spec.ts @@ -298,4 +298,72 @@ describe("Network port number declarations", () => { }); }, ); + + describe("when the declaration has constraints", () => { + beforeEach(() => { + declaration = networkPortNumber("AUSTENITE_PORT", "", { + constraints: [ + { + description: "", + constrain: (v) => v % 2 === 0 || "must be divisible by 2", + }, + { + description: "", + constrain: (v) => v % 3 === 0 || "must be divisible by 3", + }, + ], + examples: [{ value: 6, label: "example" }], + }); + }); + + describe("when the value satisfies the constraints", () => { + beforeEach(() => { + process.env.AUSTENITE_PORT = "6"; + + initialize({ onInvalid: noop }); + }); + + describe(".value()", () => { + it("returns the value", () => { + expect(declaration.value()).toBe(6); + }); + }); + }); + + describe("when the value violates the first constraint", () => { + beforeEach(() => { + process.env.AUSTENITE_PORT = "3"; + + initialize({ onInvalid: noop }); + }); + + describe(".value()", () => { + it("throws", () => { + expect(() => { + declaration.value(); + }).toThrow( + "value of AUSTENITE_PORT (3) is invalid: must be divisible by 2", + ); + }); + }); + }); + + describe("when the value violates the second constraint", () => { + beforeEach(() => { + process.env.AUSTENITE_PORT = "2"; + + initialize({ onInvalid: noop }); + }); + + describe(".value()", () => { + it("throws", () => { + expect(() => { + declaration.value(); + }).toThrow( + "value of AUSTENITE_PORT (2) is invalid: must be divisible by 3", + ); + }); + }); + }); + }); });