feat: runtime manager
This commit is contained in:
@@ -8,8 +8,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"galaxy/lobby/internal/adapters/gamestub"
|
||||
"galaxy/lobby/internal/adapters/gmclientstub"
|
||||
"galaxy/lobby/internal/adapters/gameinmem"
|
||||
"galaxy/lobby/internal/adapters/mocks"
|
||||
"galaxy/lobby/internal/domain/common"
|
||||
"galaxy/lobby/internal/domain/game"
|
||||
"galaxy/lobby/internal/ports"
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
func silentLogger() *slog.Logger {
|
||||
@@ -33,7 +34,7 @@ func fixedClock(at time.Time) func() time.Time {
|
||||
// source status.
|
||||
func seedGameWithStatus(
|
||||
t *testing.T,
|
||||
store *gamestub.Store,
|
||||
store *gameinmem.Store,
|
||||
id common.GameID,
|
||||
gameType game.GameType,
|
||||
ownerUserID string,
|
||||
@@ -94,13 +95,18 @@ func newService(
|
||||
return svc
|
||||
}
|
||||
|
||||
func newGMMock(t *testing.T) *mocks.MockGMClient {
|
||||
t.Helper()
|
||||
return mocks.NewMockGMClient(gomock.NewController(t))
|
||||
}
|
||||
|
||||
func TestNewServiceRejectsMissingDeps(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := resumegame.NewService(resumegame.Dependencies{})
|
||||
require.Error(t, err)
|
||||
|
||||
_, err = resumegame.NewService(resumegame.Dependencies{Games: gamestub.NewStore()})
|
||||
_, err = resumegame.NewService(resumegame.Dependencies{Games: gameinmem.NewStore()})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -108,10 +114,11 @@ func TestResumeGameAdminHappyPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)
|
||||
store := gamestub.NewStore()
|
||||
store := gameinmem.NewStore()
|
||||
record := seedGameWithStatus(t, store, "game-pub", game.GameTypePublic, "", game.StatusPaused, now)
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
gm := newGMMock(t)
|
||||
gm.EXPECT().Ping(gomock.Any()).Return(nil).Times(1)
|
||||
service := newService(t, store, gm, fixedClock(now.Add(time.Hour)))
|
||||
|
||||
updated, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -120,17 +127,17 @@ func TestResumeGameAdminHappyPath(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, game.StatusRunning, updated.Status)
|
||||
assert.Equal(t, 1, gm.PingCalls())
|
||||
}
|
||||
|
||||
func TestResumeGamePrivateOwnerHappyPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)
|
||||
store := gamestub.NewStore()
|
||||
store := gameinmem.NewStore()
|
||||
record := seedGameWithStatus(t, store, "game-priv", game.GameTypePrivate, "user-owner", game.StatusPaused, now)
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
gm := newGMMock(t)
|
||||
gm.EXPECT().Ping(gomock.Any()).Return(nil).Times(1)
|
||||
service := newService(t, store, gm, fixedClock(now.Add(time.Hour)))
|
||||
|
||||
updated, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -139,17 +146,16 @@ func TestResumeGamePrivateOwnerHappyPath(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, game.StatusRunning, updated.Status)
|
||||
assert.Equal(t, 1, gm.PingCalls())
|
||||
}
|
||||
|
||||
func TestResumeGameRejectsNonOwnerUser(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)
|
||||
store := gamestub.NewStore()
|
||||
store := gameinmem.NewStore()
|
||||
record := seedGameWithStatus(t, store, "game-priv", game.GameTypePrivate, "user-owner", game.StatusPaused, now)
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
gm := newGMMock(t)
|
||||
service := newService(t, store, gm, fixedClock(now.Add(time.Hour)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -157,17 +163,16 @@ func TestResumeGameRejectsNonOwnerUser(t *testing.T) {
|
||||
GameID: record.GameID,
|
||||
})
|
||||
require.ErrorIs(t, err, shared.ErrForbidden)
|
||||
assert.Equal(t, 0, gm.PingCalls(), "ping must not run before authorization passes")
|
||||
}
|
||||
|
||||
func TestResumeGameRejectsUserActorOnPublicGame(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)
|
||||
store := gamestub.NewStore()
|
||||
store := gameinmem.NewStore()
|
||||
record := seedGameWithStatus(t, store, "game-pub", game.GameTypePublic, "", game.StatusPaused, now)
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
gm := newGMMock(t)
|
||||
service := newService(t, store, gm, fixedClock(now.Add(time.Hour)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -175,7 +180,6 @@ func TestResumeGameRejectsUserActorOnPublicGame(t *testing.T) {
|
||||
GameID: record.GameID,
|
||||
})
|
||||
require.ErrorIs(t, err, shared.ErrForbidden)
|
||||
assert.Equal(t, 0, gm.PingCalls())
|
||||
}
|
||||
|
||||
func TestResumeGameRejectsWrongStatuses(t *testing.T) {
|
||||
@@ -197,10 +201,10 @@ func TestResumeGameRejectsWrongStatuses(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)
|
||||
store := gamestub.NewStore()
|
||||
store := gameinmem.NewStore()
|
||||
record := seedGameWithStatus(t, store, "game-x", game.GameTypePublic, "", status, now)
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
gm := newGMMock(t)
|
||||
service := newService(t, store, gm, fixedClock(now.Add(time.Hour)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -208,7 +212,6 @@ func TestResumeGameRejectsWrongStatuses(t *testing.T) {
|
||||
GameID: record.GameID,
|
||||
})
|
||||
require.ErrorIs(t, err, game.ErrConflict)
|
||||
assert.Equal(t, 0, gm.PingCalls(), "ping must not run before status check passes")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -217,11 +220,13 @@ func TestResumeGameGMUnavailableKeepsPaused(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
now := time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)
|
||||
store := gamestub.NewStore()
|
||||
store := gameinmem.NewStore()
|
||||
record := seedGameWithStatus(t, store, "game-pub", game.GameTypePublic, "", game.StatusPaused, now)
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
gm.SetPingError(errors.Join(ports.ErrGMUnavailable, errors.New("dial tcp: connection refused")))
|
||||
gm := newGMMock(t)
|
||||
gm.EXPECT().Ping(gomock.Any()).
|
||||
Return(errors.Join(ports.ErrGMUnavailable, errors.New("dial tcp: connection refused"))).
|
||||
Times(1)
|
||||
service := newService(t, store, gm, fixedClock(now.Add(time.Hour)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -231,7 +236,6 @@ func TestResumeGameGMUnavailableKeepsPaused(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
assert.ErrorIs(t, err, shared.ErrServiceUnavailable)
|
||||
assert.ErrorIs(t, err, ports.ErrGMUnavailable)
|
||||
assert.Equal(t, 1, gm.PingCalls())
|
||||
|
||||
persisted, err := store.Get(context.Background(), record.GameID)
|
||||
require.NoError(t, err)
|
||||
@@ -242,8 +246,8 @@ func TestResumeGameGMUnavailableKeepsPaused(t *testing.T) {
|
||||
func TestResumeGameRejectsMissingRecord(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
store := gamestub.NewStore()
|
||||
gm := newGMMock(t)
|
||||
store := gameinmem.NewStore()
|
||||
service := newService(t, store, gm, fixedClock(time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -251,14 +255,13 @@ func TestResumeGameRejectsMissingRecord(t *testing.T) {
|
||||
GameID: common.GameID("game-missing"),
|
||||
})
|
||||
require.ErrorIs(t, err, game.ErrNotFound)
|
||||
assert.Equal(t, 0, gm.PingCalls())
|
||||
}
|
||||
|
||||
func TestResumeGameInvalidActor(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
store := gamestub.NewStore()
|
||||
gm := newGMMock(t)
|
||||
store := gameinmem.NewStore()
|
||||
service := newService(t, store, gm, fixedClock(time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
@@ -272,8 +275,8 @@ func TestResumeGameInvalidActor(t *testing.T) {
|
||||
func TestResumeGameInvalidGameID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gm := gmclientstub.NewClient()
|
||||
store := gamestub.NewStore()
|
||||
gm := newGMMock(t)
|
||||
store := gameinmem.NewStore()
|
||||
service := newService(t, store, gm, fixedClock(time.Date(2026, 4, 25, 12, 0, 0, 0, time.UTC)))
|
||||
|
||||
_, err := service.Handle(context.Background(), resumegame.Input{
|
||||
|
||||
Reference in New Issue
Block a user