Files
Bose-SoundTouch/pkg/service/marge/classify_radiobrowser_test.go
Tobias GesellchenandClaude Opus 4.8 2dada5a61a fix(web): play Radio Browser via native RADIO_BROWSER source (refs #479)
Selecting a Radio Browser station from the player UI returned HTTP 500
and the speaker dropped to INVALID_SOURCE. The play path sent the speaker
a ContentItem with source="URL" and an absolute location
(https://all.api.radio-browser.info/soundtouch/stations/byuuid/<uuid>).
source="URL" makes the speaker fetch that location as a raw audio stream,
but the URL returns station JSON, not audio, so the speaker rejects it.

"URL" was never a real source: it is not in the speaker's sourceprovider
registry and never persisted in any datastore. The rest of the stack is
already built for the native RADIO_BROWSER source (BMX registry provider
39 with base URL .../soundtouch, a seeded RADIO_BROWSER source, marge
classification, and the documented relative location form). Working
RADIO_BROWSER items use source="RADIO_BROWSER" with a relative
location="/stations/byuuid/<uuid>", which the speaker resolves against
the registry base URL and plays directly.

- stations.ResolveContentItem: emit source=RADIO_BROWSER for the provider
- RadioBrowserSearch: emit the relative /stations/byuuid/<uuid> playback
  href so the speaker prepends the registry base URL
- marge classifier: match the relative /stations/byuuid/ segment (covers
  both the relative and legacy absolute forms)
- tests updated to assert the native source + relative location

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 20:44:31 +02:00

88 lines
2.9 KiB
Go

package marge
import (
"strconv"
"testing"
"github.com/gesellix/bose-soundtouch/pkg/models"
"github.com/gesellix/bose-soundtouch/pkg/service/constants"
)
// TestClassifyAsRadioBrowser verifies that classifyAsRadioBrowser sets
// the expected fields on a ConfiguredSource.
func TestClassifyAsRadioBrowser(t *testing.T) {
src := &models.ConfiguredSource{}
classifyAsRadioBrowser(src)
if src.SourceKey.Type != constants.ProviderRadioBrowser {
t.Errorf("SourceKey.Type = %q, want %q", src.SourceKey.Type, constants.ProviderRadioBrowser)
}
if src.SourceKeyType != constants.ProviderRadioBrowser {
t.Errorf("SourceKeyType = %q, want %q", src.SourceKeyType, constants.ProviderRadioBrowser)
}
if src.Type != "Audio" {
t.Errorf("Type = %q, want Audio", src.Type)
}
if src.SecretType != constants.CredentialTypeToken {
t.Errorf("SecretType = %q, want %q", src.SecretType, constants.CredentialTypeToken)
}
if src.Secret == "" {
t.Error("expected Secret to be generated, got empty string")
}
if src.DisplayName != constants.ProviderRadioBrowser {
t.Errorf("DisplayName = %q, want %q", src.DisplayName, constants.ProviderRadioBrowser)
}
}
// TestClassifyAsRadioBrowser_PreservesExistingSecret verifies that a
// pre-existing secret is NOT overwritten.
func TestClassifyAsRadioBrowser_PreservesExistingSecret(t *testing.T) {
src := &models.ConfiguredSource{Secret: "existing-secret"}
classifyAsRadioBrowser(src)
if src.Secret != "existing-secret" {
t.Errorf("expected existing secret to be preserved, got %q", src.Secret)
}
}
// TestClassifyLearnedSource_RadioBrowserByProviderID verifies that the
// classifyLearnedSource dispatcher routes to classifyAsRadioBrowser when
// sourceProviderID matches RadioBrowserProviderID (39).
func TestClassifyLearnedSource_RadioBrowserByProviderID(t *testing.T) {
src := &models.ConfiguredSource{}
classifyLearnedSource(src, "", "", strconv.Itoa(constants.RadioBrowserProviderID))
if src.SourceKey.Type != constants.ProviderRadioBrowser {
t.Errorf("expected RADIO_BROWSER from providerID 39, got %q", src.SourceKey.Type)
}
}
// TestClassifyLearnedSource_RadioBrowserByLocation verifies that the dispatcher
// routes to classifyAsRadioBrowser when the location contains the RadioBrowser
// byuuid path segment, for both the relative form the native RADIO_BROWSER play
// path now emits and the legacy absolute form (#479).
func TestClassifyLearnedSource_RadioBrowserByLocation(t *testing.T) {
locations := []string{
"/stations/byuuid/abc-123", // native relative location
"https://all.api.radio-browser.info/soundtouch/stations/byuuid/abc-123", // legacy absolute location
}
for _, location := range locations {
src := &models.ConfiguredSource{}
classifyLearnedSource(src, "", location, "")
if src.SourceKey.Type != constants.ProviderRadioBrowser {
t.Errorf("expected RADIO_BROWSER from byuuid location %q, got %q", location, src.SourceKey.Type)
}
}
}