136 lines
4.7 KiB
TypeScript
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);
|
|
});
|
|
});
|