hi-server/pkg/signature/signature_test.go
shanshanzhong 7308aa9191
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 7m37s
无订阅 支付后出现两个订阅
2026-03-05 21:53:36 -08:00

121 lines
3.5 KiB
Go

package signature
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"testing"
"time"
)
type mockNonceStore struct {
seen map[string]bool
}
func newMockNonceStore() *mockNonceStore {
return &mockNonceStore{seen: map[string]bool{}}
}
func (m *mockNonceStore) SetIfNotExists(_ context.Context, appId, nonce string, _ int64) (bool, error) {
key := appId + ":" + nonce
if m.seen[key] {
return true, nil
}
m.seen[key] = true
return false, nil
}
func makeSignature(secret, stringToSign string) string {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(stringToSign))
return hex.EncodeToString(mac.Sum(nil))
}
func TestValidateSuccess(t *testing.T) {
conf := SignatureConf{
AppSecrets: map[string]string{"web-client": "uB4G,XxL2{7b"},
ValidWindowSeconds: 300,
}
v := NewValidator(conf, newMockNonceStore())
ts := strconv.FormatInt(time.Now().Unix(), 10)
nonce := fmt.Sprintf("%x", time.Now().UnixNano())
sts := BuildStringToSign("POST", "/v1/public/order/create", "", []byte(`{"plan_id":1}`), "web-client", ts, nonce)
sig := makeSignature("uB4G,XxL2{7b", sts)
if err := v.Validate(context.Background(), "web-client", ts, nonce, sig, sts); err != nil {
t.Fatalf("expected success, got %v", err)
}
}
func TestValidateExpired(t *testing.T) {
conf := SignatureConf{
AppSecrets: map[string]string{"web-client": "uB4G,XxL2{7b"},
ValidWindowSeconds: 300,
}
v := NewValidator(conf, newMockNonceStore())
ts := strconv.FormatInt(time.Now().Unix()-400, 10)
nonce := "abc"
sts := BuildStringToSign("GET", "/v1/public/user/info", "", nil, "web-client", ts, nonce)
sig := makeSignature("uB4G,XxL2{7b", sts)
if err := v.Validate(context.Background(), "web-client", ts, nonce, sig, sts); err != ErrSignatureExpired {
t.Fatalf("expected ErrSignatureExpired, got %v", err)
}
}
func TestValidateReplay(t *testing.T) {
conf := SignatureConf{
AppSecrets: map[string]string{"web-client": "uB4G,XxL2{7b"},
ValidWindowSeconds: 300,
}
v := NewValidator(conf, newMockNonceStore())
ts := strconv.FormatInt(time.Now().Unix(), 10)
nonce := "same-nonce-replay"
sts := BuildStringToSign("GET", "/v1/public/user/info", "", nil, "web-client", ts, nonce)
sig := makeSignature("uB4G,XxL2{7b", sts)
_ = v.Validate(context.Background(), "web-client", ts, nonce, sig, sts)
if err := v.Validate(context.Background(), "web-client", ts, nonce, sig, sts); err != ErrSignatureReplay {
t.Fatalf("expected ErrSignatureReplay, got %v", err)
}
}
func TestValidateInvalidSignature(t *testing.T) {
conf := SignatureConf{
AppSecrets: map[string]string{"web-client": "uB4G,XxL2{7b"},
ValidWindowSeconds: 300,
}
v := NewValidator(conf, newMockNonceStore())
ts := strconv.FormatInt(time.Now().Unix(), 10)
nonce := "nonce-invalid-sig"
sts := BuildStringToSign("POST", "/v1/public/order/create", "", []byte(`{"plan_id":1}`), "web-client", ts, nonce)
if err := v.Validate(context.Background(), "web-client", ts, nonce, "badsignature", sts); err != ErrSignatureInvalid {
t.Fatalf("expected ErrSignatureInvalid, got %v", err)
}
}
func TestBuildStringToSignCanonicalQuery(t *testing.T) {
got := BuildStringToSign(
"get",
"/v1/public/order/list",
"b=2&a=1&a=3&c=",
nil,
"web-client",
"1700000000",
"nonce-1",
)
want := "GET\n/v1/public/order/list\na=1&b=2&c=\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\nweb-client\n1700000000\nnonce-1"
if got != want {
t.Fatalf("unexpected stringToSign\nwant: %s\ngot: %s", want, got)
}
}