mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
test(handlers): add cross-account migration test for handleDiscoveredDevice
Exercises the branch in handleDiscoveredDevice where a device's live
MargeAccountUUID differs from its stored account. The test:
- seeds a device + presets under 'default'
- mocks /info to report a different margeAccountUUID ('8637922')
- calls handleDiscoveredDevice
- asserts the device is now stored under the new account with the live name
- asserts the old 'default' entry is gone
- asserts presets survived the MoveDevice rename
- asserts ListAllDevices returns exactly one entry (no duplicates)
Closes the server-level gap noted during PR #348 review.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
439b2cb9fc
commit
109b9afa0c
@@ -362,6 +362,107 @@ func TestMACBasedDeviceDiscovery_MigrationScenario(t *testing.T) {
|
||||
t.Logf("\n✅ MAC-based device migration test completed!")
|
||||
}
|
||||
|
||||
// TestHandleDiscoveredDevice_CrossAccountMigration verifies the branch in
|
||||
// handleDiscoveredDevice that fires when a device's live MargeAccountUUID
|
||||
// differs from its stored account. The device directory must be moved to the
|
||||
// new account and the old entry must not appear in ListAllDevices.
|
||||
func TestHandleDiscoveredDevice_CrossAccountMigration(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
ds := datastore.NewDataStore(tempDir)
|
||||
|
||||
const (
|
||||
deviceID = "F4E11E930BEB"
|
||||
oldAccount = "default"
|
||||
newAccount = "8637922"
|
||||
)
|
||||
|
||||
// 1. Seed device under the old account with presets so we can verify
|
||||
// they survive the move.
|
||||
oldInfo := &models.ServiceDeviceInfo{
|
||||
DeviceID: deviceID,
|
||||
AccountID: oldAccount,
|
||||
Name: "Stale Name",
|
||||
IPAddress: "192.0.2.42",
|
||||
}
|
||||
if err := ds.SaveDeviceInfo(oldAccount, deviceID, oldInfo); err != nil {
|
||||
t.Fatalf("SaveDeviceInfo under %s: %v", oldAccount, err)
|
||||
}
|
||||
presets := []models.ServicePreset{
|
||||
{ID: "1", ServiceContentItem: models.ServiceContentItem{Name: "My Station", ContentItemType: "stationurl"}},
|
||||
}
|
||||
if err := ds.SavePresets(oldAccount, deviceID, presets); err != nil {
|
||||
t.Fatalf("SavePresets under %s: %v", oldAccount, err)
|
||||
}
|
||||
|
||||
// 2. Mock the speaker's /info endpoint — reports the new margeAccountUUID.
|
||||
deviceInfoXML := fmt.Sprintf(`<info deviceID="%s">
|
||||
<name>Updated Name</name>
|
||||
<type>SoundTouch 10</type>
|
||||
<margeAccountUUID>%s</margeAccountUUID>
|
||||
<networkInfo type="SCM">
|
||||
<macAddress>%s</macAddress>
|
||||
<ipAddress>192.0.2.42</ipAddress>
|
||||
</networkInfo>
|
||||
<moduleType>sm2</moduleType>
|
||||
</info>`, deviceID, newAccount, deviceID)
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/info" {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
fmt.Fprint(w, deviceInfoXML)
|
||||
} else {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
deviceIP := server.URL[len("http://"):]
|
||||
sm := setup.NewManager(server.URL, ds, nil)
|
||||
srv := NewServer(ds, sm, "http://localhost", false, false, false)
|
||||
|
||||
// 3. Trigger discovery — the live MargeAccountUUID differs from storedAccount.
|
||||
srv.handleDiscoveredDevice(models.DiscoveredDevice{
|
||||
Host: deviceIP,
|
||||
Name: "Discovery Name",
|
||||
DiscoveryMethod: "mDNS",
|
||||
})
|
||||
|
||||
// 4. Device must now be stored under the new account with the live name.
|
||||
info, err := ds.GetDeviceInfo(newAccount, deviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDeviceInfo under new account %s: %v", newAccount, err)
|
||||
}
|
||||
if info.AccountID != newAccount {
|
||||
t.Errorf("AccountID: want %s, got %s", newAccount, info.AccountID)
|
||||
}
|
||||
if info.Name != "Updated Name" {
|
||||
t.Errorf("Name: want %q, got %q", "Updated Name", info.Name)
|
||||
}
|
||||
|
||||
// 5. Old account entry must be gone.
|
||||
if _, err := ds.GetDeviceInfo(oldAccount, deviceID); err == nil {
|
||||
t.Errorf("stale entry still present under old account %s after MoveDevice", oldAccount)
|
||||
}
|
||||
|
||||
// 6. Presets must have survived the move.
|
||||
moved, err := ds.GetPresets(newAccount, deviceID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetPresets under new account %s: %v", newAccount, err)
|
||||
}
|
||||
if len(moved) != 1 || moved[0].Name != "My Station" {
|
||||
t.Errorf("presets not preserved after move: got %+v", moved)
|
||||
}
|
||||
|
||||
// 7. ListAllDevices must return exactly one entry — no duplicates.
|
||||
all, err := ds.ListAllDevices()
|
||||
if err != nil {
|
||||
t.Fatalf("ListAllDevices: %v", err)
|
||||
}
|
||||
if len(all) != 1 {
|
||||
t.Errorf("ListAllDevices: want 1 device, got %d: %+v", len(all), all)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMACBasedDeviceDiscovery_FallbackScenario(t *testing.T) {
|
||||
// Test scenario where /info endpoint is not available
|
||||
tempDir, err := os.MkdirTemp("", "mac-fallback-test-*")
|
||||
|
||||
Reference in New Issue
Block a user