115 lines
3.5 KiB
Go
115 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/kkyr/fig"
|
|
)
|
|
|
|
// TestExampleConfigValidates ensures the shipped config.example.yaml parses and
|
|
// passes cross-field validation, so the documented options stay in sync with the
|
|
// config structs.
|
|
func TestExampleConfigValidates(t *testing.T) {
|
|
cfg := &Config{Log: &LogConfig{}}
|
|
if err := fig.Load(cfg, fig.File("config.example.yaml"), fig.Dirs(".")); err != nil {
|
|
t.Fatalf("parse config.example.yaml: %s", err)
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
t.Fatalf("validate config.example.yaml: %s", err)
|
|
}
|
|
}
|
|
|
|
// baseConfig returns a minimal valid configuration with one serial device and an
|
|
// MQTT source, ready for per-test mutation.
|
|
func baseConfig() *Config {
|
|
return &Config{
|
|
Devices: []*DeviceConfig{{
|
|
Name: "grafik-eye",
|
|
Transport: "serial",
|
|
Serial: SerialConfig{Device: "/dev/ttyUSB0"},
|
|
Zones: 6,
|
|
}},
|
|
Sources: []*SourceConfig{{
|
|
Name: "home-assistant",
|
|
Type: "mqtt",
|
|
Device: "grafik-eye",
|
|
MQTT: MQTTConfig{Broker: "127.0.0.1", Topic: "lutron/qse-nwk"},
|
|
}},
|
|
}
|
|
}
|
|
|
|
// TestValidateMQTTLights verifies a light controlling zones 1-6 validates and that
|
|
// a zone outside the device's range is rejected.
|
|
func TestValidateMQTTLights(t *testing.T) {
|
|
c := baseConfig()
|
|
c.Sources[0].MQTT.Lights = []MQTTLightConfig{
|
|
{Name: "All Zones", Topic: "lutron/qse-nwk/all", Zones: []int{1, 2, 3, 4, 5, 6}},
|
|
}
|
|
if err := c.Validate(); err != nil {
|
|
t.Fatalf("valid lights config rejected: %s", err)
|
|
}
|
|
|
|
// A zone beyond the device's 6 zones must be rejected.
|
|
c.Sources[0].MQTT.Lights[0].Zones = []int{6, 7}
|
|
if err := c.Validate(); err == nil {
|
|
t.Error("expected error for out-of-range zone")
|
|
}
|
|
}
|
|
|
|
// TestValidateMQTTLightRequirements verifies per-light topic, zone-set, and
|
|
// duplicate-topic constraints.
|
|
func TestValidateMQTTLightRequirements(t *testing.T) {
|
|
// A light without a topic is rejected.
|
|
c := baseConfig()
|
|
c.Sources[0].MQTT.Lights = []MQTTLightConfig{{Zones: []int{1}}}
|
|
if err := c.Validate(); err == nil {
|
|
t.Error("expected error for missing light topic")
|
|
}
|
|
|
|
// A light with no zones is rejected.
|
|
c = baseConfig()
|
|
c.Sources[0].MQTT.Lights = []MQTTLightConfig{{Topic: "lutron/a"}}
|
|
if err := c.Validate(); err == nil {
|
|
t.Error("expected error for light with no zones")
|
|
}
|
|
|
|
// Two lights sharing a topic are rejected.
|
|
c = baseConfig()
|
|
c.Sources[0].MQTT.Lights = []MQTTLightConfig{
|
|
{Topic: "lutron/a", Zones: []int{1}},
|
|
{Topic: "lutron/a", Zones: []int{2}},
|
|
}
|
|
if err := c.Validate(); err == nil {
|
|
t.Error("expected error for duplicate light topic")
|
|
}
|
|
}
|
|
|
|
// TestNewMQTTSourceSynthesizesLight verifies that, with no lights configured, a
|
|
// single light over every zone is synthesized from the base topic.
|
|
func TestNewMQTTSourceSynthesizesLight(t *testing.T) {
|
|
cfg := &SourceConfig{
|
|
Name: "home-assistant",
|
|
Type: "mqtt",
|
|
MQTT: MQTTConfig{Broker: "127.0.0.1", Topic: "lutron/qse-nwk", DeviceName: "Lutron"},
|
|
}
|
|
dev := NewDevice(&DeviceConfig{Name: "grafik-eye", Zones: 6})
|
|
binding := dev.RegisterSource(cfg.Name, cfg.Priority, 0, cfg.Fade)
|
|
|
|
s := newMQTTSource(cfg, dev, binding)
|
|
if len(s.lights) != 1 {
|
|
t.Fatalf("expected 1 synthesized light, got %d", len(s.lights))
|
|
}
|
|
l := s.lights[0]
|
|
if l.topic != "lutron/qse-nwk" || l.topicSet != "lutron/qse-nwk/set" {
|
|
t.Errorf("unexpected light topics: %q / %q", l.topic, l.topicSet)
|
|
}
|
|
want := []int{1, 2, 3, 4, 5, 6}
|
|
if len(l.zones) != len(want) {
|
|
t.Fatalf("zones = %v, want %v", l.zones, want)
|
|
}
|
|
for i, z := range want {
|
|
if l.zones[i] != z {
|
|
t.Errorf("zones[%d] = %d, want %d", i, l.zones[i], z)
|
|
}
|
|
}
|
|
}
|