Initial commit

This commit is contained in:
Mrixs committed 2026-10-14 23:04:51 +03:00
commit 02a6dabcfb
62 files changed
+15751

No files matched your search

+383
View File
@@ -0,0 +1,383 @@
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)
}
}