mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 16:46:17 +00:00
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>
612 lines
15 KiB
Go
612 lines
15 KiB
Go
package client
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
"testing"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
)
|
|
|
|
func TestClient_GetClockTime(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
serverResponse string
|
|
statusCode int
|
|
expectError bool
|
|
expectedUTC int64
|
|
expectedZone string
|
|
}{
|
|
{
|
|
name: "Successful clock time retrieval",
|
|
serverResponse: `<clockTime zone="UTC" utc="1609459200">2021-01-01 00:00:00</clockTime>`,
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
expectedUTC: 1609459200,
|
|
expectedZone: "UTC",
|
|
},
|
|
{
|
|
name: "Clock time with timezone",
|
|
serverResponse: `<clockTime zone="America/New_York" utc="1609459200">2021-01-01 00:00:00</clockTime>`,
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
expectedUTC: 1609459200,
|
|
expectedZone: "America/New_York",
|
|
},
|
|
{
|
|
name: "Server error",
|
|
serverResponse: `<error>Internal server error</error>`,
|
|
statusCode: http.StatusInternalServerError,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockTime" {
|
|
t.Errorf("Expected path '/clockTime', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "GET" {
|
|
t.Errorf("Expected GET method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(tt.statusCode)
|
|
_, _ = w.Write([]byte(tt.serverResponse))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
clockTime, err := client.GetClockTime()
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
return
|
|
}
|
|
|
|
if clockTime.GetUTC() != tt.expectedUTC {
|
|
t.Errorf("Expected UTC %d, got %d", tt.expectedUTC, clockTime.GetUTC())
|
|
}
|
|
|
|
if clockTime.GetZone() != tt.expectedZone {
|
|
t.Errorf("Expected Zone %q, got %q", tt.expectedZone, clockTime.GetZone())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_SetClockTime(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
request *models.ClockTimeRequest
|
|
statusCode int
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Successful clock time set",
|
|
request: &models.ClockTimeRequest{
|
|
UTCTime: 1609459200,
|
|
},
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Invalid request",
|
|
request: &models.ClockTimeRequest{
|
|
// Empty request - should fail validation
|
|
},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Server error",
|
|
request: &models.ClockTimeRequest{
|
|
UTCTime: 1609459200,
|
|
},
|
|
statusCode: http.StatusInternalServerError,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockTime" {
|
|
t.Errorf("Expected path '/clockTime', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "POST" {
|
|
t.Errorf("Expected POST method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(tt.statusCode)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
err := client.SetClockTime(tt.request)
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_SetClockTimeNow(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockTime" {
|
|
t.Errorf("Expected path '/clockTime', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "POST" {
|
|
t.Errorf("Expected POST method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
|
|
err := client.SetClockTimeNow()
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestClient_GetClockDisplay(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
serverResponse string
|
|
statusCode int
|
|
expectError bool
|
|
expectedEnabled bool
|
|
expectedFormat string
|
|
expectedBrightness int
|
|
}{
|
|
{
|
|
name: "Successful clock display retrieval",
|
|
serverResponse: `<clockDisplay deviceID="ABCD1234EFGH" enabled="true" format="24" brightness="75" autoDim="true"></clockDisplay>`,
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
expectedEnabled: true,
|
|
expectedFormat: "24",
|
|
expectedBrightness: 75,
|
|
},
|
|
{
|
|
name: "Disabled clock display",
|
|
serverResponse: `<clockDisplay enabled="false" format="12" brightness="0"></clockDisplay>`,
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
expectedEnabled: false,
|
|
expectedFormat: "12",
|
|
expectedBrightness: 0,
|
|
},
|
|
{
|
|
name: "Server error",
|
|
serverResponse: `<error>Internal server error</error>`,
|
|
statusCode: http.StatusInternalServerError,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockDisplay" {
|
|
t.Errorf("Expected path '/clockDisplay', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "GET" {
|
|
t.Errorf("Expected GET method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(tt.statusCode)
|
|
_, _ = w.Write([]byte(tt.serverResponse))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
clockDisplay, err := client.GetClockDisplay()
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
return
|
|
}
|
|
|
|
if clockDisplay.IsEnabled() != tt.expectedEnabled {
|
|
t.Errorf("Expected Enabled %v, got %v", tt.expectedEnabled, clockDisplay.IsEnabled())
|
|
}
|
|
|
|
if clockDisplay.GetFormat() != tt.expectedFormat {
|
|
t.Errorf("Expected Format %q, got %q", tt.expectedFormat, clockDisplay.GetFormat())
|
|
}
|
|
|
|
if clockDisplay.GetBrightness() != tt.expectedBrightness {
|
|
t.Errorf("Expected Brightness %d, got %d", tt.expectedBrightness, clockDisplay.GetBrightness())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_SetClockDisplay(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
request *models.ClockDisplayRequest
|
|
statusCode int
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Successful clock display set",
|
|
request: models.NewClockDisplayRequest().
|
|
SetEnabled(true).
|
|
SetFormat(models.ClockFormat24Hour).
|
|
SetBrightness(75),
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Invalid brightness",
|
|
request: models.NewClockDisplayRequest().
|
|
SetBrightness(150), // Invalid brightness > 100
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "No changes",
|
|
request: models.NewClockDisplayRequest(),
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "Server error",
|
|
request: models.NewClockDisplayRequest().
|
|
SetEnabled(true),
|
|
statusCode: http.StatusInternalServerError,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.expectError && tt.statusCode == 0 {
|
|
// For client-side validation errors, we don't need a server
|
|
client := createTestClient("http://localhost:8080")
|
|
|
|
err := client.SetClockDisplay(tt.request)
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockDisplay" {
|
|
t.Errorf("Expected path '/clockDisplay', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "POST" {
|
|
t.Errorf("Expected POST method, got '%s'", r.Method)
|
|
}
|
|
|
|
if tt.statusCode != 0 {
|
|
w.WriteHeader(tt.statusCode)
|
|
} else {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
err := client.SetClockDisplay(tt.request)
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_EnableDisableClockDisplay(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
method func(*Client) error
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "Enable clock display",
|
|
method: func(c *Client) error {
|
|
return c.EnableClockDisplay()
|
|
},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "Disable clock display",
|
|
method: func(c *Client) error {
|
|
return c.DisableClockDisplay()
|
|
},
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
err := tt.method(client)
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_SetClockDisplayBrightness(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockDisplay" {
|
|
t.Errorf("Expected path '/clockDisplay', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "POST" {
|
|
t.Errorf("Expected POST method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
|
|
err := client.SetClockDisplayBrightness(75)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestClient_SetClockDisplayFormat(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
format models.ClockFormat
|
|
}{
|
|
{
|
|
name: "12-hour format",
|
|
format: models.ClockFormat12Hour,
|
|
},
|
|
{
|
|
name: "24-hour format",
|
|
format: models.ClockFormat24Hour,
|
|
},
|
|
{
|
|
name: "Auto format",
|
|
format: models.ClockFormatAuto,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/clockDisplay" {
|
|
t.Errorf("Expected path '/clockDisplay', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "POST" {
|
|
t.Errorf("Expected POST method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
|
|
err := client.SetClockDisplayFormat(tt.format)
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_GetNetworkInfo(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
serverResponse string
|
|
statusCode int
|
|
expectError bool
|
|
expectedDeviceID string
|
|
expectedInterfaceCount int
|
|
}{
|
|
{
|
|
name: "Successful network info retrieval - WiFi device",
|
|
serverResponse: `<networkInfo wifiProfileCount="2">
|
|
<interfaces>
|
|
<interface type="WIFI_INTERFACE" name="wlan0" macAddress="AA:BB:CC:DD:EE:FF" ipAddress="192.0.2.10" ssid="MyHomeNetwork" frequencyKHz="5500000" state="NETWORK_WIFI_CONNECTED" signal="EXCELLENT_SIGNAL" mode="STATION"/>
|
|
<interface type="WIFI_INTERFACE" name="wlan1" macAddress="AA:BB:CC:DD:EE:01" state="NETWORK_WIFI_DISCONNECTED"/>
|
|
</interfaces>
|
|
</networkInfo>`,
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
expectedDeviceID: "",
|
|
expectedInterfaceCount: 2,
|
|
},
|
|
{
|
|
name: "Ethernet device",
|
|
serverResponse: `<networkInfo wifiProfileCount="3">
|
|
<interfaces>
|
|
<interface type="ETHERNET_INTERFACE" name="eth0" macAddress="AA:BB:CC:DD:EE:FF" ipAddress="192.0.2.10" state="NETWORK_ETHERNET_CONNECTED"/>
|
|
</interfaces>
|
|
</networkInfo>`,
|
|
statusCode: http.StatusOK,
|
|
expectError: false,
|
|
expectedDeviceID: "",
|
|
expectedInterfaceCount: 1,
|
|
},
|
|
{
|
|
name: "Server error",
|
|
serverResponse: `<error>Internal server error</error>`,
|
|
statusCode: http.StatusInternalServerError,
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/networkInfo" {
|
|
t.Errorf("Expected path '/networkInfo', got '%s'", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "GET" {
|
|
t.Errorf("Expected GET method, got '%s'", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(tt.statusCode)
|
|
_, _ = w.Write([]byte(tt.serverResponse))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
networkInfo, err := client.GetNetworkInfo()
|
|
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("Expected error, got none")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Unexpected error: %v", err)
|
|
return
|
|
}
|
|
|
|
if tt.expectedDeviceID != "" && networkInfo.GetWifiProfileCount() == 0 {
|
|
t.Errorf("Expected non-zero wifi profile count for test %q", tt.name)
|
|
}
|
|
|
|
if len(networkInfo.GetInterfaces()) != tt.expectedInterfaceCount {
|
|
t.Errorf("Expected %d interfaces, got %d", tt.expectedInterfaceCount, len(networkInfo.GetInterfaces()))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_SystemEndpoints_Integration(t *testing.T) {
|
|
// This test ensures all system endpoints work together
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/clockTime":
|
|
switch r.Method {
|
|
case "GET":
|
|
_, _ = w.Write([]byte(`<clockTime zone="UTC" utc="1609459200">2021-01-01 00:00:00</clockTime>`))
|
|
case "POST":
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
case "/clockDisplay":
|
|
switch r.Method {
|
|
case "GET":
|
|
_, _ = w.Write([]byte(`<clockDisplay enabled="true" format="24" brightness="75"></clockDisplay>`))
|
|
case "POST":
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
case "/networkInfo":
|
|
_, _ = w.Write([]byte(`<networkInfo wifiProfileCount="2">
|
|
<interfaces>
|
|
<interface type="WIFI_INTERFACE" name="wlan0" macAddress="AA:BB:CC:DD:EE:FF" ipAddress="192.0.2.10" ssid="TestNetwork" frequencyKHz="5500000" state="NETWORK_WIFI_CONNECTED" signal="EXCELLENT_SIGNAL" mode="STATION"/>
|
|
</interfaces>
|
|
</networkInfo>`))
|
|
default:
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
|
|
// Test clock time operations
|
|
clockTime, err := client.GetClockTime()
|
|
if err != nil {
|
|
t.Errorf("GetClockTime failed: %v", err)
|
|
} else if clockTime.GetUTC() != 1609459200 {
|
|
t.Errorf("Expected UTC 1609459200, got %d", clockTime.GetUTC())
|
|
}
|
|
|
|
// Test setting clock time to now
|
|
err = client.SetClockTimeNow()
|
|
if err != nil {
|
|
t.Errorf("SetClockTimeNow failed: %v", err)
|
|
}
|
|
|
|
// Test clock display operations
|
|
clockDisplay, err := client.GetClockDisplay()
|
|
if err != nil {
|
|
t.Errorf("GetClockDisplay failed: %v", err)
|
|
} else if !clockDisplay.IsEnabled() {
|
|
t.Error("Expected clock display to be enabled")
|
|
}
|
|
|
|
// Test enabling/disabling clock display
|
|
err = client.EnableClockDisplay()
|
|
if err != nil {
|
|
t.Errorf("EnableClockDisplay failed: %v", err)
|
|
}
|
|
|
|
err = client.DisableClockDisplay()
|
|
if err != nil {
|
|
t.Errorf("DisableClockDisplay failed: %v", err)
|
|
}
|
|
|
|
// Test network info
|
|
networkInfo, err := client.GetNetworkInfo()
|
|
if err != nil {
|
|
t.Errorf("GetNetworkInfo failed: %v", err)
|
|
} else if networkInfo.GetWifiProfileCount() != 2 {
|
|
t.Errorf("Expected wifi profile count 2, got %d", networkInfo.GetWifiProfileCount())
|
|
}
|
|
}
|