122 lines
3.6 KiB
Go
122 lines
3.6 KiB
Go
package game_test
|
|
|
|
import (
|
|
"bindbox-game/internal/repository/mysql"
|
|
"bindbox-game/internal/service/game"
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"bindbox-game/internal/pkg/logger"
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/redis/go-redis/v9"
|
|
"github.com/stretchr/testify/assert"
|
|
"go.uber.org/zap"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Mock logger
|
|
type MockLogger struct {
|
|
logger.CustomLogger
|
|
}
|
|
|
|
func (l *MockLogger) Info(msg string, fields ...zap.Field) {}
|
|
func (l *MockLogger) Error(msg string, fields ...zap.Field) {}
|
|
func (l *MockLogger) Warn(msg string, fields ...zap.Field) {}
|
|
func (l *MockLogger) Debug(msg string, fields ...zap.Field) {}
|
|
|
|
func TestGenerateToken_FreeMode(t *testing.T) {
|
|
// 1. Setup Miniredis
|
|
mr, err := miniredis.Run()
|
|
assert.NoError(t, err)
|
|
defer mr.Close()
|
|
|
|
rdb := redis.NewClient(&redis.Options{
|
|
Addr: mr.Addr(),
|
|
})
|
|
|
|
// 2. Setup GORM (SQLite in-memory)
|
|
// We use an empty DB to ensure NO ticket exists
|
|
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
|
assert.NoError(t, err)
|
|
|
|
// AutoMigrate to make sure table exists (even if empty) to avoid SQL errors
|
|
// err = db.AutoMigrate(&model.UserGameTickets{})
|
|
// assert.NoError(t, err)
|
|
|
|
repo := mysql.NewTestRepo(db)
|
|
|
|
// 3. Create Service
|
|
svc := game.NewGameTokenService(&MockLogger{}, repo, rdb)
|
|
|
|
// 4. Test Case: minesweeper_free
|
|
ctx := context.Background()
|
|
userID := int64(12345)
|
|
username := "testuser"
|
|
gameCode := "minesweeper_free"
|
|
|
|
// Should succeed even though DB is empty (bypasses ticket check)
|
|
token, ticket, expiresAt, err := svc.GenerateToken(ctx, userID, username, "", gameCode)
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, token)
|
|
assert.NotEmpty(t, ticket)
|
|
assert.True(t, expiresAt.After(time.Now()))
|
|
|
|
// 5. Verify Redis Key Format
|
|
// Expected: "userID:gameCode"
|
|
ticketKey := fmt.Sprintf("game:token:ticket:%s", ticket)
|
|
val, err := rdb.Get(ctx, ticketKey).Result()
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, fmt.Sprintf("%d:%s", userID, gameCode), val)
|
|
|
|
// 6. Test Validation
|
|
claims, err := svc.ValidateToken(ctx, token)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, userID, claims.UserID)
|
|
assert.Equal(t, gameCode, claims.GameType)
|
|
}
|
|
|
|
func TestGenerateToken_PaidMode_NoTicket(t *testing.T) {
|
|
// 1. Setup
|
|
mr, err := miniredis.Run()
|
|
assert.NoError(t, err)
|
|
defer mr.Close()
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
db, _ := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
|
// db.AutoMigrate(&model.UserGameTickets{}) // Empty table
|
|
repo := mysql.NewTestRepo(db)
|
|
svc := game.NewGameTokenService(&MockLogger{}, repo, rdb)
|
|
|
|
// 2. Test Case: normal game
|
|
// Should FAIL because no ticket in DB
|
|
_, _, _, err = svc.GenerateToken(context.Background(), 12345, "user", "", "minesweeper_paid")
|
|
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "no available game tickets")
|
|
}
|
|
|
|
func TestValidateToken_LegacyFormat_ShouldFail(t *testing.T) {
|
|
// Test that strict mode rejects legacy keys
|
|
mr, _ := miniredis.Run()
|
|
defer mr.Close()
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
|
|
svc := game.NewGameTokenService(&MockLogger{}, mysql.NewTestRepo(nil), rdb)
|
|
userID := int64(999)
|
|
|
|
// Generate valid token first
|
|
token, ticket, _, _ := svc.GenerateToken(context.Background(), userID, "user", "", "minesweeper_free")
|
|
|
|
// Overwrite Redis with legacy format (just userID)
|
|
rdb.Set(context.Background(), "game:token:ticket:"+ticket, fmt.Sprintf("%d", userID), time.Hour)
|
|
|
|
// Now Validate - SHOULD FAIL
|
|
_, err := svc.ValidateToken(context.Background(), token)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "invalid ticket format")
|
|
}
|