122 lines
3.6 KiB
Go
Executable File

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")
}