384 lines
10 KiB
Go
384 lines
10 KiB
Go
package ynison
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/client"
|
|
"gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/ynison/proto"
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
// TestYnisonClient_Connect tests the Connect method with a mock WebSocket server
|
|
func TestYnisonClient_Connect(t *testing.T) {
|
|
// Create a mock WebSocket server
|
|
var receivedMessages [][]byte
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
upgrader := websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool { return true },
|
|
}
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
t.Logf("Upgrade error: %v", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Read messages from client
|
|
for {
|
|
_, message, err := conn.ReadMessage()
|
|
if err != nil {
|
|
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
|
|
t.Logf("Read error: %v", err)
|
|
}
|
|
return
|
|
}
|
|
receivedMessages = append(receivedMessages, message)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
// Convert HTTP URL to WS URL
|
|
wsURL := "ws" + server.URL[4:] // Replace http with ws
|
|
|
|
// Create a client with test token
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-123",
|
|
Title: "Test Device",
|
|
DeviceType: proto.DeviceType_DEVICE_TYPE_WEB_DESKTOP,
|
|
AppName: "TestApp",
|
|
AppVersion: "1.0.0",
|
|
CanBePlayer: true,
|
|
CanBeRemote: true,
|
|
VolumeGran: 100,
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
|
|
// Override the wsURL for testing
|
|
ynisonClient.overrideWSURL(wsURL)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
err := ynisonClient.Connect(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Connect failed: %v", err)
|
|
}
|
|
|
|
if !ynisonClient.IsConnected() {
|
|
t.Error("Expected client to be connected after Connect")
|
|
}
|
|
|
|
// Wait a bit for the initial message to be sent
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Verify that an UpdateFullState message was sent
|
|
if len(receivedMessages) == 0 {
|
|
t.Error("Expected at least one message to be sent")
|
|
} else {
|
|
var req proto.PutYnisonStateRequest
|
|
if err := json.Unmarshal(receivedMessages[0], &req); err != nil {
|
|
t.Fatalf("Failed to unmarshal sent message: %v", err)
|
|
}
|
|
if req.UpdateFullState == nil {
|
|
t.Error("Expected UpdateFullState in first message")
|
|
}
|
|
if req.UpdateFullState.Device == nil {
|
|
t.Error("Expected Device in UpdateFullState")
|
|
}
|
|
if req.UpdateFullState.Device.Info.DeviceId != "test-device-123" {
|
|
t.Errorf("Expected DeviceId 'test-device-123', got '%s'", req.UpdateFullState.Device.Info.DeviceId)
|
|
}
|
|
}
|
|
|
|
// Cleanup
|
|
err = ynisonClient.Disconnect()
|
|
if err != nil {
|
|
t.Errorf("Disconnect failed: %v", err)
|
|
}
|
|
|
|
if ynisonClient.IsConnected() {
|
|
t.Error("Expected client to be disconnected after Disconnect")
|
|
}
|
|
}
|
|
|
|
// TestYnisonClient_Disconnect tests the Disconnect method
|
|
func TestYnisonClient_Disconnect(t *testing.T) {
|
|
// Create a mock WebSocket server
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
upgrader := websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool { return true },
|
|
}
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
// Keep connection open until client closes it
|
|
for {
|
|
_, _, err := conn.ReadMessage()
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
wsURL := "ws" + server.URL[4:]
|
|
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-disconnect",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
ynisonClient.overrideWSURL(wsURL)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
err := ynisonClient.Connect(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Connect failed: %v", err)
|
|
}
|
|
|
|
if !ynisonClient.IsConnected() {
|
|
t.Error("Expected client to be connected")
|
|
}
|
|
|
|
err = ynisonClient.Disconnect()
|
|
if err != nil {
|
|
t.Errorf("Disconnect failed: %v", err)
|
|
}
|
|
|
|
if ynisonClient.IsConnected() {
|
|
t.Error("Expected client to be disconnected after Disconnect")
|
|
}
|
|
|
|
// Calling Disconnect again should not error
|
|
err = ynisonClient.Disconnect()
|
|
if err != nil {
|
|
t.Errorf("Second Disconnect should not error: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestYnisonClient_UpdatePlayingStatus tests the UpdatePlayingStatus method
|
|
func TestYnisonClient_UpdatePlayingStatus(t *testing.T) {
|
|
var receivedMessages [][]byte
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
upgrader := websocket.Upgrader{
|
|
CheckOrigin: func(r *http.Request) bool { return true },
|
|
}
|
|
conn, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
for {
|
|
_, message, err := conn.ReadMessage()
|
|
if err != nil {
|
|
return
|
|
}
|
|
receivedMessages = append(receivedMessages, message)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
wsURL := "ws" + server.URL[4:]
|
|
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-status",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
ynisonClient.overrideWSURL(wsURL)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
err := ynisonClient.Connect(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Connect failed: %v", err)
|
|
}
|
|
|
|
// Wait for initial connection message
|
|
time.Sleep(100 * time.Millisecond)
|
|
initialMsgCount := len(receivedMessages)
|
|
|
|
// Create a playing status
|
|
status := CreatePlayingStatus(50000, 200000, false, 1.0, "test-device-status")
|
|
|
|
// Update playing status
|
|
err = ynisonClient.UpdatePlayingStatus(ctx, status)
|
|
if err != nil {
|
|
t.Fatalf("UpdatePlayingStatus failed: %v", err)
|
|
}
|
|
|
|
// Wait for message to be sent
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Verify the UpdatePlayingStatus message was sent
|
|
if len(receivedMessages) <= initialMsgCount {
|
|
t.Error("Expected UpdatePlayingStatus message to be sent")
|
|
} else {
|
|
var req proto.PutYnisonStateRequest
|
|
if err := json.Unmarshal(receivedMessages[initialMsgCount], &req); err != nil {
|
|
t.Fatalf("Failed to unmarshal sent message: %v", err)
|
|
}
|
|
if req.UpdatePlayingStatus == nil {
|
|
t.Error("Expected UpdatePlayingStatus in message")
|
|
}
|
|
if req.UpdatePlayingStatus.PlayingStatus == nil {
|
|
t.Error("Expected PlayingStatus in UpdatePlayingStatus")
|
|
}
|
|
if req.UpdatePlayingStatus.PlayingStatus.ProgressMs != 50000 {
|
|
t.Errorf("Expected ProgressMs 50000, got %d", req.UpdatePlayingStatus.PlayingStatus.ProgressMs)
|
|
}
|
|
if req.UpdatePlayingStatus.PlayingStatus.DurationMs != 200000 {
|
|
t.Errorf("Expected DurationMs 200000, got %d", req.UpdatePlayingStatus.PlayingStatus.DurationMs)
|
|
}
|
|
if req.UpdatePlayingStatus.PlayingStatus.Paused != false {
|
|
t.Errorf("Expected Paused false, got %v", req.UpdatePlayingStatus.PlayingStatus.Paused)
|
|
}
|
|
if req.UpdatePlayingStatus.PlayingStatus.PlaybackSpeed != 1.0 {
|
|
t.Errorf("Expected PlaybackSpeed 1.0, got %f", req.UpdatePlayingStatus.PlayingStatus.PlaybackSpeed)
|
|
}
|
|
if req.UpdatePlayingStatus.PlayingStatus.Version.DeviceId != "test-device-status" {
|
|
t.Errorf("Expected DeviceId 'test-device-status', got '%s'", req.UpdatePlayingStatus.PlayingStatus.Version.DeviceId)
|
|
}
|
|
if req.Rid == "" {
|
|
t.Error("Expected non-empty Rid")
|
|
}
|
|
}
|
|
|
|
err = ynisonClient.Disconnect()
|
|
if err != nil {
|
|
t.Errorf("Disconnect failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestYnisonClient_UpdatePlayingStatus_NotConnected tests UpdatePlayingStatus when not connected
|
|
func TestYnisonClient_UpdatePlayingStatus_NotConnected(t *testing.T) {
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-not-connected",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
|
|
status := CreatePlayingStatus(50000, 200000, false, 1.0, "test-device-not-connected")
|
|
|
|
err := ynisonClient.UpdatePlayingStatus(context.Background(), status)
|
|
if err == nil {
|
|
t.Error("Expected error when not connected")
|
|
}
|
|
if err != nil && err.Error() != "not connected" {
|
|
t.Errorf("Expected 'not connected' error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestYnisonClient_Connect_NoToken tests Connect without a token
|
|
func TestYnisonClient_Connect_NoToken(t *testing.T) {
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-no-token",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
|
|
err := ynisonClient.Connect(context.Background())
|
|
if err == nil {
|
|
t.Error("Expected error when no token")
|
|
}
|
|
if err != nil && err.Error() != "no auth token available" {
|
|
t.Errorf("Expected 'no auth token available' error, got: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestYnisonClient_IsConnected tests the IsConnected method
|
|
func TestYnisonClient_IsConnected(t *testing.T) {
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-isconnected",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
|
|
if ynisonClient.IsConnected() {
|
|
t.Error("Expected client to not be connected initially")
|
|
}
|
|
|
|
// We can't easily test connected state without a real server,
|
|
// but we verified this in the Connect/Disconnect tests
|
|
}
|
|
|
|
// TestYnisonClient_RegisterHandler tests handler registration
|
|
func TestYnisonClient_RegisterHandler(t *testing.T) {
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-handler",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
|
|
ynisonClient.RegisterHandler("test", func(resp *proto.PutYnisonStateResponse) {})
|
|
|
|
// We can't easily test handler invocation without a real server,
|
|
// but we can verify the handler is registered
|
|
ynisonClient.UnregisterHandler("test")
|
|
|
|
// Re-register and verify it doesn't panic
|
|
ynisonClient.RegisterHandler("test2", func(resp *proto.PutYnisonStateResponse) {})
|
|
ynisonClient.UnregisterHandler("test2")
|
|
ynisonClient.UnregisterHandler("nonexistent") // Should not panic
|
|
}
|
|
|
|
// TestYnisonClient_DisconnectWithoutConnect tests Disconnect without prior Connect
|
|
func TestYnisonClient_DisconnectWithoutConnect(t *testing.T) {
|
|
c := client.NewClient(client.ClientConfig{
|
|
Token: "test-token",
|
|
})
|
|
|
|
config := DeviceConfig{
|
|
DeviceID: "test-device-disconnect-no-connect",
|
|
}
|
|
|
|
ynisonClient := NewYnisonClient(c, config)
|
|
|
|
// Should not panic or error
|
|
err := ynisonClient.Disconnect()
|
|
if err != nil {
|
|
t.Errorf("Disconnect without connect should not error: %v", err)
|
|
}
|
|
}
|