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
@@ -0,0 +1,207 @@
package operationlogstore_test
import (
"context"
"testing"
"time"
"galaxy/rtmanager/internal/adapters/postgres/internal/pgtest"
"galaxy/rtmanager/internal/adapters/postgres/operationlogstore"
"galaxy/rtmanager/internal/domain/operation"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) { pgtest.RunMain(m) }
func newStore(t *testing.T) *operationlogstore.Store {
t.Helper()
pgtest.TruncateAll(t)
store, err := operationlogstore.New(operationlogstore.Config{
DB: pgtest.Ensure(t).Pool(),
OperationTimeout: pgtest.OperationTimeout,
})
require.NoError(t, err)
return store
}
func successStartEntry(gameID string, startedAt time.Time, sourceRef string) operation.OperationEntry {
finishedAt := startedAt.Add(time.Second)
return operation.OperationEntry{
GameID: gameID,
OpKind: operation.OpKindStart,
OpSource: operation.OpSourceLobbyStream,
SourceRef: sourceRef,
ImageRef: "galaxy/game:v1.2.3",
ContainerID: "container-1",
Outcome: operation.OutcomeSuccess,
StartedAt: startedAt,
FinishedAt: &finishedAt,
}
}
func TestAppendReturnsPositiveIDs(t *testing.T) {
ctx := context.Background()
store := newStore(t)
startedAt := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
id1, err := store.Append(ctx, successStartEntry("game-001", startedAt, "1700000000000-0"))
require.NoError(t, err)
assert.Greater(t, id1, int64(0))
id2, err := store.Append(ctx, successStartEntry("game-001", startedAt.Add(time.Minute), "1700000000001-0"))
require.NoError(t, err)
assert.Greater(t, id2, id1)
}
func TestAppendValidatesEntry(t *testing.T) {
ctx := context.Background()
store := newStore(t)
tests := []struct {
name string
mutate func(*operation.OperationEntry)
}{
{"empty game id", func(e *operation.OperationEntry) { e.GameID = "" }},
{"unknown op kind", func(e *operation.OperationEntry) { e.OpKind = "exotic" }},
{"unknown op source", func(e *operation.OperationEntry) { e.OpSource = "exotic" }},
{"unknown outcome", func(e *operation.OperationEntry) { e.Outcome = "exotic" }},
{"zero started at", func(e *operation.OperationEntry) { e.StartedAt = time.Time{} }},
{"failure without error code", func(e *operation.OperationEntry) {
e.Outcome = operation.OutcomeFailure
e.ErrorCode = ""
}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
entry := successStartEntry("game-001",
time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC), "ref")
tt.mutate(&entry)
_, err := store.Append(ctx, entry)
require.Error(t, err)
})
}
}
func TestListByGameReturnsEntriesNewestFirst(t *testing.T) {
ctx := context.Background()
store := newStore(t)
base := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
for index := range 3 {
_, err := store.Append(ctx, successStartEntry("game-001",
base.Add(time.Duration(index)*time.Minute),
"ref-game-001-"))
require.NoError(t, err)
}
// Foreign-game entry must not appear in the list.
_, err := store.Append(ctx, successStartEntry("game-other", base, "ref-other"))
require.NoError(t, err)
entries, err := store.ListByGame(ctx, "game-001", 10)
require.NoError(t, err)
require.Len(t, entries, 3)
for index := range 2 {
assert.True(t,
!entries[index].StartedAt.Before(entries[index+1].StartedAt),
"entries must be ordered started_at DESC; got %s before %s",
entries[index].StartedAt, entries[index+1].StartedAt,
)
}
}
func TestListByGameRespectsLimit(t *testing.T) {
ctx := context.Background()
store := newStore(t)
base := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
for index := range 5 {
_, err := store.Append(ctx, successStartEntry("game-001",
base.Add(time.Duration(index)*time.Minute), "ref"))
require.NoError(t, err)
}
entries, err := store.ListByGame(ctx, "game-001", 2)
require.NoError(t, err)
require.Len(t, entries, 2)
}
func TestListByGameReturnsEmptyForUnknownGame(t *testing.T) {
ctx := context.Background()
store := newStore(t)
entries, err := store.ListByGame(ctx, "game-missing", 10)
require.NoError(t, err)
assert.Empty(t, entries)
}
func TestListByGameRejectsInvalidArgs(t *testing.T) {
ctx := context.Background()
store := newStore(t)
_, err := store.ListByGame(ctx, "", 10)
require.Error(t, err)
_, err = store.ListByGame(ctx, "game-001", 0)
require.Error(t, err)
_, err = store.ListByGame(ctx, "game-001", -3)
require.Error(t, err)
}
func TestAppendRoundTripsAllFields(t *testing.T) {
ctx := context.Background()
store := newStore(t)
startedAt := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
finishedAt := startedAt.Add(2 * time.Second)
original := operation.OperationEntry{
GameID: "game-001",
OpKind: operation.OpKindStop,
OpSource: operation.OpSourceGMRest,
SourceRef: "request-7",
ImageRef: "galaxy/game:v2.0.0",
ContainerID: "container-X",
Outcome: operation.OutcomeFailure,
ErrorCode: "container_start_failed",
ErrorMessage: "stop deadline exceeded",
StartedAt: startedAt,
FinishedAt: &finishedAt,
}
id, err := store.Append(ctx, original)
require.NoError(t, err)
entries, err := store.ListByGame(ctx, "game-001", 10)
require.NoError(t, err)
require.Len(t, entries, 1)
got := entries[0]
assert.Equal(t, id, got.ID)
assert.Equal(t, original.GameID, got.GameID)
assert.Equal(t, original.OpKind, got.OpKind)
assert.Equal(t, original.OpSource, got.OpSource)
assert.Equal(t, original.SourceRef, got.SourceRef)
assert.Equal(t, original.ImageRef, got.ImageRef)
assert.Equal(t, original.ContainerID, got.ContainerID)
assert.Equal(t, original.Outcome, got.Outcome)
assert.Equal(t, original.ErrorCode, got.ErrorCode)
assert.Equal(t, original.ErrorMessage, got.ErrorMessage)
assert.True(t, original.StartedAt.Equal(got.StartedAt))
require.NotNil(t, got.FinishedAt)
assert.True(t, original.FinishedAt.Equal(*got.FinishedAt))
assert.Equal(t, time.UTC, got.StartedAt.Location())
assert.Equal(t, time.UTC, got.FinishedAt.Location())
}
func TestNewRejectsNilDB(t *testing.T) {
_, err := operationlogstore.New(operationlogstore.Config{OperationTimeout: time.Second})
require.Error(t, err)
}
func TestNewRejectsNonPositiveTimeout(t *testing.T) {
_, err := operationlogstore.New(operationlogstore.Config{
DB: pgtest.Ensure(t).Pool(),
})
require.Error(t, err)
}