mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
Fix generic source names in Local Account UI by falling back to account name (#124)
Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
co-authored by
Junie
parent
717693e01f
commit
50b694aa08
@@ -135,10 +135,18 @@ func mapToFullResponseSource(src *models.ConfiguredSource) models.FullResponseSo
|
||||
fs.Credential.Value = src.Secret
|
||||
fs.Credential.Type = src.SecretType
|
||||
|
||||
// If DisplayName is generic (e.g. "Audio") and we have a more specific Account name, use it.
|
||||
if fs.DisplayName == fs.Type && fs.Account != "" {
|
||||
fs.DisplayName = fs.Account
|
||||
fs.Name = fs.Account
|
||||
}
|
||||
|
||||
// Provide fallback for Name and SourceName if missing
|
||||
switch {
|
||||
case fs.Name != "":
|
||||
// Name already set to DisplayName
|
||||
case fs.Account != "":
|
||||
fs.Name = fs.Account
|
||||
case fs.SourceLabel != "":
|
||||
fs.Name = fs.SourceLabel
|
||||
default:
|
||||
@@ -149,6 +157,10 @@ func mapToFullResponseSource(src *models.ConfiguredSource) models.FullResponseSo
|
||||
fs.SourceName = fs.Name
|
||||
}
|
||||
|
||||
if fs.DisplayName == "" {
|
||||
fs.DisplayName = fs.Name
|
||||
}
|
||||
|
||||
return fs
|
||||
}
|
||||
|
||||
|
||||
@@ -146,3 +146,147 @@ func TestHandleMgmtAccountDetails_Recents(t *testing.T) {
|
||||
t.Errorf("Expected button_number '1', got '%s'", p0.ButtonNumber)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMgmtAccountDetails_Sources(t *testing.T) {
|
||||
tempBaseDir := "sources_test_data"
|
||||
err := os.MkdirAll(tempBaseDir, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tempBaseDir)
|
||||
|
||||
ds := datastore.NewDataStore(tempBaseDir)
|
||||
err = ds.Initialize()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
accountID := "1234567"
|
||||
deviceID := "001122334455"
|
||||
|
||||
deviceDir := ds.AccountDeviceDir(accountID, deviceID)
|
||||
err = os.MkdirAll(deviceDir, 0755)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Mock Sources.xml as it might be read by Sync/Save logic if we were using it,
|
||||
// but here we will save them directly via DataStore.
|
||||
sources := []models.ConfiguredSource{
|
||||
{
|
||||
ID: "9330201",
|
||||
Type: "Audio",
|
||||
DisplayName: "Audio",
|
||||
SourceName: "Audio",
|
||||
Name: "Audio",
|
||||
SourceKey: struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Account string `xml:"account,attr"`
|
||||
}{
|
||||
Type: "Audio",
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "10863533",
|
||||
Type: "Audio",
|
||||
DisplayName: "Audio",
|
||||
SourceName: "Audio",
|
||||
Name: "Audio",
|
||||
SourceKey: struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Account string `xml:"account,attr"`
|
||||
}{
|
||||
Type: "Audio",
|
||||
Account: "gesellix",
|
||||
},
|
||||
},
|
||||
}
|
||||
err = ds.SaveConfiguredSources(accountID, deviceID, sources)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Also need a device info file to be listed
|
||||
deviceInfo := models.ServiceDeviceInfo{
|
||||
AccountID: accountID,
|
||||
DeviceID: deviceID,
|
||||
Name: "Test Device",
|
||||
}
|
||||
err = ds.SaveDeviceInfo(accountID, deviceID, &deviceInfo)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server := &Server{ds: ds}
|
||||
|
||||
r := chi.NewRouter()
|
||||
r.Get("/mgmt/accounts/{accountId}", server.HandleMgmtAccountDetails)
|
||||
|
||||
req := httptest.NewRequest("GET", "/mgmt/accounts/1234567", nil)
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("Expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var response struct {
|
||||
Devices []struct {
|
||||
Sources []models.FullResponseSource `json:"sources"`
|
||||
} `json:"devices"`
|
||||
}
|
||||
|
||||
err = json.Unmarshal(w.Body.Bytes(), &response)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
if len(response.Devices) == 0 {
|
||||
t.Fatal("Expected one device")
|
||||
}
|
||||
|
||||
if len(response.Devices[0].Sources) < 2 {
|
||||
t.Fatalf("Expected at least 2 sources, got %d", len(response.Devices[0].Sources))
|
||||
}
|
||||
|
||||
// Find the gesellix source
|
||||
var gesellixSource *models.FullResponseSource
|
||||
for i := range response.Devices[0].Sources {
|
||||
if response.Devices[0].Sources[i].ID == "10863533" {
|
||||
gesellixSource = &response.Devices[0].Sources[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if gesellixSource == nil {
|
||||
t.Fatal("gesellix source not found")
|
||||
}
|
||||
|
||||
// It should have fallen back to Account name "gesellix" because DisplayName was generic "Audio"
|
||||
if gesellixSource.DisplayName != "gesellix" {
|
||||
t.Errorf("Expected display_name 'gesellix', got '%s'", gesellixSource.DisplayName)
|
||||
}
|
||||
if gesellixSource.Name != "gesellix" {
|
||||
t.Errorf("Expected name 'gesellix', got '%s'", gesellixSource.Name)
|
||||
}
|
||||
if gesellixSource.Type != "Audio" {
|
||||
t.Errorf("Expected type 'Audio', got '%s'", gesellixSource.Type)
|
||||
}
|
||||
|
||||
// Find the generic audio source
|
||||
var audioSource *models.FullResponseSource
|
||||
for i := range response.Devices[0].Sources {
|
||||
if response.Devices[0].Sources[i].ID == "9330201" {
|
||||
audioSource = &response.Devices[0].Sources[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if audioSource == nil {
|
||||
t.Fatal("audio source not found")
|
||||
}
|
||||
// It should still be "Audio" as there is no account fallback
|
||||
if audioSource.DisplayName != "Audio" {
|
||||
t.Errorf("Expected display_name 'Audio', got '%s'", audioSource.DisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user