feat: runtime manager

This commit is contained in:
Ilia Denisov
2026-04-28 20:39:18 +02:00
committed by GitHub
parent e0a99b346b
commit a7cee15115
289 changed files with 45660 additions and 2207 deletions
@@ -5,12 +5,13 @@ import (
"errors"
"io"
"log/slog"
"sync"
"testing"
"time"
"galaxy/lobby/internal/adapters/gamestub"
"galaxy/lobby/internal/adapters/intentpubstub"
"galaxy/lobby/internal/adapters/invitestub"
"galaxy/lobby/internal/adapters/gameinmem"
"galaxy/lobby/internal/adapters/inviteinmem"
"galaxy/lobby/internal/adapters/mocks"
"galaxy/lobby/internal/domain/common"
"galaxy/lobby/internal/domain/game"
"galaxy/lobby/internal/domain/invite"
@@ -20,6 +21,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
const (
@@ -30,20 +32,57 @@ const (
func silentLogger() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) }
type intentRec struct {
mu sync.Mutex
published []notificationintent.Intent
err error
}
func (r *intentRec) record(_ context.Context, intent notificationintent.Intent) (string, error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.err != nil {
return "", r.err
}
r.published = append(r.published, intent)
return "1", nil
}
func (r *intentRec) snapshot() []notificationintent.Intent {
r.mu.Lock()
defer r.mu.Unlock()
return append([]notificationintent.Intent(nil), r.published...)
}
func (r *intentRec) setErr(err error) {
r.mu.Lock()
defer r.mu.Unlock()
r.err = err
}
func newIntentMock(t *testing.T, rec *intentRec) *mocks.MockIntentPublisher {
t.Helper()
m := mocks.NewMockIntentPublisher(gomock.NewController(t))
m.EXPECT().Publish(gomock.Any(), gomock.Any()).DoAndReturn(rec.record).AnyTimes()
return m
}
type closeFixture struct {
now time.Time
games *gamestub.Store
invites *invitestub.Store
intents *intentpubstub.Publisher
game game.Game
now time.Time
games *gameinmem.Store
invites *inviteinmem.Store
intentRec *intentRec
intents *mocks.MockIntentPublisher
game game.Game
}
func newCloseFixture(t *testing.T) *closeFixture {
t.Helper()
now := time.Date(2026, 4, 25, 10, 0, 0, 0, time.UTC)
games := gamestub.NewStore()
invites := invitestub.NewStore()
intents := intentpubstub.NewPublisher()
games := gameinmem.NewStore()
invites := inviteinmem.NewStore()
rec := &intentRec{}
intents := newIntentMock(t, rec)
gameRecord, err := game.New(game.NewGameInput{
GameID: closeGameID,
@@ -64,11 +103,12 @@ func newCloseFixture(t *testing.T) *closeFixture {
require.NoError(t, games.Save(context.Background(), gameRecord))
return &closeFixture{
now: now,
games: games,
invites: invites,
intents: intents,
game: gameRecord,
now: now,
games: games,
invites: invites,
intentRec: rec,
intents: intents,
game: gameRecord,
}
}
@@ -120,7 +160,7 @@ func TestCloseEnrollmentTransitionsGameAndExpiresInvites(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, invite.StatusExpired, second.Status)
intents := f.intents.Published()
intents := f.intentRec.snapshot()
require.Len(t, intents, 2)
for _, intent := range intents {
assert.Equal(t, notificationintent.NotificationTypeLobbyInviteExpired, intent.NotificationType)
@@ -158,7 +198,7 @@ func TestCloseEnrollmentLeavesNonCreatedInvitesUntouched(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, invite.StatusDeclined, declinedAfter.Status)
intents := f.intents.Published()
intents := f.intentRec.snapshot()
require.Len(t, intents, 1)
}
@@ -184,14 +224,14 @@ func TestCloseEnrollmentSurfacesGameConflict(t *testing.T) {
stillCreated, err := f.invites.Get(context.Background(), "invite-1")
require.NoError(t, err)
assert.Equal(t, invite.StatusCreated, stillCreated.Status)
assert.Empty(t, f.intents.Published())
assert.Empty(t, f.intentRec.snapshot())
}
func TestCloseEnrollmentSwallowsIntentPublishFailure(t *testing.T) {
t.Parallel()
f := newCloseFixture(t)
f.addCreatedInvite(t, "invite-1", "user-a")
f.intents.SetError(errors.New("publisher offline"))
f.intentRec.setErr(errors.New("publisher offline"))
updated, err := shared.CloseEnrollment(
context.Background(),
@@ -221,7 +261,7 @@ func TestCloseEnrollmentIsIdempotentOnSecondCall(t *testing.T) {
f.now.Add(time.Minute),
)
require.NoError(t, err)
assert.Len(t, f.intents.Published(), 1)
assert.Len(t, f.intentRec.snapshot(), 1)
_, err = shared.CloseEnrollment(
context.Background(),
@@ -231,7 +271,7 @@ func TestCloseEnrollmentIsIdempotentOnSecondCall(t *testing.T) {
f.now.Add(2*time.Minute),
)
require.ErrorIs(t, err, game.ErrConflict)
assert.Len(t, f.intents.Published(), 1)
assert.Len(t, f.intentRec.snapshot(), 1)
}
func TestCloseEnrollmentRejectsUnknownTrigger(t *testing.T) {