diff --git a/packages/utils/package.json b/packages/utils/package.json index 43daecc2d71c..8137cd496a0a 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -27,7 +27,7 @@ "build:watch": "yarn run build --watch", "build:release": "yarn clean && yarn build", "check-build": "node -e \"(async function() { await import('./lib/index.js') })()\"", - "check-types": "tsc", + "check-types": "tsc && vitest --run --typecheck --dir test/types/", "lint": "eslint --color --ext .ts src/ test/", "lint:fix": "yarn run lint --fix", "pretest": "yarn run check-types", @@ -50,6 +50,7 @@ "devDependencies": { "@types/js-yaml": "^4.0.5", "@types/triple-beam": "^1.3.2", + "prom-client": "^15.1.0", "triple-beam": "^1.3.0" }, "keywords": [ diff --git a/packages/utils/test/types/metrics.test-d.ts b/packages/utils/test/types/metrics.test-d.ts new file mode 100644 index 000000000000..2f008618e648 --- /dev/null +++ b/packages/utils/test/types/metrics.test-d.ts @@ -0,0 +1,114 @@ +import {describe, it, expectTypeOf} from "vitest"; +import {Counter as PromCounter, Gauge as PromGauge, Histogram as PromHistogram} from "prom-client"; +import {Counter, Gauge, Histogram, MetricsRegister} from "../../src/metrics.js"; + +describe("Metric types", () => { + type Labels = {label: string}; + type MultipleLabels = {label1: string; label2: string}; + + describe("MetricsRegister", () => { + const register = {} as MetricsRegister; + + it("should require name and help to be defined on each metric", () => { + expectTypeOf(register.gauge).parameter(0).toHaveProperty("name").toBeString(); + expectTypeOf(register.gauge).parameter(0).toHaveProperty("help").toBeString(); + }); + + it("should require to set labelNames if metric has defined labels", () => { + expectTypeOf(register.gauge) + .parameter(0) + .toHaveProperty("labelNames") + .toMatchTypeOf<"label"[]>(); + + expectTypeOf(register.gauge) + .parameter(0) + .toHaveProperty("labelNames") + .toMatchTypeOf<("label1" | "label2")[]>(); + }); + + it("should not require to set labelNames if metric has no labels", () => { + expectTypeOf(register.gauge).parameter(0).toHaveProperty("labelNames").toEqualTypeOf(); + }); + }); + + describe("Gauge", () => { + it("should be compatible with prom-client type", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("should require to set labels if metric has defined labels", () => { + const gauge = {} as Gauge; + + expectTypeOf(gauge.inc).toEqualTypeOf<(labels: Labels, value?: number | undefined) => void>(); + expectTypeOf(gauge.dec).toEqualTypeOf<(labels: Labels, value?: number | undefined) => void>(); + expectTypeOf(gauge.set).toEqualTypeOf<(labels: Labels, value: number) => void>(); + }); + + it("should not require to set labels if metric has no labels", () => { + const gauge = {} as Gauge; + + expectTypeOf(gauge.inc).toEqualTypeOf<(value?: number | undefined) => void>(); + expectTypeOf(gauge.dec).toEqualTypeOf<(value?: number | undefined) => void>(); + expectTypeOf(gauge.set).toEqualTypeOf<(value: number) => void>(); + }); + }); + + describe("Histogram", () => { + it("should be compatible with prom-client type", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("should require to set labels if metric has defined labels", () => { + const histogram = {} as Histogram; + + expectTypeOf(histogram.startTimer).toMatchTypeOf<(labels: Labels) => () => number>(); + expectTypeOf(histogram.observe).toEqualTypeOf<(labels: Labels, value: number) => void>(); + }); + + it("should require to set labels in timer if not set in startTimer", () => { + const histogram = {} as Histogram; + + const timer = histogram.startTimer(); + expectTypeOf(timer).toEqualTypeOf<(labels: MultipleLabels) => number>(); + }); + + it("should not require to set labels in timer if already set in startTimer", () => { + const histogram = {} as Histogram; + + const timer = histogram.startTimer({label1: "value1", label2: "label2"}); + expectTypeOf(timer).toEqualTypeOf<() => number>(); + }); + + it("should allow to set labels in either startTimer or timer", () => { + const histogram = {} as Histogram; + + const timer = histogram.startTimer({label1: "value1"}); + expectTypeOf(timer).toEqualTypeOf<(labels: {label2: string}) => number>(); + }); + + it("should not require to set labels if metric has no labels", () => { + const histogram = {} as Histogram; + + expectTypeOf(histogram.startTimer).toMatchTypeOf<() => () => number>(); + expectTypeOf(histogram.observe).toEqualTypeOf<(value: number) => void>(); + }); + }); + + describe("Counter", () => { + it("should be compatible with prom-client type", () => { + expectTypeOf().toMatchTypeOf(); + }); + + it("should require to set labels if metric has defined labels", () => { + const counter = {} as Counter; + + expectTypeOf(counter.inc).toEqualTypeOf<(labels: Labels, value?: number | undefined) => void>(); + }); + + it("should not require to set labels if metric has no labels", () => { + const counter = {} as Counter; + + expectTypeOf(counter.inc).toEqualTypeOf<(value?: number | undefined) => void>(); + }); + }); +});