feat: runtime manager
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatusIsKnown(t *testing.T) {
|
||||
for _, status := range AllStatuses() {
|
||||
assert.Truef(t, status.IsKnown(), "expected %q known", status)
|
||||
}
|
||||
|
||||
assert.False(t, Status("").IsKnown())
|
||||
assert.False(t, Status("unknown").IsKnown())
|
||||
}
|
||||
|
||||
func TestStatusIsTerminal(t *testing.T) {
|
||||
assert.True(t, StatusRemoved.IsTerminal())
|
||||
|
||||
for _, status := range []Status{StatusRunning, StatusStopped} {
|
||||
assert.Falsef(t, status.IsTerminal(), "expected %q non-terminal", status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllStatuses(t *testing.T) {
|
||||
statuses := AllStatuses()
|
||||
|
||||
assert.ElementsMatch(t,
|
||||
[]Status{StatusRunning, StatusStopped, StatusRemoved},
|
||||
statuses,
|
||||
)
|
||||
|
||||
statuses[0] = "tampered"
|
||||
assert.Equal(t, StatusRunning, AllStatuses()[0],
|
||||
"AllStatuses must return an independent slice")
|
||||
}
|
||||
|
||||
func runningRecord() RuntimeRecord {
|
||||
created := time.Date(2026, 4, 27, 12, 0, 0, 0, time.UTC)
|
||||
started := created.Add(time.Second)
|
||||
return RuntimeRecord{
|
||||
GameID: "game-test",
|
||||
Status: StatusRunning,
|
||||
CurrentContainerID: "container-1",
|
||||
CurrentImageRef: "galaxy/game:1.0.0",
|
||||
EngineEndpoint: "http://galaxy-game-game-test:8080",
|
||||
StatePath: "/var/lib/galaxy/games/game-test",
|
||||
DockerNetwork: "galaxy-net",
|
||||
StartedAt: &started,
|
||||
LastOpAt: started,
|
||||
CreatedAt: created,
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRunningHappy(t *testing.T) {
|
||||
require.NoError(t, runningRecord().Validate())
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateStoppedHappy(t *testing.T) {
|
||||
record := runningRecord()
|
||||
stopped := record.StartedAt.Add(time.Minute)
|
||||
record.Status = StatusStopped
|
||||
record.StoppedAt = &stopped
|
||||
record.LastOpAt = stopped
|
||||
|
||||
require.NoError(t, record.Validate())
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRemovedHappy(t *testing.T) {
|
||||
record := runningRecord()
|
||||
stopped := record.StartedAt.Add(time.Minute)
|
||||
removed := stopped.Add(time.Minute)
|
||||
record.Status = StatusRemoved
|
||||
record.StoppedAt = &stopped
|
||||
record.RemovedAt = &removed
|
||||
record.CurrentContainerID = ""
|
||||
record.LastOpAt = removed
|
||||
|
||||
require.NoError(t, record.Validate())
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRejects(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
mutate func(*RuntimeRecord)
|
||||
}{
|
||||
{"empty game id", func(r *RuntimeRecord) { r.GameID = "" }},
|
||||
{"unknown status", func(r *RuntimeRecord) { r.Status = "exotic" }},
|
||||
{"empty engine endpoint", func(r *RuntimeRecord) { r.EngineEndpoint = "" }},
|
||||
{"empty state path", func(r *RuntimeRecord) { r.StatePath = "" }},
|
||||
{"empty docker network", func(r *RuntimeRecord) { r.DockerNetwork = "" }},
|
||||
{"zero last op at", func(r *RuntimeRecord) { r.LastOpAt = time.Time{} }},
|
||||
{"zero created at", func(r *RuntimeRecord) { r.CreatedAt = time.Time{} }},
|
||||
{"last op at before created at", func(r *RuntimeRecord) {
|
||||
r.LastOpAt = r.CreatedAt.Add(-time.Second)
|
||||
}},
|
||||
{"running without container id", func(r *RuntimeRecord) {
|
||||
r.CurrentContainerID = ""
|
||||
}},
|
||||
{"running without image ref", func(r *RuntimeRecord) {
|
||||
r.CurrentImageRef = ""
|
||||
}},
|
||||
{"running without started at", func(r *RuntimeRecord) {
|
||||
r.StartedAt = nil
|
||||
}},
|
||||
{"started at before created at", func(r *RuntimeRecord) {
|
||||
before := r.CreatedAt.Add(-time.Second)
|
||||
r.StartedAt = &before
|
||||
}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
record := runningRecord()
|
||||
tt.mutate(&record)
|
||||
assert.Error(t, record.Validate())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRejectsStoppedWithoutStoppedAt(t *testing.T) {
|
||||
record := runningRecord()
|
||||
record.Status = StatusStopped
|
||||
record.StoppedAt = nil
|
||||
|
||||
assert.Error(t, record.Validate())
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRejectsStoppedBeforeStarted(t *testing.T) {
|
||||
record := runningRecord()
|
||||
stopped := record.StartedAt.Add(-time.Second)
|
||||
record.Status = StatusStopped
|
||||
record.StoppedAt = &stopped
|
||||
|
||||
assert.Error(t, record.Validate())
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRejectsRemovedWithoutRemovedAt(t *testing.T) {
|
||||
record := runningRecord()
|
||||
record.Status = StatusRemoved
|
||||
record.RemovedAt = nil
|
||||
|
||||
assert.Error(t, record.Validate())
|
||||
}
|
||||
|
||||
func TestRuntimeRecordValidateRejectsRemovedBeforeCreated(t *testing.T) {
|
||||
record := runningRecord()
|
||||
before := record.CreatedAt.Add(-time.Second)
|
||||
record.Status = StatusRemoved
|
||||
record.RemovedAt = &before
|
||||
|
||||
assert.Error(t, record.Validate())
|
||||
}
|
||||
Reference in New Issue
Block a user