typhon/tests/TyphonMetrics.test.ts

136 lines
4.7 KiB
TypeScript

import { Registry } from "prom-client";
import {
AcInfinityMode,
ClimateSnapshot,
ControllerClimate,
PortClimate
} from "../src/domain/ClimateSnapshot";
import { TyphonMetrics } from "../src/metrics/TyphonMetrics";
function valueForMetric(
registryJson: Array<{
name: string;
values?: Array<{ labels: Record<string, string | number | undefined>; value: number | string }>;
}>,
name: string,
labels: Record<string, string>
): number | null {
const metric = registryJson.find((item) => item.name === name);
const found = metric?.values?.find((entry) =>
Object.entries(labels).every(([key, expected]) => String(entry.labels[key]) === expected)
);
return found ? Number(found.value) : null;
}
describe("TyphonMetrics", () => {
it("updates all climate gauges from a snapshot", async () => {
const registry = new Registry();
const metrics = new TyphonMetrics("0.1.0", false, registry);
const snapshot = new ClimateSnapshot(1_700_000_000, [
new ControllerClimate("c1", "Tent A", true, 24.5, 0.55, 1.2, 20, true, [
new PortClimate(1, "Fan A", "interior", true, true, 6, 7, 1, true, 1200, AcInfinityMode.On)
])
]);
metrics.updateFromSnapshot(snapshot);
const json = await registry.getMetricsAsJSON();
expect(valueForMetric(json, "typhon_up", {})).toBe(1);
expect(valueForMetric(json, "typhon_temperature_celsius", { controller_id: "c1", controller_name: "Tent A" })).toBe(24.5);
expect(valueForMetric(json, "typhon_relative_humidity_ratio", { controller_id: "c1", controller_name: "Tent A" })).toBe(0.55);
expect(valueForMetric(json, "typhon_relative_humidity_percent", { controller_id: "c1", controller_name: "Tent A" })).toBeCloseTo(55, 6);
expect(valueForMetric(json, "typhon_controller_info", {
controller_id: "c1",
controller_name: "Tent A",
device_type: "20",
new_framework_device: "true"
})).toBe(1);
expect(valueForMetric(json, "typhon_fan_speed_level", {
controller_id: "c1",
controller_name: "Tent A",
port: "1",
port_name: "Fan A",
fan_group: "interior"
})).toBe(6);
expect(valueForMetric(json, "typhon_port_connected_device", {
controller_id: "c1",
controller_name: "Tent A",
port: "1",
port_name: "Fan A",
fan_group: "interior"
})).toBe(1);
expect(valueForMetric(json, "typhon_mode", {
controller_id: "c1",
controller_name: "Tent A",
port: "1",
port_name: "Fan A",
fan_group: "interior",
mode: "on"
})).toBe(1);
});
it("supports unknown controller metadata and nullable resistance", async () => {
const registry = new Registry();
const metrics = new TyphonMetrics("0.1.0", false, registry);
const snapshot = new ClimateSnapshot(1_700_000_050, [
new ControllerClimate("c2", "Tent B", false, 22.2, 0.40, 0.8, null, null, [
new PortClimate(2, "Fan B", "outlet", false, false, 0, 3, 0, true, null, AcInfinityMode.Unknown)
])
]);
metrics.updateFromSnapshot(snapshot);
const json = await registry.getMetricsAsJSON();
expect(valueForMetric(json, "typhon_controller_info", {
controller_id: "c2",
controller_name: "Tent B",
device_type: "unknown",
new_framework_device: "unknown"
})).toBe(1);
const resistance = valueForMetric(json, "typhon_port_resistance_ohms", {
controller_id: "c2",
controller_name: "Tent B",
port: "2",
port_name: "Fan B",
fan_group: "outlet"
});
expect(resistance).toBeNull();
expect(valueForMetric(json, "typhon_mode", {
controller_id: "c2",
controller_name: "Tent B",
port: "2",
port_name: "Fan B",
fan_group: "outlet",
mode: "unknown"
})).toBe(1);
});
it("tracks polling failures", async () => {
const registry = new Registry();
const metrics = new TyphonMetrics("0.1.0", false, registry);
metrics.markPollFailure("api", "100001");
const json = await registry.getMetricsAsJSON();
expect(valueForMetric(json, "typhon_up", {})).toBe(0);
expect(valueForMetric(json, "typhon_poll_errors_total", { reason: "api", code: "100001" })).toBe(1);
});
it("reports data age when there is a successful snapshot", async () => {
const registry = new Registry();
const metrics = new TyphonMetrics("0.1.0", false, registry);
metrics.updateFromSnapshot(new ClimateSnapshot(1_700_000_000, []));
metrics.refreshDataAgeGauge(1_700_000_090);
const json = await registry.getMetricsAsJSON();
expect(valueForMetric(json, "typhon_data_age_seconds", {})).toBe(90);
expect(valueForMetric(json, "typhon_last_successful_poll_timestamp_seconds", {})).toBe(1_700_000_000);
});
});