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.
396 lines
10 KiB
Go
396 lines
10 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gesellix/bose-soundtouch/pkg/models"
|
|
)
|
|
|
|
func TestGetServiceAvailability(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
responseBody string
|
|
statusCode int
|
|
expectError bool
|
|
validate func(t *testing.T, sa *models.ServiceAvailability)
|
|
}{
|
|
{
|
|
name: "successful response with mixed availability",
|
|
responseBody: `<?xml version="1.0" encoding="UTF-8" ?>
|
|
<serviceAvailability>
|
|
<services>
|
|
<service type="AIRPLAY" isAvailable="true" />
|
|
<service type="ALEXA" isAvailable="false" />
|
|
<service type="AMAZON" isAvailable="true" />
|
|
<service type="BLUETOOTH" isAvailable="false" reason="INVALID_SOURCE_TYPE" />
|
|
<service type="BMX" isAvailable="false" />
|
|
<service type="DEEZER" isAvailable="true" />
|
|
<service type="IHEART" isAvailable="true" />
|
|
<service type="LOCAL_INTERNET_RADIO" isAvailable="true" />
|
|
<service type="LOCAL_MUSIC" isAvailable="true" />
|
|
<service type="NOTIFICATION" isAvailable="false" />
|
|
<service type="PANDORA" isAvailable="true" />
|
|
<service type="SPOTIFY" isAvailable="true" />
|
|
<service type="TUNEIN" isAvailable="true" />
|
|
</services>
|
|
</serviceAvailability>`,
|
|
statusCode: 200,
|
|
expectError: false,
|
|
validate: func(t *testing.T, sa *models.ServiceAvailability) {
|
|
t.Helper()
|
|
|
|
if sa == nil {
|
|
t.Fatal("service availability should not be nil")
|
|
}
|
|
|
|
if sa.Services == nil {
|
|
t.Fatal("services should not be nil")
|
|
}
|
|
|
|
// Check total service count
|
|
if sa.GetServiceCount() != 13 {
|
|
t.Errorf("expected 13 services, got %d", sa.GetServiceCount())
|
|
}
|
|
|
|
// Check available services count
|
|
if sa.GetAvailableServiceCount() != 9 {
|
|
t.Errorf("expected 9 available services, got %d", sa.GetAvailableServiceCount())
|
|
}
|
|
|
|
// Check unavailable services count
|
|
if sa.GetUnavailableServiceCount() != 4 {
|
|
t.Errorf("expected 4 unavailable services, got %d", sa.GetUnavailableServiceCount())
|
|
}
|
|
|
|
// Check specific service availability
|
|
if !sa.HasSpotify() {
|
|
t.Error("should have Spotify")
|
|
}
|
|
|
|
if !sa.HasAirPlay() {
|
|
t.Error("should have AirPlay")
|
|
}
|
|
|
|
if !sa.HasTuneIn() {
|
|
t.Error("should have TuneIn")
|
|
}
|
|
|
|
if !sa.HasPandora() {
|
|
t.Error("should have Pandora")
|
|
}
|
|
|
|
if !sa.HasLocalMusic() {
|
|
t.Error("should have Local Music")
|
|
}
|
|
|
|
if sa.HasAlexa() {
|
|
t.Error("should not have Alexa")
|
|
}
|
|
|
|
if sa.HasBluetooth() {
|
|
t.Error("should not have Bluetooth")
|
|
}
|
|
|
|
// Check service with reason
|
|
bluetoothService := sa.GetServiceByType(models.ServiceTypeBluetooth)
|
|
if bluetoothService == nil {
|
|
t.Fatal("bluetooth service should not be nil")
|
|
}
|
|
|
|
if bluetoothService.IsAvailable {
|
|
t.Error("bluetooth service should not be available")
|
|
}
|
|
|
|
if bluetoothService.GetReason() != "INVALID_SOURCE_TYPE" {
|
|
t.Errorf("expected bluetooth reason 'INVALID_SOURCE_TYPE', got '%s'", bluetoothService.GetReason())
|
|
}
|
|
|
|
// Check streaming services
|
|
streamingServices := sa.GetStreamingServices()
|
|
if len(streamingServices) != 7 {
|
|
t.Errorf("expected 7 streaming services, got %d", len(streamingServices))
|
|
}
|
|
|
|
// Check local services
|
|
localServices := sa.GetLocalServices()
|
|
if len(localServices) != 3 {
|
|
t.Errorf("expected 3 local services, got %d", len(localServices))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "successful response with all services available",
|
|
responseBody: `<?xml version="1.0" encoding="UTF-8" ?>
|
|
<serviceAvailability>
|
|
<services>
|
|
<service type="SPOTIFY" isAvailable="true" />
|
|
<service type="BLUETOOTH" isAvailable="true" />
|
|
<service type="AIRPLAY" isAvailable="true" />
|
|
</services>
|
|
</serviceAvailability>`,
|
|
statusCode: 200,
|
|
expectError: false,
|
|
validate: func(t *testing.T, sa *models.ServiceAvailability) {
|
|
t.Helper()
|
|
|
|
if sa == nil {
|
|
t.Fatal("service availability should not be nil")
|
|
}
|
|
|
|
if sa.Services == nil {
|
|
t.Fatal("services should not be nil")
|
|
}
|
|
|
|
if sa.GetServiceCount() != 3 {
|
|
t.Errorf("expected 3 services, got %d", sa.GetServiceCount())
|
|
}
|
|
|
|
if sa.GetAvailableServiceCount() != 3 {
|
|
t.Errorf("expected 3 available services, got %d", sa.GetAvailableServiceCount())
|
|
}
|
|
|
|
if sa.GetUnavailableServiceCount() != 0 {
|
|
t.Errorf("expected 0 unavailable services, got %d", sa.GetUnavailableServiceCount())
|
|
}
|
|
|
|
if !sa.HasSpotify() {
|
|
t.Error("should have Spotify")
|
|
}
|
|
|
|
if !sa.HasBluetooth() {
|
|
t.Error("should have Bluetooth")
|
|
}
|
|
|
|
if !sa.HasAirPlay() {
|
|
t.Error("should have AirPlay")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "successful response with no services",
|
|
responseBody: `<?xml version="1.0" encoding="UTF-8" ?>
|
|
<serviceAvailability>
|
|
<services>
|
|
</services>
|
|
</serviceAvailability>`,
|
|
statusCode: 200,
|
|
expectError: false,
|
|
validate: func(t *testing.T, sa *models.ServiceAvailability) {
|
|
t.Helper()
|
|
|
|
if sa == nil {
|
|
t.Fatal("service availability should not be nil")
|
|
}
|
|
|
|
if sa.Services == nil {
|
|
t.Fatal("services should not be nil")
|
|
}
|
|
|
|
if sa.GetServiceCount() != 0 {
|
|
t.Errorf("expected 0 services, got %d", sa.GetServiceCount())
|
|
}
|
|
|
|
if sa.GetAvailableServiceCount() != 0 {
|
|
t.Errorf("expected 0 available services, got %d", sa.GetAvailableServiceCount())
|
|
}
|
|
|
|
if sa.GetUnavailableServiceCount() != 0 {
|
|
t.Errorf("expected 0 unavailable services, got %d", sa.GetUnavailableServiceCount())
|
|
}
|
|
|
|
if sa.HasSpotify() {
|
|
t.Error("should not have Spotify")
|
|
}
|
|
|
|
if sa.HasBluetooth() {
|
|
t.Error("should not have Bluetooth")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "server error",
|
|
responseBody: "Internal Server Error",
|
|
statusCode: 500,
|
|
expectError: true,
|
|
validate: func(_ *testing.T, _ *models.ServiceAvailability) {},
|
|
},
|
|
{
|
|
name: "invalid XML",
|
|
responseBody: "not valid xml",
|
|
statusCode: 200,
|
|
expectError: true,
|
|
validate: func(_ *testing.T, _ *models.ServiceAvailability) {},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Create test server
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/serviceAvailability" {
|
|
t.Errorf("expected path /serviceAvailability, got %s", r.URL.Path)
|
|
}
|
|
|
|
if r.Method != "GET" {
|
|
t.Errorf("expected GET method, got %s", r.Method)
|
|
}
|
|
|
|
w.WriteHeader(tt.statusCode)
|
|
_, _ = fmt.Fprint(w, tt.responseBody)
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Create client
|
|
client := createTestClient(server.URL)
|
|
|
|
// Execute test
|
|
result, err := client.GetServiceAvailability()
|
|
|
|
// Validate error expectation
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Error("expected an error but got none")
|
|
}
|
|
|
|
if result != nil {
|
|
t.Error("expected nil result on error")
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
tt.validate(t, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetServiceAvailability_NetworkError(t *testing.T) {
|
|
// Create client with invalid host
|
|
client := createTestClient("http://invalid-host:99999")
|
|
|
|
result, err := client.GetServiceAvailability()
|
|
if err == nil {
|
|
t.Error("expected an error but got none")
|
|
}
|
|
|
|
if result != nil {
|
|
t.Error("expected nil result on error")
|
|
}
|
|
|
|
if err != nil && !contains(err.Error(), "failed to get service availability") {
|
|
t.Errorf("error message should contain 'failed to get service availability', got: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestServiceAvailabilityModel_EdgeCases(t *testing.T) {
|
|
t.Run("nil services", func(t *testing.T) {
|
|
sa := &models.ServiceAvailability{}
|
|
|
|
if sa.GetServiceCount() != 0 {
|
|
t.Errorf("expected 0 service count, got %d", sa.GetServiceCount())
|
|
}
|
|
|
|
if sa.GetAvailableServiceCount() != 0 {
|
|
t.Errorf("expected 0 available count, got %d", sa.GetAvailableServiceCount())
|
|
}
|
|
|
|
if sa.GetUnavailableServiceCount() != 0 {
|
|
t.Errorf("expected 0 unavailable count, got %d", sa.GetUnavailableServiceCount())
|
|
}
|
|
|
|
if sa.HasSpotify() {
|
|
t.Error("should not have Spotify")
|
|
}
|
|
|
|
if sa.GetServiceByType(models.ServiceTypeSpotify) != nil {
|
|
t.Error("service should be nil")
|
|
}
|
|
|
|
if len(sa.GetAvailableServices()) != 0 {
|
|
t.Error("available services should be empty")
|
|
}
|
|
|
|
if len(sa.GetUnavailableServices()) != 0 {
|
|
t.Error("unavailable services should be empty")
|
|
}
|
|
|
|
if len(sa.GetStreamingServices()) != 0 {
|
|
t.Error("streaming services should be empty")
|
|
}
|
|
|
|
if len(sa.GetLocalServices()) != 0 {
|
|
t.Error("local services should be empty")
|
|
}
|
|
})
|
|
|
|
t.Run("service type checking", func(t *testing.T) {
|
|
service := models.Service{
|
|
Type: "SPOTIFY",
|
|
IsAvailable: true,
|
|
}
|
|
|
|
if !service.IsType(models.ServiceTypeSpotify) {
|
|
t.Error("service should be of type Spotify")
|
|
}
|
|
|
|
if service.IsType(models.ServiceTypeBluetooth) {
|
|
t.Error("service should not be of type Bluetooth")
|
|
}
|
|
})
|
|
|
|
t.Run("service reason handling", func(t *testing.T) {
|
|
serviceWithReason := models.Service{
|
|
Type: "BLUETOOTH",
|
|
IsAvailable: false,
|
|
Reason: "DEVICE_NOT_CONNECTED",
|
|
}
|
|
|
|
serviceWithoutReason := models.Service{
|
|
Type: "SPOTIFY",
|
|
IsAvailable: true,
|
|
}
|
|
|
|
if serviceWithReason.GetReason() != "DEVICE_NOT_CONNECTED" {
|
|
t.Errorf("expected DEVICE_NOT_CONNECTED, got %s", serviceWithReason.GetReason())
|
|
}
|
|
|
|
if serviceWithoutReason.GetReason() != "" {
|
|
t.Errorf("expected empty reason, got %s", serviceWithoutReason.GetReason())
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkGetServiceAvailability(b *testing.B) {
|
|
responseBody := `<?xml version="1.0" encoding="UTF-8" ?>
|
|
<serviceAvailability>
|
|
<services>
|
|
<service type="SPOTIFY" isAvailable="true" />
|
|
<service type="BLUETOOTH" isAvailable="false" reason="UNAVAILABLE" />
|
|
<service type="AIRPLAY" isAvailable="true" />
|
|
<service type="PANDORA" isAvailable="true" />
|
|
<service type="TUNEIN" isAvailable="true" />
|
|
</services>
|
|
</serviceAvailability>`
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
|
_, _ = fmt.Fprint(w, responseBody)
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := createTestClient(server.URL)
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := client.GetServiceAvailability()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|