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>
236 lines
6.8 KiB
Go
236 lines
6.8 KiB
Go
package datastore
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
)
|
|
|
|
func TestMacAddressSerialization(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "mac-serialization-test-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
ds := NewDataStore(tempDir)
|
|
account := "1000001"
|
|
device := "I6332527703739342000020"
|
|
macAddress := "AABBCCDDEEFF"
|
|
|
|
// Create device info with MAC address
|
|
info := &models.ServiceDeviceInfo{
|
|
DeviceID: device,
|
|
Name: "Test SoundTouch",
|
|
ProductCode: "SoundTouch 10",
|
|
IPAddress: "192.0.2.100",
|
|
MacAddress: macAddress,
|
|
DeviceSerialNumber: device,
|
|
ProductSerialNumber: "PROD123456",
|
|
FirmwareVersion: "4.8.1.23456",
|
|
DiscoveryMethod: "UPnP",
|
|
}
|
|
|
|
// Save device info
|
|
err = ds.SaveDeviceInfo(account, device, info)
|
|
if err != nil {
|
|
t.Fatalf("SaveDeviceInfo failed: %v", err)
|
|
}
|
|
|
|
// Verify the XML file was created
|
|
deviceInfoPath := filepath.Join(ds.AccountDeviceDir(account, device), "DeviceInfo.xml")
|
|
if _, err := os.Stat(deviceInfoPath); err != nil {
|
|
t.Fatalf("DeviceInfo.xml not created: %v", err)
|
|
}
|
|
|
|
// Read back the device info
|
|
loadedInfo, err := ds.GetDeviceInfo(account, device)
|
|
if err != nil {
|
|
t.Fatalf("GetDeviceInfo failed: %v", err)
|
|
}
|
|
|
|
// Verify MAC address is preserved
|
|
if loadedInfo.MacAddress != macAddress {
|
|
t.Errorf("MAC address not preserved. Expected: '%s', Got: '%s'", macAddress, loadedInfo.MacAddress)
|
|
}
|
|
|
|
// Verify other fields are also correct
|
|
if loadedInfo.DeviceID != device {
|
|
t.Errorf("DeviceID mismatch. Expected: %s, Got: %s", device, loadedInfo.DeviceID)
|
|
}
|
|
|
|
if loadedInfo.IPAddress != "192.0.2.100" {
|
|
t.Errorf("IPAddress mismatch. Expected: 192.0.2.100, Got: %s", loadedInfo.IPAddress)
|
|
}
|
|
|
|
// Initialize datastore to populate MAC mappings
|
|
err = ds.Initialize()
|
|
if err != nil {
|
|
t.Fatalf("Initialize failed: %v", err)
|
|
}
|
|
|
|
// Test that MAC address mapping works
|
|
resolvedPath := ds.AccountDeviceDir(account, macAddress)
|
|
expectedPath := ds.AccountDeviceDir(account, device)
|
|
|
|
if resolvedPath != expectedPath {
|
|
t.Errorf("MAC address mapping failed. MAC '%s' resolved to '%s', expected '%s'",
|
|
macAddress, resolvedPath, expectedPath)
|
|
}
|
|
|
|
// Test that Sources.xml path resolves correctly via MAC address
|
|
// (We don't need to actually read the file, just verify the path resolution works)
|
|
macPath := ds.AccountDeviceDir(account, macAddress)
|
|
devicePath := ds.AccountDeviceDir(account, device)
|
|
|
|
if macPath != devicePath {
|
|
t.Errorf("MAC address path resolution failed. MAC path: %s, Device path: %s", macPath, devicePath)
|
|
}
|
|
|
|
t.Logf("✅ MAC address serialization working correctly")
|
|
t.Logf(" - MAC address '%s' saved to DeviceInfo.xml", macAddress)
|
|
t.Logf(" - MAC address '%s' loaded from DeviceInfo.xml", loadedInfo.MacAddress)
|
|
t.Logf(" - MAC mapping: '%s' -> '%s'", macAddress, device)
|
|
t.Logf(" - Sources.xml accessible via MAC address")
|
|
}
|
|
|
|
func TestMacAddressSerializationEdgeCases(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "mac-edge-cases-test-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
ds := NewDataStore(tempDir)
|
|
account := "testaccount"
|
|
device := "testdevice"
|
|
|
|
testCases := []struct {
|
|
name string
|
|
macAddress string
|
|
expected string
|
|
}{
|
|
{"uppercase", "AABBCCDDEEFF", "AABBCCDDEEFF"},
|
|
{"lowercase", "aabbccddeeff", "aabbccddeeff"},
|
|
{"with_colons", "AA:BB:CC:DD:EE:FF", "AA:BB:CC:DD:EE:FF"},
|
|
{"with_dashes", "AA-BB-CC-DD-EE-FF", "AA-BB-CC-DD-EE-FF"},
|
|
{"empty", "", ""},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
deviceID := device + "_" + tc.name
|
|
|
|
info := &models.ServiceDeviceInfo{
|
|
DeviceID: deviceID,
|
|
Name: "Test Device " + tc.name,
|
|
ProductCode: "SoundTouch 10",
|
|
IPAddress: "192.0.2.100",
|
|
MacAddress: tc.macAddress,
|
|
DeviceSerialNumber: deviceID,
|
|
}
|
|
|
|
// Save and load
|
|
err := ds.SaveDeviceInfo(account, deviceID, info)
|
|
if err != nil {
|
|
t.Fatalf("SaveDeviceInfo failed for %s: %v", tc.name, err)
|
|
}
|
|
|
|
loadedInfo, err := ds.GetDeviceInfo(account, deviceID)
|
|
if err != nil {
|
|
t.Fatalf("GetDeviceInfo failed for %s: %v", tc.name, err)
|
|
}
|
|
|
|
if loadedInfo.MacAddress != tc.expected {
|
|
t.Errorf("MAC address mismatch for %s. Expected: '%s', Got: '%s'",
|
|
tc.name, tc.expected, loadedInfo.MacAddress)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExistingDeviceInfoUpdate(t *testing.T) {
|
|
tempDir, err := os.MkdirTemp("", "device-update-test-*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tempDir)
|
|
|
|
ds := NewDataStore(tempDir)
|
|
account := "1000001"
|
|
device := "I6332527703739342000020"
|
|
|
|
// First save without MAC address (simulating old DeviceInfo.xml)
|
|
infoWithoutMAC := &models.ServiceDeviceInfo{
|
|
DeviceID: device,
|
|
Name: "Test SoundTouch",
|
|
ProductCode: "SoundTouch 10",
|
|
IPAddress: "192.0.2.100",
|
|
MacAddress: "", // No MAC address initially
|
|
DeviceSerialNumber: device,
|
|
}
|
|
|
|
err = ds.SaveDeviceInfo(account, device, infoWithoutMAC)
|
|
if err != nil {
|
|
t.Fatalf("Initial SaveDeviceInfo failed: %v", err)
|
|
}
|
|
|
|
// Verify no MAC address initially
|
|
loadedInfo1, err := ds.GetDeviceInfo(account, device)
|
|
if err != nil {
|
|
t.Fatalf("Initial GetDeviceInfo failed: %v", err)
|
|
}
|
|
|
|
if loadedInfo1.MacAddress != "" {
|
|
t.Errorf("Expected empty MAC address, got '%s'", loadedInfo1.MacAddress)
|
|
}
|
|
|
|
// Now update with MAC address (simulating discovery update)
|
|
macAddress := "AABBCCDDEEFF"
|
|
infoWithMAC := &models.ServiceDeviceInfo{
|
|
DeviceID: device,
|
|
Name: "Test SoundTouch",
|
|
ProductCode: "SoundTouch 10",
|
|
IPAddress: "192.0.2.100",
|
|
MacAddress: macAddress,
|
|
DeviceSerialNumber: device,
|
|
}
|
|
|
|
err = ds.SaveDeviceInfo(account, device, infoWithMAC)
|
|
if err != nil {
|
|
t.Fatalf("Update SaveDeviceInfo failed: %v", err)
|
|
}
|
|
|
|
// Verify MAC address is now present
|
|
loadedInfo2, err := ds.GetDeviceInfo(account, device)
|
|
if err != nil {
|
|
t.Fatalf("Updated GetDeviceInfo failed: %v", err)
|
|
}
|
|
|
|
if loadedInfo2.MacAddress != macAddress {
|
|
t.Errorf("MAC address not updated. Expected: '%s', Got: '%s'", macAddress, loadedInfo2.MacAddress)
|
|
}
|
|
|
|
// Initialize to test mapping
|
|
err = ds.Initialize()
|
|
if err != nil {
|
|
t.Fatalf("Initialize failed: %v", err)
|
|
}
|
|
|
|
// Test that MAC mapping now works
|
|
resolvedPath := ds.AccountDeviceDir(account, macAddress)
|
|
expectedPath := ds.AccountDeviceDir(account, device)
|
|
|
|
if resolvedPath != expectedPath {
|
|
t.Errorf("MAC mapping failed after update. MAC '%s' resolved to '%s', expected '%s'",
|
|
macAddress, resolvedPath, expectedPath)
|
|
}
|
|
|
|
t.Logf("✅ DeviceInfo.xml update with MAC address working correctly")
|
|
t.Logf(" - Initial: no MAC address")
|
|
t.Logf(" - Updated: MAC address '%s' added", macAddress)
|
|
t.Logf(" - Mapping: '%s' -> '%s'", macAddress, device)
|
|
}
|