This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"lostak.dev/shelly-exporter/configuration"
|
||||
)
|
||||
|
||||
const plugSStatusFixture = `{
|
||||
"wifi_sta":{"connected":true,"ssid":"lostak","ip":"192.168.0.30","rssi":-77},
|
||||
"cloud":{"enabled":false,"connected":false},
|
||||
"mqtt":{"connected":false},
|
||||
"time":"23:52","serial":1,"has_update":false,"mac":"E8DB84BC666A",
|
||||
"relays":[{"ison":true,"has_timer":false,"overpower":false}],
|
||||
"meters":[{"power":166.00,"is_valid":true,"timestamp":1787010749,"counters":[168.129,167.929,167.771],"total":4188430}],
|
||||
"temperature":49.40,"overtemperature":false,
|
||||
"update":{"status":"unknown","has_update":false,"new_version":"","old_version":"20190516-073020/master@ea1b23db"},
|
||||
"ram_total":50832,"ram_free":40188,"fs_size":233681,"fs_free":171182,"uptime":1512798
|
||||
}`
|
||||
|
||||
type statusClientFunc func(context.Context, any) error
|
||||
|
||||
func (function statusClientFunc) GetStatus(ctx context.Context, destination any) error {
|
||||
return function(ctx, destination)
|
||||
}
|
||||
|
||||
func fixtureClient(_ context.Context, destination any) error {
|
||||
return json.Unmarshal([]byte(plugSStatusFixture), destination)
|
||||
}
|
||||
|
||||
func TestPlugSCollectorExposesStatusAndEnergyCounter(t *testing.T) {
|
||||
deviceCollector, err := NewDeviceCollector(configuration.DeviceConfiguration{
|
||||
Name: "office", Product: "plug_s",
|
||||
}, statusClientFunc(fixtureClient))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
registry := prometheus.NewPedanticRegistry()
|
||||
if err := registry.Register(deviceCollector); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
families, err := registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather() returned an unexpected error: %v", err)
|
||||
}
|
||||
energy := findMetricFamily(t, families, "shelly_meter_energy_watt_hours_total")
|
||||
if energy.GetType() != dto.MetricType_COUNTER {
|
||||
t.Fatalf("energy metric type = %s, want COUNTER", energy.GetType())
|
||||
}
|
||||
value := energy.Metric[0].GetCounter().GetValue()
|
||||
want := 4188430.0 / 60.0
|
||||
if math.Abs(value-want) > 0.000001 {
|
||||
t.Fatalf("energy counter = %f, want %f", value, want)
|
||||
}
|
||||
|
||||
assertGauge(t, families, "shelly_up", 1)
|
||||
assertGauge(t, families, "shelly_meter_power_watts", 166)
|
||||
assertGauge(t, families, "shelly_temperature_celsius", 49.4)
|
||||
assertGauge(t, families, "shelly_relay_on", 1)
|
||||
}
|
||||
|
||||
func TestPlugSCollectorReportsFailedScrapes(t *testing.T) {
|
||||
client := statusClientFunc(func(context.Context, any) error { return errors.New("unreachable") })
|
||||
deviceCollector, err := NewDeviceCollector(configuration.DeviceConfiguration{
|
||||
Name: "office", Product: "plug-s",
|
||||
}, client)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
registry := prometheus.NewRegistry()
|
||||
if err := registry.Register(deviceCollector); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for expectedErrors := 1.0; expectedErrors <= 2; expectedErrors++ {
|
||||
families, err := registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assertGauge(t, families, "shelly_up", 0)
|
||||
errorsFamily := findMetricFamily(t, families, "shelly_scrape_errors_total")
|
||||
if value := errorsFamily.Metric[0].GetCounter().GetValue(); value != expectedErrors {
|
||||
t.Fatalf("scrape errors = %f, want %f", value, expectedErrors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupAllowsMultipleDevicesWithTheSameMetrics(t *testing.T) {
|
||||
group := make(Group, 0, 2)
|
||||
for _, name := range []string{"office", "kitchen"} {
|
||||
deviceCollector, err := NewDeviceCollector(configuration.DeviceConfiguration{
|
||||
Name: name, Product: "plug_s",
|
||||
}, statusClientFunc(fixtureClient))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
group = append(group, deviceCollector)
|
||||
}
|
||||
registry := prometheus.NewPedanticRegistry()
|
||||
if err := registry.Register(group); err != nil {
|
||||
t.Fatalf("Register() returned an unexpected error: %v", err)
|
||||
}
|
||||
families, err := registry.Gather()
|
||||
if err != nil {
|
||||
t.Fatalf("Gather() returned an unexpected error: %v", err)
|
||||
}
|
||||
up := findMetricFamily(t, families, "shelly_up")
|
||||
if len(up.Metric) != 2 {
|
||||
t.Fatalf("shelly_up has %d metrics, want 2", len(up.Metric))
|
||||
}
|
||||
}
|
||||
|
||||
func findMetricFamily(t *testing.T, families []*dto.MetricFamily, name string) *dto.MetricFamily {
|
||||
t.Helper()
|
||||
for _, family := range families {
|
||||
if family.GetName() == name {
|
||||
return family
|
||||
}
|
||||
}
|
||||
t.Fatalf("metric family %q not found", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func assertGauge(t *testing.T, families []*dto.MetricFamily, name string, want float64) {
|
||||
t.Helper()
|
||||
family := findMetricFamily(t, families, name)
|
||||
if family.GetType() != dto.MetricType_GAUGE {
|
||||
t.Fatalf("%s type = %s, want GAUGE", name, family.GetType())
|
||||
}
|
||||
if value := family.Metric[0].GetGauge().GetValue(); value != want {
|
||||
t.Fatalf("%s = %f, want %f", name, value, want)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user