392 lines
11 KiB
Go
392 lines
11 KiB
Go
package gameopen
|
|
|
|
import (
|
|
"chatapp3-golang/internal/config"
|
|
"chatapp3-golang/internal/integration"
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
type stubGateway struct {
|
|
credential integration.UserCredential
|
|
token string
|
|
authToken string
|
|
profile integration.UserProfile
|
|
accountMap map[string]integration.UserProfile
|
|
balance int64
|
|
exists bool
|
|
cmd integration.GoldReceiptCommand
|
|
}
|
|
|
|
func (s *stubGateway) AuthenticateToken(_ context.Context, token string) (integration.UserCredential, error) {
|
|
s.authToken = token
|
|
return s.credential, nil
|
|
}
|
|
|
|
func (s *stubGateway) CreateIfAbsentUserCredentialToken(context.Context, int64) (string, error) {
|
|
return s.token, nil
|
|
}
|
|
|
|
func (s *stubGateway) GetUserProfile(context.Context, int64) (integration.UserProfile, error) {
|
|
return s.profile, nil
|
|
}
|
|
|
|
func (s *stubGateway) GetUserProfileByAccount(_ context.Context, _ string, account string) (integration.UserProfile, error) {
|
|
if profile, ok := s.accountMap[account]; ok {
|
|
return profile, nil
|
|
}
|
|
return integration.UserProfile{}, context.Canceled
|
|
}
|
|
|
|
func (s *stubGateway) GetUserProfileByAccountOne(_ context.Context, account string) (integration.UserProfile, error) {
|
|
if profile, ok := s.accountMap[account]; ok {
|
|
return profile, nil
|
|
}
|
|
return integration.UserProfile{}, context.Canceled
|
|
}
|
|
|
|
func (s *stubGateway) MapGoldBalance(context.Context, []int64) (map[int64]int64, error) {
|
|
result := map[int64]int64{}
|
|
if int64(s.credential.UserID) > 0 {
|
|
result[int64(s.credential.UserID)] = s.balance
|
|
}
|
|
if int64(s.profile.ID) > 0 {
|
|
result[int64(s.profile.ID)] = s.balance
|
|
}
|
|
for _, profile := range s.accountMap {
|
|
if int64(profile.ID) > 0 {
|
|
result[int64(profile.ID)] = s.balance
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (s *stubGateway) ExistsGoldEvent(context.Context, string) (bool, error) {
|
|
return s.exists, nil
|
|
}
|
|
|
|
func (s *stubGateway) ChangeGoldBalance(_ context.Context, cmd integration.GoldReceiptCommand) error {
|
|
s.cmd = cmd
|
|
return nil
|
|
}
|
|
|
|
func TestHandleQueryUser(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
|
|
profile: integration.UserProfile{
|
|
ID: 1234567,
|
|
Account: "1234567",
|
|
UserNickname: "tester",
|
|
UserAvatar: "https://example.com/avatar.png",
|
|
},
|
|
balance: 88,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := QueryUserRequest{
|
|
GameID: "101",
|
|
UID: "1234567",
|
|
Token: "token",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildQueryUserSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleQueryUser(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleQueryUser() error = %+v", resp)
|
|
}
|
|
data, ok := resp.Data.(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("HandleQueryUser() data type = %T", resp.Data)
|
|
}
|
|
if got := data["coin"]; got != int64(88) {
|
|
t.Fatalf("HandleQueryUser() coin = %v, want 88", got)
|
|
}
|
|
}
|
|
|
|
func TestHandleQueryUserNormalizesCallbackTokenAfterSignVerify(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
token string
|
|
want string
|
|
}{
|
|
{name: "missing_padding", token: "token-sign.YWJjZA", want: "token-sign.YWJjZA=="},
|
|
{name: "encoded_padding", token: "token-sign.YWJjZA%3D%3D", want: "token-sign.YWJjZA=="},
|
|
{name: "encoded_bearer_missing_padding", token: "Bearer%20token-sign.YWJjZA", want: "token-sign.YWJjZA=="},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
|
|
profile: integration.UserProfile{
|
|
ID: 1234567,
|
|
Account: "1234567",
|
|
},
|
|
balance: 88,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := QueryUserRequest{
|
|
GameID: "101",
|
|
UID: "1234567",
|
|
Token: tt.token,
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildQueryUserSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleQueryUser(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleQueryUser() error = %+v", resp)
|
|
}
|
|
if gateway.authToken != tt.want {
|
|
t.Fatalf("AuthenticateToken() token = %q, want %q", gateway.authToken, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHandleUpdateCoin(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
|
|
profile: integration.UserProfile{
|
|
ID: 1234567,
|
|
Account: "1234567",
|
|
},
|
|
balance: 99,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := UpdateCoinRequest{
|
|
OrderID: "order-1",
|
|
GameID: "101",
|
|
RoundID: "round-1",
|
|
UID: "1234567",
|
|
Coin: 15,
|
|
Type: 2,
|
|
RewardType: 2,
|
|
Token: "token",
|
|
WinID: "win-1",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildUpdateCoinSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleUpdateCoin(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleUpdateCoin() error = %+v", resp)
|
|
}
|
|
if gateway.cmd.EventID != "GAME_OPEN:order-1" {
|
|
t.Fatalf("HandleUpdateCoin() event id = %q", gateway.cmd.EventID)
|
|
}
|
|
if gateway.cmd.ReceiptType != "INCOME" {
|
|
t.Fatalf("HandleUpdateCoin() receipt type = %q", gateway.cmd.ReceiptType)
|
|
}
|
|
}
|
|
|
|
func TestHandleUpdateCoinNormalizesCallbackTokenAfterSignVerify(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
|
|
profile: integration.UserProfile{
|
|
ID: 1234567,
|
|
Account: "1234567",
|
|
},
|
|
balance: 99,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := UpdateCoinRequest{
|
|
OrderID: "order-1",
|
|
GameID: "101",
|
|
RoundID: "round-1",
|
|
UID: "1234567",
|
|
Coin: 15,
|
|
Type: 2,
|
|
RewardType: 2,
|
|
Token: "Bearer%20token-sign.YWJjZA",
|
|
WinID: "win-1",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildUpdateCoinSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleUpdateCoin(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleUpdateCoin() error = %+v", resp)
|
|
}
|
|
if gateway.authToken != "token-sign.YWJjZA==" {
|
|
t.Fatalf("AuthenticateToken() token = %q, want token-sign.YWJjZA==", gateway.authToken)
|
|
}
|
|
}
|
|
|
|
func TestHandleUpdateCoinDuplicateOrderReturns10003(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
|
|
profile: integration.UserProfile{
|
|
ID: 1234567,
|
|
Account: "1234567",
|
|
},
|
|
balance: 99,
|
|
exists: true,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := UpdateCoinRequest{
|
|
OrderID: "27822d8ba4a34d0e8db3e049716d427d",
|
|
GameID: "101",
|
|
RoundID: "round-1",
|
|
UID: "1234567",
|
|
Coin: 15,
|
|
Type: 2,
|
|
RewardType: 2,
|
|
Token: "token",
|
|
WinID: "win-1",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildUpdateCoinSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleUpdateCoin(context.Background(), req, "{}")
|
|
if resp.ErrorCode != gameOpenCodeDuplicateOrder {
|
|
t.Fatalf("HandleUpdateCoin() errorCode = %d, want %d", resp.ErrorCode, gameOpenCodeDuplicateOrder)
|
|
}
|
|
if resp.ErrorMsg != "duplicate orderId" {
|
|
t.Fatalf("HandleUpdateCoin() errorMsg = %q", resp.ErrorMsg)
|
|
}
|
|
if gateway.cmd.EventID != "" {
|
|
t.Fatalf("HandleUpdateCoin() should not change balance for duplicate order, got event id %q", gateway.cmd.EventID)
|
|
}
|
|
}
|
|
|
|
func TestHandleQueryUserAcceptsAccountUID(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 4569421795454615552, SysOrigin: "LIKEI"},
|
|
profile: integration.UserProfile{
|
|
ID: 4569421795454615552,
|
|
Account: "1234567",
|
|
UserNickname: "tester",
|
|
UserAvatar: "https://example.com/avatar.png",
|
|
},
|
|
accountMap: map[string]integration.UserProfile{
|
|
"1234567": {
|
|
ID: 4569421795454615552,
|
|
Account: "1234567",
|
|
UserNickname: "tester",
|
|
UserAvatar: "https://example.com/avatar.png",
|
|
},
|
|
},
|
|
balance: 88,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := QueryUserRequest{
|
|
GameID: "101",
|
|
UID: "1234567",
|
|
Token: "token",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildQueryUserSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleQueryUser(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleQueryUser() error = %+v", resp)
|
|
}
|
|
}
|
|
|
|
func TestHandleUpdateCoinAcceptsAccountUID(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
credential: integration.UserCredential{UserID: 4569421795454615552, SysOrigin: "LIKEI"},
|
|
accountMap: map[string]integration.UserProfile{
|
|
"1234567": {
|
|
ID: 4569421795454615552,
|
|
Account: "1234567",
|
|
},
|
|
},
|
|
balance: 99,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := UpdateCoinRequest{
|
|
OrderID: "order-1",
|
|
GameID: "101",
|
|
RoundID: "round-1",
|
|
UID: "1234567",
|
|
Coin: 15,
|
|
Type: 2,
|
|
RewardType: 2,
|
|
Token: "token",
|
|
WinID: "win-1",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildUpdateCoinSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleUpdateCoin(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleUpdateCoin() error = %+v", resp)
|
|
}
|
|
}
|
|
|
|
func TestHandleSupplementAcceptsAccountUID(t *testing.T) {
|
|
gateway := &stubGateway{
|
|
accountMap: map[string]integration.UserProfile{
|
|
"1234567": {
|
|
ID: 4569421795454615552,
|
|
Account: "1234567",
|
|
},
|
|
},
|
|
balance: 66,
|
|
}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
|
|
}, nil, gateway)
|
|
req := SupplementRequest{
|
|
OrderID: "order-1",
|
|
GameID: "101",
|
|
RoundID: "round-1",
|
|
UID: "1234567",
|
|
Coin: 1,
|
|
RewardType: 1,
|
|
WinID: "win-1",
|
|
RoomID: "room-1",
|
|
}
|
|
req.Sign = buildSupplementSign(req, "game-open-test-key")
|
|
|
|
resp := service.HandleSupplement(context.Background(), req, "{}")
|
|
if resp.ErrorCode != 0 {
|
|
t.Fatalf("HandleSupplement() error = %+v", resp)
|
|
}
|
|
data, ok := resp.Data.(map[string]any)
|
|
if !ok {
|
|
t.Fatalf("HandleSupplement() data type = %T", resp.Data)
|
|
}
|
|
if got := data["coin"]; got != int64(66) {
|
|
t.Fatalf("HandleSupplement() coin = %v, want 66", got)
|
|
}
|
|
}
|
|
|
|
func TestGetIntegrationInfo(t *testing.T) {
|
|
gateway := &stubGateway{token: "token-123"}
|
|
service := NewGameOpenService(config.Config{
|
|
GameOpen: config.GameOpenConfig{
|
|
AppKey: "game-open-test-key",
|
|
TestUserID: 1234567,
|
|
TestAccount: "1234567",
|
|
TestPassword: "1234567",
|
|
},
|
|
}, nil, gateway)
|
|
|
|
info, err := service.GetIntegrationInfo(context.Background(), "https://jvapi.haiyihy.com")
|
|
if err != nil {
|
|
t.Fatalf("GetIntegrationInfo() error = %v", err)
|
|
}
|
|
if info.QueryUser != "https://jvapi.haiyihy.com/game/open/user-info" {
|
|
t.Fatalf("GetIntegrationInfo() query url = %q", info.QueryUser)
|
|
}
|
|
if info.Token != "token-123" {
|
|
t.Fatalf("GetIntegrationInfo() token = %q", info.Token)
|
|
}
|
|
}
|