Files
Bose-SoundTouch/pkg/config/config_test.go
T
Tobias GesellchenandClaude Opus 4.7 feadc478d5 test: sweep example data in test files to RFC-5737 + placeholders
Mirrors the .md/.txt sweep across all tracked _test.go, testdata XML,
and .http integration files. Test files are self-contained (producer
+ assertion in the same file), so the matched-pair swap stays green
under `go test ./...`.

Mapping applied:
  192.168.178.[0-9]+   → 192.0.2.[same]
  192.168.1.[0-9]+     → 192.0.2.[same]
  Sound Machinechen    → Living Room SoundTouch
  A Sound Machine      → Kitchen SoundTouch
  A81B6A536A98 + case/separator variants → AABBCCDDEEFF (etc.)
  A81B6A849D99         → AABBCCDDEE01
  A81B6A849D88         → AABBCCDDEE03
  A81B6A536A09         → AABBCCDDEE04
  884AEAEEBD27         → AABBCCDDEE02
  3230304              → 1000001
  9569497              → 1000002

Two semantic fixes alongside the bulk swap:

- pkg/service/zeroconf/zeroconf_test.go: the "private 192" and
  "strips query" cases pin acceptance of RFC-1918 192.168/16. They
  must use a real 192.168 value; doc-range IPs would (correctly) be
  rejected by validateZcBaseURL. Switched to 192.168.10.10 — generic
  enough not to match any home LAN default, real enough for the
  validator. Added a comment explaining why this single test still
  carries a 192.168 literal.

- pkg/service/setup/setup_test.go: TestTestDNSRedirection mocks the
  device's `od -An -tu1` byte output, which is space-separated
  octets ("192 168 1 100"). My sed only matched the dot-separated
  form, so the mock was returning the old IP while the test
  assertions had moved to the doc range. Updated to " 192 0 2 100".

go build ./... clean. go test ./... clean (only TestDocsConsistency
remains failing, which is a pre-existing/untracked-file issue).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:05:13 +02:00

450 lines
11 KiB
Go

package config
import (
"os"
"testing"
"time"
)
func TestDefaultConfig(t *testing.T) {
config := DefaultConfig()
if config.DiscoveryTimeout != 5*time.Second {
t.Errorf("Expected discovery timeout 5s, got %v", config.DiscoveryTimeout)
}
if !config.UPnPEnabled {
t.Error("Expected UPnP to be enabled by default")
}
if !config.MDNSEnabled {
t.Error("Expected mDNS to be enabled by default")
}
if config.HTTPTimeout != 10*time.Second {
t.Errorf("Expected HTTP timeout 10s, got %v", config.HTTPTimeout)
}
if config.UserAgent != "Bose-SoundTouch-Go-Client/1.0" {
t.Errorf("Expected default user agent, got %s", config.UserAgent)
}
if !config.CacheEnabled {
t.Error("Expected cache to be enabled by default")
}
if config.CacheTTL != 30*time.Second {
t.Errorf("Expected cache TTL 30s, got %v", config.CacheTTL)
}
if len(config.PreferredDevices) != 0 {
t.Errorf("Expected no preferred devices by default, got %d", len(config.PreferredDevices))
}
}
func TestLoadFromEnv_NoEnvVars(t *testing.T) {
// Clear relevant environment variables
clearTestEnvVars()
config, err := LoadFromEnv()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
// Should have default values
if config.DiscoveryTimeout != 5*time.Second {
t.Errorf("Expected default discovery timeout, got %v", config.DiscoveryTimeout)
}
if !config.UPnPEnabled {
t.Error("Expected UPnP enabled by default")
}
}
func TestLoadFromEnv_WithEnvVars(t *testing.T) {
clearTestEnvVars()
// Set test environment variables
_ = os.Setenv("DISCOVERY_TIMEOUT", "15s")
_ = os.Setenv("UPNP_ENABLED", "false")
_ = os.Setenv("MDNS_ENABLED", "false")
_ = os.Setenv("HTTP_TIMEOUT", "20s")
_ = os.Setenv("USER_AGENT", "Test-Client/1.0")
_ = os.Setenv("CACHE_ENABLED", "false")
_ = os.Setenv("CACHE_TTL", "60s")
defer clearTestEnvVars()
config, err := LoadFromEnv()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if config.DiscoveryTimeout != 15*time.Second {
t.Errorf("Expected discovery timeout 15s, got %v", config.DiscoveryTimeout)
}
if config.UPnPEnabled {
t.Error("Expected UPnP to be disabled")
}
if config.MDNSEnabled {
t.Error("Expected mDNS to be disabled")
}
if config.HTTPTimeout != 20*time.Second {
t.Errorf("Expected HTTP timeout 20s, got %v", config.HTTPTimeout)
}
if config.UserAgent != "Test-Client/1.0" {
t.Errorf("Expected custom user agent, got %s", config.UserAgent)
}
if config.CacheEnabled {
t.Error("Expected cache to be disabled")
}
if config.CacheTTL != 60*time.Second {
t.Errorf("Expected cache TTL 60s, got %v", config.CacheTTL)
}
}
func TestParseDeviceString_HostOnly(t *testing.T) {
device, err := parseDeviceString("192.0.2.100")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if device.Host != "192.0.2.100" {
t.Errorf("Expected host '192.0.2.100', got '%s'", device.Host)
}
if device.Port != 8090 {
t.Errorf("Expected default port 8090, got %d", device.Port)
}
if device.Name != "SoundTouch-192.0.2.100" {
t.Errorf("Expected default name 'SoundTouch-192.0.2.100', got '%s'", device.Name)
}
}
func TestParseDeviceString_HostPort(t *testing.T) {
device, err := parseDeviceString("192.0.2.100:8091")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if device.Host != "192.0.2.100" {
t.Errorf("Expected host '192.0.2.100', got '%s'", device.Host)
}
if device.Port != 8091 {
t.Errorf("Expected port 8091, got %d", device.Port)
}
if device.Name != "SoundTouch-192.0.2.100" {
t.Errorf("Expected default name 'SoundTouch-192.0.2.100', got '%s'", device.Name)
}
}
func TestParseDeviceString_NameHostPort(t *testing.T) {
device, err := parseDeviceString("Living Room@192.0.2.100:8090")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if device.Host != "192.0.2.100" {
t.Errorf("Expected host '192.0.2.100', got '%s'", device.Host)
}
if device.Port != 8090 {
t.Errorf("Expected port 8090, got %d", device.Port)
}
if device.Name != "Living Room" {
t.Errorf("Expected name 'Living Room', got '%s'", device.Name)
}
}
func TestParseDeviceString_NameHost(t *testing.T) {
device, err := parseDeviceString("Kitchen Speaker@192.0.2.101")
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if device.Host != "192.0.2.101" {
t.Errorf("Expected host '192.0.2.101', got '%s'", device.Host)
}
if device.Port != 8090 {
t.Errorf("Expected default port 8090, got %d", device.Port)
}
if device.Name != "Kitchen Speaker" {
t.Errorf("Expected name 'Kitchen Speaker', got '%s'", device.Name)
}
}
func TestParseDeviceString_InvalidPort(t *testing.T) {
_, err := parseDeviceString("192.0.2.100:invalid")
if err == nil {
t.Error("Expected error for invalid port, got nil")
}
_, err = parseDeviceString("192.0.2.100:0")
if err == nil {
t.Error("Expected error for port 0, got nil")
}
_, err = parseDeviceString("192.0.2.100:70000")
if err == nil {
t.Error("Expected error for port > 65535, got nil")
}
}
func TestParseDeviceString_EmptyHost(t *testing.T) {
_, err := parseDeviceString("")
if err == nil {
t.Error("Expected error for empty string, got nil")
}
_, err = parseDeviceString("@")
if err == nil {
t.Error("Expected error for empty host with @, got nil")
}
}
func TestParsePreferredDevices_SingleDevice(t *testing.T) {
clearTestEnvVars()
_ = os.Setenv("PREFERRED_DEVICES", "192.0.2.100")
defer clearTestEnvVars()
devices, err := parsePreferredDevices()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(devices) != 1 {
t.Errorf("Expected 1 device, got %d", len(devices))
}
if devices[0].Host != "192.0.2.100" {
t.Errorf("Expected host '192.0.2.100', got '%s'", devices[0].Host)
}
}
func TestParsePreferredDevices_MultipleDevices(t *testing.T) {
clearTestEnvVars()
_ = os.Setenv("PREFERRED_DEVICES", "Living Room@192.0.2.100:8090;Kitchen@192.0.2.101;192.0.2.102:8091")
defer clearTestEnvVars()
devices, err := parsePreferredDevices()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(devices) != 3 {
t.Errorf("Expected 3 devices, got %d", len(devices))
}
// Check first device
if devices[0].Name != "Living Room" {
t.Errorf("Expected first device name 'Living Room', got '%s'", devices[0].Name)
}
if devices[0].Host != "192.0.2.100" {
t.Errorf("Expected first device host '192.0.2.100', got '%s'", devices[0].Host)
}
if devices[0].Port != 8090 {
t.Errorf("Expected first device port 8090, got %d", devices[0].Port)
}
// Check second device
if devices[1].Name != "Kitchen" {
t.Errorf("Expected second device name 'Kitchen', got '%s'", devices[1].Name)
}
if devices[1].Host != "192.0.2.101" {
t.Errorf("Expected second device host '192.0.2.101', got '%s'", devices[1].Host)
}
if devices[1].Port != 8090 {
t.Errorf("Expected second device port 8090, got %d", devices[1].Port)
}
// Check third device
if devices[2].Name != "SoundTouch-192.0.2.102" {
t.Errorf("Expected third device default name, got '%s'", devices[2].Name)
}
if devices[2].Host != "192.0.2.102" {
t.Errorf("Expected third device host '192.0.2.102', got '%s'", devices[2].Host)
}
if devices[2].Port != 8091 {
t.Errorf("Expected third device port 8091, got %d", devices[2].Port)
}
}
func TestParsePreferredDevices_EmptyString(t *testing.T) {
clearTestEnvVars()
_ = os.Setenv("PREFERRED_DEVICES", "")
defer clearTestEnvVars()
devices, err := parsePreferredDevices()
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if len(devices) != 0 {
t.Errorf("Expected 0 devices for empty string, got %d", len(devices))
}
}
func TestParsePreferredDevices_InvalidDevice(t *testing.T) {
clearTestEnvVars()
_ = os.Setenv("PREFERRED_DEVICES", "192.0.2.100:invalid")
defer clearTestEnvVars()
_, err := parsePreferredDevices()
if err == nil {
t.Error("Expected error for invalid device configuration, got nil")
}
}
func TestGetPreferredDevicesAsDiscovered(t *testing.T) {
config := &Config{
PreferredDevices: []DeviceConfig{
{Name: "Living Room", Host: "192.0.2.100", Port: 8090},
{Name: "Kitchen", Host: "192.0.2.101", Port: 8091},
},
}
devices := config.GetPreferredDevicesAsDiscovered()
if len(devices) != 2 {
t.Errorf("Expected 2 discovered devices, got %d", len(devices))
}
// Check first device
if devices[0].Name != "Living Room" {
t.Errorf("Expected name 'Living Room', got '%s'", devices[0].Name)
}
if devices[0].Host != "192.0.2.100" {
t.Errorf("Expected host '192.0.2.100', got '%s'", devices[0].Host)
}
if devices[0].Port != 8090 {
t.Errorf("Expected port 8090, got %d", devices[0].Port)
}
expectedInfoURL := "http://192.0.2.100:8090/info"
if devices[0].InfoURL != expectedInfoURL {
t.Errorf("Expected info URL '%s', got '%s'", expectedInfoURL, devices[0].InfoURL)
}
}
func TestValidate_ValidConfig(t *testing.T) {
config := &Config{
DiscoveryTimeout: 5 * time.Second,
HTTPTimeout: 10 * time.Second,
CacheTTL: 30 * time.Second,
PreferredDevices: []DeviceConfig{
{Name: "Test", Host: "192.0.2.100", Port: 8090},
},
}
err := config.Validate()
if err != nil {
t.Errorf("Expected valid config, got error: %v", err)
}
}
func TestValidate_InvalidTimeouts(t *testing.T) {
config := &Config{
DiscoveryTimeout: 0,
HTTPTimeout: 10 * time.Second,
CacheTTL: 30 * time.Second,
}
err := config.Validate()
if err == nil {
t.Error("Expected error for zero discovery timeout, got nil")
}
config.DiscoveryTimeout = 5 * time.Second
config.HTTPTimeout = 0
err = config.Validate()
if err == nil {
t.Error("Expected error for zero HTTP timeout, got nil")
}
config.HTTPTimeout = 10 * time.Second
config.CacheTTL = 0
err = config.Validate()
if err == nil {
t.Error("Expected error for zero cache TTL, got nil")
}
}
func TestValidate_InvalidDevices(t *testing.T) {
config := &Config{
DiscoveryTimeout: 5 * time.Second,
HTTPTimeout: 10 * time.Second,
CacheTTL: 30 * time.Second,
PreferredDevices: []DeviceConfig{
{Name: "Test", Host: "", Port: 8090}, // Empty host
},
}
err := config.Validate()
if err == nil {
t.Error("Expected error for empty host, got nil")
}
config.PreferredDevices[0].Host = "192.0.2.100"
config.PreferredDevices[0].Port = 0 // Invalid port
err = config.Validate()
if err == nil {
t.Error("Expected error for invalid port, got nil")
}
config.PreferredDevices[0].Port = 70000 // Port too high
err = config.Validate()
if err == nil {
t.Error("Expected error for port > 65535, got nil")
}
}
// Helper function to clear test environment variables
func clearTestEnvVars() {
envVars := []string{
"DISCOVERY_TIMEOUT",
"UPNP_ENABLED",
"MDNS_ENABLED",
"HTTP_TIMEOUT",
"USER_AGENT",
"CACHE_ENABLED",
"CACHE_TTL",
"PREFERRED_DEVICES",
}
for _, env := range envVars {
_ = os.Unsetenv(env)
}
}