mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-18 16:46:17 +00:00
- Added defaultSoundTouchPort constant (8090) to client.go - Updated parseBassHostPort and parseHostPort test utility functions to use constant - Removed unnecessary defaultPort parameters that always received 8090 - Fixed function signatures and all call sites in integration tests Progress: Reduced unparam issues from 3 to 1 (only client.go post method remains) Total issues: 23 → 21 (9% improvement) Remaining: - gocyclo: 14 (complexity) - revive: 1 (DiscoveryService naming) - staticcheck: 5 - unparam: 1 (client.post result parameter - kept for future extensibility)
433 lines
11 KiB
Go
433 lines
11 KiB
Go
package client
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// Integration tests for source selection functionality
|
|
// These tests require a real SoundTouch device for validation
|
|
// Set SOUNDTOUCH_TEST_HOST environment variable to run these tests
|
|
|
|
func TestClient_SelectSource_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
|
|
finalHost, finalPort := parseHostPort(host)
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 15 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
// First, get available sources to know what we can test
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
t.Logf("Found %d total sources, %d ready", sources.GetSourceCount(), sources.GetReadySourceCount())
|
|
|
|
// Test source selection based on what's available
|
|
tests := []struct {
|
|
name string
|
|
method func() error
|
|
checkSource func() bool
|
|
description string
|
|
skipIfMissing bool
|
|
}{
|
|
{
|
|
name: "Select Spotify",
|
|
method: func() error {
|
|
spotifySources := sources.GetReadySpotifySources()
|
|
if len(spotifySources) == 0 {
|
|
return nil // Skip if no Spotify available
|
|
}
|
|
// Use first available Spotify account
|
|
return client.SelectSpotify(spotifySources[0].SourceAccount)
|
|
},
|
|
checkSource: func() bool {
|
|
return sources.HasSpotify()
|
|
},
|
|
description: "Spotify source selection",
|
|
skipIfMissing: true,
|
|
},
|
|
{
|
|
name: "Select TuneIn",
|
|
method: func() error {
|
|
tuneInSources := sources.GetSourcesByType("TUNEIN")
|
|
for _, src := range tuneInSources {
|
|
if src.Status.IsReady() {
|
|
return client.SelectTuneIn(src.SourceAccount)
|
|
}
|
|
}
|
|
|
|
return nil // Skip if no TuneIn available
|
|
},
|
|
checkSource: func() bool {
|
|
return sources.HasSource("TUNEIN")
|
|
},
|
|
description: "TuneIn source selection",
|
|
skipIfMissing: true,
|
|
},
|
|
{
|
|
name: "Select via generic method",
|
|
method: func() error {
|
|
// Find any ready streaming source
|
|
for _, source := range sources.GetAvailableSources() {
|
|
if source.IsStreamingService() {
|
|
return client.SelectSource(source.Source, source.SourceAccount)
|
|
}
|
|
}
|
|
|
|
return nil // Skip if no streaming sources available
|
|
},
|
|
checkSource: func() bool {
|
|
streaming := sources.GetStreamingSources()
|
|
for _, src := range streaming {
|
|
if src.Status.IsReady() {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
},
|
|
description: "Generic source selection",
|
|
skipIfMissing: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipIfMissing && !tt.checkSource() {
|
|
t.Skipf("Skipping %s - source not available on test device", tt.description)
|
|
}
|
|
|
|
t.Logf("Testing %s on %s:%d", tt.description, finalHost, finalPort)
|
|
|
|
err := tt.method()
|
|
if err != nil {
|
|
t.Errorf("Failed to execute %s: %v", tt.description, err)
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ %s completed successfully", tt.description)
|
|
|
|
// Give the device a moment to process the change
|
|
time.Sleep(500 * time.Millisecond)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestClient_SelectSourceFromItem_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
|
|
finalHost, finalPort := parseHostPort(host)
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 15 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
// Get available sources
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
// Test selecting from available source items
|
|
availableSources := sources.GetAvailableSources()
|
|
if len(availableSources) == 0 {
|
|
t.Skip("No available sources to test with")
|
|
}
|
|
|
|
// Test with the first available source
|
|
testSource := availableSources[0]
|
|
t.Logf("Testing SelectSourceFromItem with source: %s (account: %s)",
|
|
testSource.Source, testSource.SourceAccount)
|
|
|
|
err = client.SelectSourceFromItem(&testSource)
|
|
if err != nil {
|
|
t.Errorf("Failed to select source from item: %v", err)
|
|
return
|
|
}
|
|
|
|
t.Logf("✓ SelectSourceFromItem completed successfully")
|
|
}
|
|
|
|
func TestClient_SelectSource_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
|
|
finalHost, finalPort := parseHostPort(host)
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 15 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
// Test with invalid source
|
|
t.Run("Invalid source", func(t *testing.T) {
|
|
err := client.SelectSource("INVALID_SOURCE", "")
|
|
if err == nil {
|
|
t.Error("Expected error for invalid source, got nil")
|
|
} else {
|
|
t.Logf("✓ Got expected error for invalid source: %v", err)
|
|
}
|
|
})
|
|
|
|
// Test with empty source (should fail validation)
|
|
t.Run("Empty source", func(t *testing.T) {
|
|
err := client.SelectSource("", "")
|
|
if err == nil {
|
|
t.Error("Expected error for empty source, got nil")
|
|
} else if err.Error() != "source cannot be empty" {
|
|
t.Errorf("Expected 'source cannot be empty' error, got: %v", err)
|
|
} else {
|
|
t.Logf("✓ Got expected validation error: %v", err)
|
|
}
|
|
})
|
|
|
|
// Test with nil source item
|
|
t.Run("Nil source item", func(t *testing.T) {
|
|
err := client.SelectSourceFromItem(nil)
|
|
if err == nil {
|
|
t.Error("Expected error for nil source item, got nil")
|
|
} else if err.Error() != "sourceItem cannot be nil" {
|
|
t.Errorf("Expected 'sourceItem cannot be nil' error, got: %v", err)
|
|
} else {
|
|
t.Logf("✓ Got expected validation error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestClient_ConvenienceSourceMethods_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
|
|
finalHost, finalPort := parseHostPort(host)
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 15 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Integration-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
// Get available sources to determine what we can test
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
t.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
// Test convenience methods based on availability
|
|
if sources.HasSpotify() {
|
|
t.Run("SelectSpotify", func(t *testing.T) {
|
|
spotifySources := sources.GetReadySpotifySources()
|
|
if len(spotifySources) > 0 {
|
|
err := client.SelectSpotify(spotifySources[0].SourceAccount)
|
|
if err != nil {
|
|
t.Errorf("SelectSpotify failed: %v", err)
|
|
} else {
|
|
t.Log("✓ SelectSpotify succeeded")
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
t.Log("Spotify not available - skipping SelectSpotify test")
|
|
}
|
|
|
|
if sources.HasBluetooth() {
|
|
t.Run("SelectBluetooth", func(t *testing.T) {
|
|
err := client.SelectBluetooth()
|
|
if err != nil {
|
|
t.Errorf("SelectBluetooth failed: %v", err)
|
|
} else {
|
|
t.Log("✓ SelectBluetooth succeeded")
|
|
}
|
|
})
|
|
} else {
|
|
t.Log("Bluetooth not available - skipping SelectBluetooth test")
|
|
}
|
|
|
|
if sources.HasSource("TUNEIN") {
|
|
t.Run("SelectTuneIn", func(t *testing.T) {
|
|
tuneInSources := sources.GetSourcesByType("TUNEIN")
|
|
for _, src := range tuneInSources {
|
|
if src.Status.IsReady() {
|
|
err := client.SelectTuneIn(src.SourceAccount)
|
|
if err != nil {
|
|
t.Errorf("SelectTuneIn failed: %v", err)
|
|
} else {
|
|
t.Log("✓ SelectTuneIn succeeded")
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
t.Log("TuneIn not available - skipping SelectTuneIn test")
|
|
}
|
|
|
|
if sources.HasSource("PANDORA") {
|
|
t.Run("SelectPandora", func(t *testing.T) {
|
|
pandoraSources := sources.GetSourcesByType("PANDORA")
|
|
for _, src := range pandoraSources {
|
|
if src.Status.IsReady() {
|
|
err := client.SelectPandora(src.SourceAccount)
|
|
if err != nil {
|
|
t.Errorf("SelectPandora failed: %v", err)
|
|
} else {
|
|
t.Log("✓ SelectPandora succeeded")
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
})
|
|
} else {
|
|
t.Log("Pandora not available - skipping SelectPandora test")
|
|
}
|
|
}
|
|
|
|
// Benchmark source selection performance
|
|
func BenchmarkClient_SelectSource_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
|
|
finalHost, finalPort := parseHostPort(host)
|
|
|
|
config := &Config{
|
|
Host: finalHost,
|
|
Port: finalPort,
|
|
Timeout: 15 * time.Second,
|
|
UserAgent: "Bose-SoundTouch-Go-Benchmark-Test/1.0",
|
|
}
|
|
|
|
client := NewClient(config)
|
|
|
|
// Get available sources
|
|
sources, err := client.GetSources()
|
|
if err != nil {
|
|
b.Fatalf("Failed to get sources: %v", err)
|
|
}
|
|
|
|
availableSources := sources.GetAvailableSources()
|
|
if len(availableSources) == 0 {
|
|
b.Skip("No available sources to benchmark with")
|
|
}
|
|
|
|
// Use first available source for benchmarking
|
|
testSource := availableSources[0]
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := client.SelectSource(testSource.Source, testSource.SourceAccount)
|
|
if err != nil {
|
|
b.Fatalf("SelectSource failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// parseHostPort is a helper function for integration tests
|
|
// This is a simple version for test use
|
|
func parseHostPort(hostPort string) (string, int) {
|
|
if !containsSubstring(hostPort, ":") {
|
|
return hostPort, defaultSoundTouchPort
|
|
}
|
|
|
|
// Simple parsing - in real use, we'd use net.SplitHostPort
|
|
parts := make([]string, 0, 2)
|
|
current := ""
|
|
|
|
for _, char := range hostPort {
|
|
if char == ':' {
|
|
parts = append(parts, current)
|
|
current = ""
|
|
} else {
|
|
current += string(char)
|
|
}
|
|
}
|
|
|
|
if current != "" {
|
|
parts = append(parts, current)
|
|
}
|
|
|
|
if len(parts) == 2 {
|
|
// Try to parse port
|
|
port := defaultSoundTouchPort
|
|
portStr := parts[1]
|
|
portInt := 0
|
|
|
|
for _, char := range portStr {
|
|
if char >= '0' && char <= '9' {
|
|
portInt = portInt*10 + int(char-'0')
|
|
} else {
|
|
portInt = -1
|
|
break
|
|
}
|
|
}
|
|
|
|
if portInt > 0 && portInt <= 65535 {
|
|
port = portInt
|
|
}
|
|
|
|
return parts[0], port
|
|
}
|
|
|
|
return hostPort, defaultSoundTouchPort
|
|
}
|