mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-09-07 15:07:17 +00:00
fix(setup): let an unpaired speaker migrate
The readiness check refused when the live /info carried no margeAccountUUID, which is exactly the state a factory-reset speaker is in. That made the documented onboarding impossible. The admin UI migrates first and pairs afterwards (see "Pairing runs after the URL flip" in the setup page), and MIGRATION-GUIDE.md step 4 tells the user to Generate an account ID on a factory-reset device. Both now hit a 409 before pairing can run. The suggested remedy could not help either: SyncDeviceData files an account-less device under "default", which never matches an empty live account, so the user had no way forward at all. An unpaired speaker has no account data to preserve, so there is nothing for this check to compare and nothing to lose. Skip it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b9f0c16275
commit
ba7e2da7b3
@@ -58,7 +58,16 @@ func (m *Manager) checkMigrationDataReady(deviceIP string) error {
|
||||
}
|
||||
|
||||
if accountID == "" {
|
||||
return migrationDataNotReadyf("live /info has no paired margeAccountUUID")
|
||||
// An unpaired speaker, typically factory-reset, has no account data to
|
||||
// preserve, so there is nothing for this check to compare and nothing
|
||||
// to lose. Refusing here would also break the documented onboarding
|
||||
// order: the admin UI migrates first and pairs afterwards (see the
|
||||
// "Pairing runs after the URL flip" comment in the setup page), and
|
||||
// MIGRATION-GUIDE.md tells the user to Generate an account ID on a
|
||||
// factory-reset device. Data Sync cannot unblock it either, since it
|
||||
// files an account-less device under "default", which never matches an
|
||||
// empty live account.
|
||||
return nil
|
||||
}
|
||||
|
||||
if !datastore.IsSafeIdentifier(accountID) || !datastore.IsSafeIdentifier(deviceID) {
|
||||
|
||||
@@ -252,3 +252,27 @@ func assertPresetSnapshotUnchanged(t *testing.T, path string, want []byte) {
|
||||
t.Fatalf("readiness preflight rewrote Presets.xml\n got: %s\nwant: %s", after, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMigrationDataReadinessAllowsUnpairedSpeaker: a factory-reset speaker has
|
||||
// no account data to preserve, and the admin UI migrates before it pairs, so
|
||||
// refusing here would make onboarding impossible. Data Sync could not unblock
|
||||
// it either: an account-less device is filed under "default", which never
|
||||
// matches an empty live account.
|
||||
func TestMigrationDataReadinessAllowsUnpairedSpeaker(t *testing.T) {
|
||||
speaker := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/info" {
|
||||
_, _ = fmt.Fprintf(w, `<info deviceID="%s"><name>Fresh Speaker</name><margeAccountUUID></margeAccountUUID></info>`, readinessDevice)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
http.NotFound(w, r)
|
||||
}))
|
||||
defer speaker.Close()
|
||||
|
||||
m := NewManager("http://aftertouch.example:8000", datastore.NewDataStore(t.TempDir()), nil)
|
||||
|
||||
if err := m.checkMigrationDataReady(strings.TrimPrefix(speaker.URL, "http://")); err != nil {
|
||||
t.Fatalf("unpaired speaker was refused migration: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user