feat: game lobby service
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatusIsKnown(t *testing.T) {
|
||||
for _, status := range []Status{
|
||||
StatusDraft,
|
||||
StatusEnrollmentOpen,
|
||||
StatusReadyToStart,
|
||||
StatusStarting,
|
||||
StatusStartFailed,
|
||||
StatusRunning,
|
||||
StatusPaused,
|
||||
StatusFinished,
|
||||
StatusCancelled,
|
||||
} {
|
||||
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, StatusFinished.IsTerminal())
|
||||
assert.True(t, StatusCancelled.IsTerminal())
|
||||
|
||||
for _, status := range []Status{
|
||||
StatusDraft,
|
||||
StatusEnrollmentOpen,
|
||||
StatusReadyToStart,
|
||||
StatusStarting,
|
||||
StatusStartFailed,
|
||||
StatusRunning,
|
||||
StatusPaused,
|
||||
} {
|
||||
assert.Falsef(t, status.IsTerminal(), "expected %q non-terminal", status)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTriggerIsKnown(t *testing.T) {
|
||||
for _, trigger := range []Trigger{
|
||||
TriggerCommand,
|
||||
TriggerManual,
|
||||
TriggerDeadline,
|
||||
TriggerGap,
|
||||
TriggerRuntimeEvent,
|
||||
TriggerExternalBlock,
|
||||
} {
|
||||
assert.Truef(t, trigger.IsKnown(), "expected %q known", trigger)
|
||||
}
|
||||
|
||||
assert.False(t, Trigger("").IsKnown())
|
||||
assert.False(t, Trigger("bogus").IsKnown())
|
||||
}
|
||||
|
||||
func TestTransitionHappyPathsCoverFrozenTable(t *testing.T) {
|
||||
cases := []struct {
|
||||
from Status
|
||||
to Status
|
||||
triggers []Trigger
|
||||
}{
|
||||
{StatusDraft, StatusEnrollmentOpen, []Trigger{TriggerCommand}},
|
||||
{StatusEnrollmentOpen, StatusReadyToStart, []Trigger{TriggerManual, TriggerDeadline, TriggerGap}},
|
||||
{StatusReadyToStart, StatusStarting, []Trigger{TriggerCommand}},
|
||||
{StatusStarting, StatusRunning, []Trigger{TriggerRuntimeEvent}},
|
||||
{StatusStarting, StatusPaused, []Trigger{TriggerRuntimeEvent}},
|
||||
{StatusStarting, StatusStartFailed, []Trigger{TriggerRuntimeEvent}},
|
||||
{StatusStartFailed, StatusReadyToStart, []Trigger{TriggerCommand}},
|
||||
{StatusRunning, StatusPaused, []Trigger{TriggerCommand}},
|
||||
{StatusRunning, StatusFinished, []Trigger{TriggerRuntimeEvent}},
|
||||
{StatusPaused, StatusRunning, []Trigger{TriggerCommand}},
|
||||
{StatusPaused, StatusFinished, []Trigger{TriggerRuntimeEvent}},
|
||||
{StatusDraft, StatusCancelled, []Trigger{TriggerCommand, TriggerExternalBlock}},
|
||||
{StatusEnrollmentOpen, StatusCancelled, []Trigger{TriggerCommand, TriggerExternalBlock}},
|
||||
{StatusReadyToStart, StatusCancelled, []Trigger{TriggerCommand, TriggerExternalBlock}},
|
||||
{StatusStartFailed, StatusCancelled, []Trigger{TriggerCommand, TriggerExternalBlock}},
|
||||
{StatusStarting, StatusCancelled, []Trigger{TriggerExternalBlock}},
|
||||
{StatusRunning, StatusCancelled, []Trigger{TriggerExternalBlock}},
|
||||
{StatusPaused, StatusCancelled, []Trigger{TriggerExternalBlock}},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
for _, trigger := range tc.triggers {
|
||||
t.Run(string(tc.from)+"->"+string(tc.to)+"/"+string(trigger), func(t *testing.T) {
|
||||
require.NoError(t, Transition(tc.from, tc.to, trigger))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransitionRejectsUnknownPair(t *testing.T) {
|
||||
err := Transition(StatusDraft, StatusRunning, TriggerCommand)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, ErrInvalidTransition))
|
||||
|
||||
var typed *InvalidTransitionError
|
||||
require.True(t, errors.As(err, &typed))
|
||||
assert.Equal(t, StatusDraft, typed.From)
|
||||
assert.Equal(t, StatusRunning, typed.To)
|
||||
assert.Equal(t, TriggerCommand, typed.Trigger)
|
||||
}
|
||||
|
||||
func TestTransitionRejectsWrongTrigger(t *testing.T) {
|
||||
err := Transition(StatusDraft, StatusEnrollmentOpen, TriggerDeadline)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, ErrInvalidTransition))
|
||||
}
|
||||
|
||||
func TestTransitionRejectsUnknownStatusOrTrigger(t *testing.T) {
|
||||
require.Error(t, Transition(Status("bogus"), StatusEnrollmentOpen, TriggerCommand))
|
||||
require.Error(t, Transition(StatusDraft, Status("bogus"), TriggerCommand))
|
||||
require.Error(t, Transition(StatusDraft, StatusEnrollmentOpen, Trigger("bogus")))
|
||||
}
|
||||
|
||||
func TestTransitionsOutOfTerminalStatusAllRejected(t *testing.T) {
|
||||
triggers := []Trigger{
|
||||
TriggerCommand,
|
||||
TriggerManual,
|
||||
TriggerDeadline,
|
||||
TriggerGap,
|
||||
TriggerRuntimeEvent,
|
||||
TriggerExternalBlock,
|
||||
}
|
||||
|
||||
for _, from := range []Status{StatusFinished, StatusCancelled} {
|
||||
for _, to := range []Status{
|
||||
StatusDraft,
|
||||
StatusEnrollmentOpen,
|
||||
StatusReadyToStart,
|
||||
StatusStarting,
|
||||
StatusStartFailed,
|
||||
StatusRunning,
|
||||
StatusPaused,
|
||||
StatusFinished,
|
||||
StatusCancelled,
|
||||
} {
|
||||
for _, trigger := range triggers {
|
||||
if from == to {
|
||||
continue
|
||||
}
|
||||
err := Transition(from, to, trigger)
|
||||
require.Errorf(t, err, "%s->%s via %s should be rejected", from, to, trigger)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowedTransitionsSnapshotMatchesTable(t *testing.T) {
|
||||
snapshot := AllowedTransitions()
|
||||
|
||||
count := 0
|
||||
for _, inner := range snapshot {
|
||||
count += len(inner)
|
||||
}
|
||||
assert.Equal(t, len(allowedTransitions), count)
|
||||
|
||||
for key, triggers := range allowedTransitions {
|
||||
inner, ok := snapshot[key.from]
|
||||
require.Truef(t, ok, "expected from=%s in snapshot", key.from)
|
||||
|
||||
list, ok := inner[key.to]
|
||||
require.Truef(t, ok, "expected to=%s under from=%s", key.to, key.from)
|
||||
|
||||
for trigger := range triggers {
|
||||
assert.Containsf(t, list, trigger, "missing trigger %q for %s->%s", trigger, key.from, key.to)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user