234 lines
7.1 KiB
Go
234 lines
7.1 KiB
Go
package utils
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestGetSignRequest tests the GetSignRequest function with various inputs
|
|
func TestGetSignRequest(t *testing.T) {
|
|
// Test with default key
|
|
sign := GetSignRequest("12345", "")
|
|
if sign.Timestamp == 0 {
|
|
t.Error("Expected non-zero timestamp")
|
|
}
|
|
if sign.Value == "" {
|
|
t.Error("Expected non-empty sign value")
|
|
}
|
|
|
|
// Test with custom key
|
|
sign = GetSignRequest("12345", "custom-key")
|
|
if sign.Timestamp == 0 {
|
|
t.Error("Expected non-zero timestamp")
|
|
}
|
|
if sign.Value == "" {
|
|
t.Error("Expected non-empty sign value")
|
|
}
|
|
|
|
// Test track ID as string (function accepts string)
|
|
sign = GetSignRequest("12345", "")
|
|
if sign.Timestamp == 0 {
|
|
t.Error("Expected non-zero timestamp for string track ID")
|
|
}
|
|
if sign.Value == "" {
|
|
t.Error("Expected non-empty sign value for string track ID")
|
|
}
|
|
}
|
|
|
|
// TestGetFileInfoSign tests the GetFileInfoSign function with various inputs
|
|
func TestGetFileInfoSign(t *testing.T) {
|
|
codecs := []string{"mp3", "flac"}
|
|
sign := GetFileInfoSign("12345", "high", codecs, "http", "")
|
|
if sign.Timestamp == 0 {
|
|
t.Error("Expected non-zero timestamp")
|
|
}
|
|
if sign.Value == "" {
|
|
t.Error("Expected non-empty sign value")
|
|
}
|
|
|
|
// Test with custom key
|
|
sign = GetFileInfoSign("12345", "high", codecs, "http", "custom-key")
|
|
if sign.Timestamp == 0 {
|
|
t.Error("Expected non-zero timestamp")
|
|
}
|
|
if sign.Value == "" {
|
|
t.Error("Expected non-empty sign value")
|
|
}
|
|
|
|
// Test empty codecs
|
|
sign = GetFileInfoSign("12345", "high", []string{}, "http", "")
|
|
if sign.Timestamp == 0 {
|
|
t.Error("Expected non-zero timestamp for empty codecs")
|
|
}
|
|
if sign.Value == "" {
|
|
t.Error("Expected non-empty sign value for empty codecs")
|
|
}
|
|
}
|
|
|
|
// TestSignDeterministic tests that same input produces same output with fixed time
|
|
func TestSignDeterministic(t *testing.T) {
|
|
// We can't easily test this without mocking time, but we can verify
|
|
// that the function doesn't panic
|
|
for i := 0; i < 10; i++ {
|
|
sign := GetSignRequest("test-track", "test-key")
|
|
if sign.Value == "" {
|
|
t.Errorf("Iteration %d: got empty sign", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestGetSignRequestFixedTimestamp tests GetSignRequest with a fixed timestamp
|
|
// to verify deterministic output matches Python reference implementation
|
|
func TestGetSignRequestFixedTimestamp(t *testing.T) {
|
|
// Use a fixed timestamp for deterministic testing
|
|
fixedTime := time.Unix(1668687184, 0)
|
|
trackID := "4784420"
|
|
key := "SUPER_SECRET_KEY"
|
|
expectedSign := "vssEEweZhgv2Aud0rdH9maOXUC03ZkZ/hlo6bSRN8Qg="
|
|
|
|
// Manually compute expected sign with fixed time
|
|
message := trackID + strconv.FormatInt(fixedTime.Unix(), 10)
|
|
h := hmac.New(sha256.New, []byte(key))
|
|
h.Write([]byte(message))
|
|
computedSign := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
|
|
if computedSign != expectedSign {
|
|
t.Errorf("Expected sign %q, got %q", expectedSign, computedSign)
|
|
}
|
|
|
|
// Test that our function produces correct sign when we override time
|
|
// Note: We can't easily mock time.Now() in Go without refactoring,
|
|
// so we test the algorithm directly here
|
|
sign := GetSignRequest(trackID, key)
|
|
_ = sign // Just verify no panic
|
|
}
|
|
|
|
// TestGetFileInfoSignFixedTimestamp tests GetFileInfoSign with a fixed timestamp
|
|
func TestGetFileInfoSignFixedTimestamp(t *testing.T) {
|
|
fixedTime := time.Unix(1668687184, 0)
|
|
trackID := "4784420:100500"
|
|
quality := "lossless"
|
|
codecs := []string{"flac", "aac", "mp3"}
|
|
transport := "encraw"
|
|
key := "SUPER_SECRET_KEY"
|
|
|
|
// Manually compute expected sign with fixed time (matching Python)
|
|
message := strconv.FormatInt(fixedTime.Unix(), 10) + trackID + quality + "flacaacmp3" + transport
|
|
h := hmac.New(sha256.New, []byte(key))
|
|
h.Write([]byte(message))
|
|
sign := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
// Remove last character as in Python
|
|
if len(sign) > 0 {
|
|
sign = sign[:len(sign)-1]
|
|
}
|
|
|
|
// Test our function with fixed time logic
|
|
sign2 := GetFileInfoSign(trackID, quality, codecs, transport, key)
|
|
_ = sign2 // Just verify no panic
|
|
|
|
// Verify the algorithm produces expected format
|
|
if sign == "" {
|
|
t.Error("Expected non-empty computed sign")
|
|
}
|
|
if sign[len(sign)-1] == '=' {
|
|
t.Error("Sign should not end with '=' (last char removed)")
|
|
}
|
|
}
|
|
|
|
// TestJoinCodecs tests the joinCodecs helper function
|
|
func TestJoinCodecs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
codecs []string
|
|
expected string
|
|
}{
|
|
{"empty", []string{}, ""},
|
|
{"single", []string{"mp3"}, "mp3"},
|
|
{"multiple", []string{"flac", "aac", "mp3"}, "flacaacmp3"},
|
|
{"with_special_chars", []string{"opus", "aac+"}, "opusaac+"},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := joinCodecs(tc.codecs)
|
|
if result != tc.expected {
|
|
t.Errorf("joinCodecs(%v) = %q, want %q", tc.codecs, result, tc.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestSignWithDifferentKeys tests that different keys produce different signatures
|
|
func TestSignWithDifferentKeys(t *testing.T) {
|
|
trackID := "12345"
|
|
key1 := "key1"
|
|
key2 := "key2"
|
|
|
|
sign1 := GetSignRequest(trackID, key1)
|
|
sign2 := GetSignRequest(trackID, key2)
|
|
|
|
if sign1.Value == sign2.Value {
|
|
t.Error("Different keys should produce different signatures")
|
|
}
|
|
}
|
|
|
|
// TestFileInfoSignWithDifferentKeys tests that different keys produce different signatures
|
|
func TestFileInfoSignWithDifferentKeys(t *testing.T) {
|
|
codecs := []string{"mp3", "flac"}
|
|
key1 := "key1"
|
|
key2 := "key2"
|
|
|
|
sign1 := GetFileInfoSign("12345", "high", codecs, "http", key1)
|
|
sign2 := GetFileInfoSign("12345", "high", codecs, "http", key2)
|
|
|
|
if sign1.Value == sign2.Value {
|
|
t.Error("Different keys should produce different signatures")
|
|
}
|
|
}
|
|
|
|
// TestFileInfoSignRemovesLastChar tests that the last character is removed from base64 output
|
|
func TestFileInfoSignRemovesLastChar(t *testing.T) {
|
|
codecs := []string{"mp3"}
|
|
sign := GetFileInfoSign("12345", "high", codecs, "http", "test-key")
|
|
|
|
// The sign should not end with '=' (padding) because we remove the last char
|
|
// Note: it could end with '=' if the original base64 ended with '==', but we remove one char
|
|
_ = sign
|
|
// This is more of a sanity check that the function runs
|
|
}
|
|
|
|
// TestDefaultSignKey tests that default keys are used when empty string provided
|
|
func TestDefaultSignKey(t *testing.T) {
|
|
// GetSignRequest should use DefaultSignKey when key is empty
|
|
sign1 := GetSignRequest("12345", "")
|
|
sign2 := GetSignRequest("12345", DefaultSignKey)
|
|
|
|
// They should produce same result (timestamps will differ, but algorithm same)
|
|
if sign1.Value == "" || sign2.Value == "" {
|
|
t.Error("Both should produce non-empty signatures")
|
|
}
|
|
|
|
// GetFileInfoSign should use DesktopSignKey when key is empty
|
|
sign3 := GetFileInfoSign("12345", "high", []string{"mp3"}, "http", "")
|
|
sign4 := GetFileInfoSign("12345", "high", []string{"mp3"}, "http", DesktopSignKey)
|
|
|
|
if sign3.Value == "" || sign4.Value == "" {
|
|
t.Error("Both should produce non-empty signatures")
|
|
}
|
|
}
|
|
|
|
// TestTimestampIsRecent tests that timestamp is close to current time
|
|
func TestTimestampIsRecent(t *testing.T) {
|
|
before := time.Now().Unix()
|
|
sign := GetSignRequest("12345", "test-key")
|
|
after := time.Now().Unix()
|
|
|
|
if sign.Timestamp < before || sign.Timestamp > after {
|
|
t.Errorf("Timestamp %d not in range [%d, %d]", sign.Timestamp, before, after)
|
|
}
|
|
}
|