mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 08:36:13 +00:00
Applied automatic fixes using golangci-lint --fix which resolved: - All remaining wsl_v5 whitespace issues (28 issues) - All whitespace formatting issues (1 issue) - Improved code formatting consistency across the entire codebase All tests passing and functionality preserved.
461 lines
11 KiB
Go
461 lines
11 KiB
Go
package client
|
|
|
|
import (
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
)
|
|
|
|
func TestClient_Navigation_Integration(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration tests in short mode")
|
|
}
|
|
|
|
host := os.Getenv("SOUNDTOUCH_TEST_HOST")
|
|
if host == "" {
|
|
t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
|
|
}
|
|
|
|
// Parse host:port if provided
|
|
var finalHost string
|
|
|
|
var finalPort int
|
|
|
|
if strings.Contains(host, ":") {
|
|
parts := strings.Split(host, ":")
|
|
|
|
finalHost = parts[0]
|
|
if len(parts) > 1 {
|
|
// Use default port if parsing fails
|
|
finalPort = 8090
|
|
}
|
|
} else {
|
|
finalHost = host
|
|
finalPort = 8090
|
|
}
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 30 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Client-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
t.Run("Navigate_TuneIn", func(t *testing.T) {
|
|
response, err := client.Navigate("TUNEIN", "", 1, 10)
|
|
if err != nil {
|
|
t.Logf("Navigate TUNEIN failed (may not be available): %v", err)
|
|
t.Skip("TUNEIN not available on test device")
|
|
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ Navigate TUNEIN succeeded")
|
|
t.Logf(" Total items: %d", response.TotalItems)
|
|
t.Logf(" Items returned: %d", len(response.Items))
|
|
|
|
if response.TotalItems > 0 {
|
|
t.Logf(" First item: %s", response.Items[0].GetDisplayName())
|
|
}
|
|
})
|
|
|
|
t.Run("GetTuneInStations", func(t *testing.T) {
|
|
response, err := client.GetTuneInStations("")
|
|
if err != nil {
|
|
t.Logf("GetTuneInStations failed (may not be available): %v", err)
|
|
t.Skip("TuneIn not available on test device")
|
|
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ GetTuneInStations succeeded")
|
|
t.Logf(" Total stations: %d", response.TotalItems)
|
|
|
|
stations := response.GetStations()
|
|
t.Logf(" Station items: %d", len(stations))
|
|
})
|
|
|
|
t.Run("Navigate_StoredMusic", func(t *testing.T) {
|
|
// Get sources first to check if STORED_MUSIC is available
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
var storedMusicAccount string
|
|
|
|
for _, source := range sources.SourceItem {
|
|
if source.Source == "STORED_MUSIC" && source.Status.IsReady() {
|
|
storedMusicAccount = source.SourceAccount
|
|
break
|
|
}
|
|
}
|
|
|
|
if storedMusicAccount == "" {
|
|
t.Skip("STORED_MUSIC not available or not ready on test device")
|
|
}
|
|
|
|
response, err := client.GetStoredMusicLibrary(storedMusicAccount)
|
|
if err != nil {
|
|
t.Logf("GetStoredMusicLibrary failed: %v", err)
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ GetStoredMusicLibrary succeeded")
|
|
t.Logf(" Source account: %s", storedMusicAccount)
|
|
t.Logf(" Total items: %d", response.TotalItems)
|
|
|
|
directories := response.GetDirectories()
|
|
t.Logf(" Directories: %d", len(directories))
|
|
|
|
tracks := response.GetTracks()
|
|
t.Logf(" Tracks: %d", len(tracks))
|
|
})
|
|
|
|
t.Run("SearchStation_TuneIn", func(t *testing.T) {
|
|
response, err := client.SearchTuneInStations("jazz")
|
|
if err != nil {
|
|
t.Logf("SearchTuneInStations failed (may not be supported): %v", err)
|
|
t.Skip("TuneIn search not supported on test device")
|
|
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ SearchTuneInStations succeeded")
|
|
t.Logf(" Search term: jazz")
|
|
t.Logf(" Total results: %d", response.GetResultCount())
|
|
|
|
songs := response.GetSongs()
|
|
artists := response.GetArtists()
|
|
stations := response.GetStations()
|
|
|
|
t.Logf(" Songs: %d", len(songs))
|
|
t.Logf(" Artists: %d", len(artists))
|
|
t.Logf(" Stations: %d", len(stations))
|
|
|
|
if len(stations) > 0 {
|
|
station := stations[0]
|
|
t.Logf(" First station: %s", station.GetDisplayName())
|
|
|
|
if station.Token != "" {
|
|
t.Logf(" Station token: %s", station.Token)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestClient_StationManagement_Integration(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration tests in short mode")
|
|
}
|
|
|
|
host := os.Getenv("SOUNDTOUCH_TEST_HOST")
|
|
if host == "" {
|
|
t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
|
|
}
|
|
|
|
// Parse host:port if provided
|
|
var finalHost string
|
|
|
|
var finalPort int
|
|
|
|
if strings.Contains(host, ":") {
|
|
parts := strings.Split(host, ":")
|
|
|
|
finalHost = parts[0]
|
|
if len(parts) > 1 {
|
|
finalPort = 8090
|
|
}
|
|
} else {
|
|
finalHost = host
|
|
finalPort = 8090
|
|
}
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 30 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Client-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
t.Run("SearchAndAddStation_Pandora", func(t *testing.T) {
|
|
// Get sources first to check if Pandora is available
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
var pandoraAccount string
|
|
|
|
for _, source := range sources.SourceItem {
|
|
if source.Source == "PANDORA" && source.Status.IsReady() {
|
|
pandoraAccount = source.SourceAccount
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if pandoraAccount == "" {
|
|
t.Skip("Pandora not available or not configured on test device")
|
|
}
|
|
|
|
// Search for stations
|
|
searchResponse, err := client.SearchPandoraStations(pandoraAccount, "classic rock")
|
|
if err != nil {
|
|
t.Logf("SearchPandoraStations failed: %v", err)
|
|
t.Skip("Pandora search not working")
|
|
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ SearchPandoraStations succeeded")
|
|
t.Logf(" Account: %s", pandoraAccount)
|
|
t.Logf(" Results: %d", searchResponse.GetResultCount())
|
|
|
|
// Try to find an artist or station result to add
|
|
var tokenToAdd string
|
|
|
|
var nameToAdd string
|
|
|
|
artists := searchResponse.GetArtists()
|
|
if len(artists) > 0 {
|
|
tokenToAdd = artists[0].Token
|
|
nameToAdd = artists[0].Name + " Radio"
|
|
} else {
|
|
stations := searchResponse.GetStations()
|
|
if len(stations) > 0 {
|
|
tokenToAdd = stations[0].Token
|
|
nameToAdd = stations[0].Name
|
|
}
|
|
}
|
|
|
|
if tokenToAdd == "" {
|
|
t.Skip("No suitable results found to test AddStation")
|
|
}
|
|
|
|
t.Logf(" Will attempt to add: %s (Token: %s)", nameToAdd, tokenToAdd)
|
|
|
|
// Note: AddStation immediately starts playing and modifies user's collection
|
|
// In a real integration test, you might want to skip this or use a test account
|
|
t.Logf(" Skipping actual AddStation to avoid modifying user collection")
|
|
t.Logf(" AddStation would call: client.AddStation(%q, %q, %q, %q)", "PANDORA", pandoraAccount, tokenToAdd, nameToAdd)
|
|
})
|
|
|
|
t.Run("NavigateContainer_Integration", func(t *testing.T) {
|
|
// Get sources to find a suitable container-based source
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
var testSource string
|
|
|
|
var testAccount string
|
|
|
|
// Look for STORED_MUSIC as it typically has containers
|
|
|
|
for _, source := range sources.SourceItem {
|
|
if source.Source == "STORED_MUSIC" && source.Status.IsReady() {
|
|
testSource = source.Source
|
|
testAccount = source.SourceAccount
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if testSource == "" {
|
|
t.Skip("No suitable container-based source found")
|
|
}
|
|
|
|
// First, navigate to get a container
|
|
response, err := client.Navigate(testSource, testAccount, 1, 10)
|
|
if err != nil {
|
|
t.Logf("Initial navigate failed: %v", err)
|
|
return
|
|
}
|
|
|
|
directories := response.GetDirectories()
|
|
if len(directories) == 0 {
|
|
t.Skip("No directories found to test container navigation")
|
|
}
|
|
|
|
// Pick the first directory to navigate into
|
|
container := directories[0]
|
|
if container.ContentItem == nil {
|
|
t.Skip("Directory has no ContentItem for navigation")
|
|
}
|
|
|
|
t.Logf("✓ Found container: %s", container.GetDisplayName())
|
|
|
|
// Navigate into the container
|
|
containerResponse, err := client.NavigateContainer(testSource, testAccount, 1, 20, container.ContentItem)
|
|
if err != nil {
|
|
t.Logf("NavigateContainer failed: %v", err)
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ NavigateContainer succeeded")
|
|
t.Logf(" Container: %s", container.GetDisplayName())
|
|
t.Logf(" Items in container: %d", len(containerResponse.Items))
|
|
|
|
tracks := containerResponse.GetTracks()
|
|
subdirs := containerResponse.GetDirectories()
|
|
|
|
t.Logf(" Tracks: %d", len(tracks))
|
|
t.Logf(" Subdirectories: %d", len(subdirs))
|
|
})
|
|
}
|
|
|
|
func TestClient_Navigation_ErrorHandling_Integration(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping integration tests in short mode")
|
|
}
|
|
|
|
host := os.Getenv("SOUNDTOUCH_TEST_HOST")
|
|
if host == "" {
|
|
t.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration tests")
|
|
}
|
|
|
|
// Parse host:port if provided
|
|
var finalHost string
|
|
|
|
var finalPort int
|
|
|
|
if strings.Contains(host, ":") {
|
|
parts := strings.Split(host, ":")
|
|
|
|
finalHost = parts[0]
|
|
if len(parts) > 1 {
|
|
finalPort = 8090
|
|
}
|
|
} else {
|
|
finalHost = host
|
|
finalPort = 8090
|
|
}
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 10 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Client-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
t.Run("Navigate_InvalidSource", func(t *testing.T) {
|
|
_, err := client.Navigate("INVALID_SOURCE", "", 1, 10)
|
|
if err == nil {
|
|
t.Error("Expected error for invalid source, got none")
|
|
} else {
|
|
t.Logf("✓ Correctly failed for invalid source: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("SearchStation_InvalidSource", func(t *testing.T) {
|
|
_, err := client.SearchStation("INVALID_SOURCE", "", "test")
|
|
if err == nil {
|
|
t.Error("Expected error for invalid source, got none")
|
|
} else {
|
|
t.Logf("✓ Correctly failed for invalid source: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("AddStation_InvalidToken", func(t *testing.T) {
|
|
err := client.AddStation("PANDORA", "fake_account", "invalid_token", "Test Station")
|
|
if err == nil {
|
|
t.Error("Expected error for invalid token, got none")
|
|
} else {
|
|
t.Logf("✓ Correctly failed for invalid token: %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("RemoveStation_InvalidContentItem", func(t *testing.T) {
|
|
invalidContentItem := &models.ContentItem{
|
|
Source: "PANDORA",
|
|
Location: "invalid_location",
|
|
ItemName: "Invalid Station",
|
|
}
|
|
|
|
err := client.RemoveStation(invalidContentItem)
|
|
if err == nil {
|
|
t.Error("Expected error for invalid content item, got none")
|
|
} else {
|
|
t.Logf("✓ Correctly failed for invalid content item: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkClient_Navigate_Integration(b *testing.B) {
|
|
if testing.Short() {
|
|
b.Skip("Skipping integration benchmarks in short mode")
|
|
}
|
|
|
|
host := os.Getenv("SOUNDTOUCH_TEST_HOST")
|
|
if host == "" {
|
|
b.Skip("SOUNDTOUCH_TEST_HOST environment variable not set - skipping integration benchmarks")
|
|
}
|
|
|
|
// Parse host:port if provided
|
|
var finalHost string
|
|
|
|
var finalPort int
|
|
|
|
if strings.Contains(host, ":") {
|
|
parts := strings.Split(host, ":")
|
|
|
|
finalHost = parts[0]
|
|
if len(parts) > 1 {
|
|
finalPort = 8090
|
|
}
|
|
} else {
|
|
finalHost = host
|
|
finalPort = 8090
|
|
}
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 10 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Client-Benchmark/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
b.ResetTimer()
|
|
|
|
b.Run("Navigate_TuneIn", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := client.Navigate("TUNEIN", "", 1, 10)
|
|
if err != nil {
|
|
b.Logf("Navigate failed: %v", err)
|
|
b.Skip("TuneIn not available")
|
|
|
|
return
|
|
}
|
|
}
|
|
})
|
|
|
|
b.Run("SearchStation_TuneIn", func(b *testing.B) {
|
|
searchTerms := []string{"jazz", "rock", "classical", "pop", "country"}
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
term := searchTerms[i%len(searchTerms)]
|
|
|
|
_, err := client.SearchTuneInStations(term)
|
|
if err != nil {
|
|
b.Logf("Search failed: %v", err)
|
|
b.Skip("TuneIn search not available")
|
|
|
|
return
|
|
}
|
|
}
|
|
})
|
|
}
|