~nromdotcom/gemif

0d4b60a8720279f402503491905f77a2a27b9d1e — Norm MacLennan 1 year, 1 month ago 077db0c
Add a few more tests
M pkg/gamemanager/engineconfig.go => pkg/gamemanager/engineconfig.go +9 -6
@@ 15,14 15,17 @@ type EngineConfig struct {
type SerializationFormat int

const (
	json SerializationFormat = iota + 1
	protobuf
	// JSON describes the json serilization format.
	JSON SerializationFormat = iota + 1
	// Protobuf describes the protobuf serilization format.
	Protobuf
)

func getSerDeFormat(format string) SerializationFormat {
// GetSerDeFormat translates format type strings to SerializationFormat.
func GetSerDeFormat(format string) SerializationFormat {
	serializationFormats := map[string]SerializationFormat{
		"json":  json,
		"proto": protobuf,
		"json":  JSON,
		"proto": Protobuf,
	}

	return serializationFormats[format]


@@ 33,7 36,7 @@ var ErrInvalidSerializationFormat = errors.New("invalid serialization format")

// NewEngineConfig news up an EngineConfig based on passed in values.
func NewEngineConfig(renderDesc bool, tokenFormat string) (EngineConfig, error) {
	serdeFormat := getSerDeFormat(tokenFormat)
	serdeFormat := GetSerDeFormat(tokenFormat)
	if serdeFormat == 0 {
		return EngineConfig{}, fmt.Errorf("%w: %s", ErrInvalidSerializationFormat, tokenFormat)
	}

A pkg/gamemanager/engineconfig_test.go => pkg/gamemanager/engineconfig_test.go +120 -0
@@ 0,0 1,120 @@
package gamemanager_test

import (
	"fmt"
	"gemif/pkg/gamemanager"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestGetSerDeFormat(t *testing.T) {
	arrayTests := []struct {
		name      string
		condition string                          // input
		expected  gamemanager.SerializationFormat // expected result
	}{
		{
			"JSON",
			"json", gamemanager.JSON,
		},
		{
			"Protobuf",
			"proto", gamemanager.Protobuf,
		},
		{
			"Neither",
			"something_else", 0,
		},
	}

	t.Parallel()

	for _, tc := range arrayTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			actual := gamemanager.GetSerDeFormat(tc.condition)
			assert.True(t, assert.ObjectsAreEqualValues(tc.expected, actual))
		})
	}
}

func TestNewEngineFormatPass(t *testing.T) {
	arrayTests := []struct {
		name     string
		render   bool
		format   string                   // input
		expected gamemanager.EngineConfig // expected result
	}{
		{
			"JSON/true", true, "json",
			gamemanager.EngineConfig{
				RenderDescriptions: true,
				StateTokenFormat:   gamemanager.JSON,
			},
		},
		{
			"Proto/true", true, "proto",
			gamemanager.EngineConfig{
				RenderDescriptions: true,
				StateTokenFormat:   gamemanager.Protobuf,
			},
		},
		{
			"JSON/false", false, "json",
			gamemanager.EngineConfig{
				RenderDescriptions: false,
				StateTokenFormat:   gamemanager.JSON,
			},
		},
		{
			"Proto/false", false, "proto",
			gamemanager.EngineConfig{
				RenderDescriptions: false,
				StateTokenFormat:   gamemanager.Protobuf,
			},
		},
	}

	t.Parallel()

	for _, tc := range arrayTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			actual, _ := gamemanager.NewEngineConfig(tc.render, tc.format)
			assert.True(t, assert.ObjectsAreEqualValues(tc.expected, actual))
		})
	}
}

func TestNewEngineFormatFail(t *testing.T) {
	arrayTests := []struct {
		name     string
		render   bool
		format   string // input
		expected error  // expected result
	}{
		{
			"Neither/true", true, "neither",
			gamemanager.ErrInvalidSerializationFormat,
		},
		{
			"Neither/false", false, "neither",
			gamemanager.ErrInvalidSerializationFormat,
		},
	}

	t.Parallel()

	for _, tc := range arrayTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			_, err := gamemanager.NewEngineConfig(tc.render, tc.format)

			assert.True(t, assert.EqualError(t, err, fmt.Sprintf("%s: %s", tc.expected, tc.format)))
		})
	}
}

M pkg/gamemanager/gamemanager.go => pkg/gamemanager/gamemanager.go +4 -4
@@ 80,11 80,11 @@ func (gm *GameManager) DeserializeState(stateToken string) (GameState, error) {
	var st StateToken

	switch gm.EngineConfig.StateTokenFormat {
	case json:
	case JSON:
		if unmarshallErr := protojson.Unmarshal(decodedString, &st); unmarshallErr != nil {
			return GameState{}, fmt.Errorf("failed to load state: %w", unmarshallErr)
		}
	case protobuf:
	case Protobuf:
		if unmarshallErr := proto.Unmarshal(decodedString, &st); unmarshallErr != nil {
			return GameState{}, fmt.Errorf("failed to load state: %w", unmarshallErr)
		}


@@ 108,14 108,14 @@ func (gm *GameManager) SerializeState(gameState GameState) (string, error) {
	}

	switch gm.EngineConfig.StateTokenFormat {
	case protobuf:
	case Protobuf:
		stateBytes, err := proto.Marshal(&st)
		if err != nil {
			return "", fmt.Errorf("failed to save state: %w", err)
		}

		return base64.StdEncoding.EncodeToString(stateBytes), nil
	case json:
	case JSON:
		stateBytes, err := protojson.Marshal(&st)
		if err != nil {
			return "", fmt.Errorf("failed to save state: %w", err)

M pkg/gamemanager/gamestate.go => pkg/gamemanager/gamestate.go +18 -6
@@ 9,14 9,22 @@ type GameState struct {

// MeetCondition adds a condtition to the list of met conditions if not already met.
func (gs *GameState) MeetCondition(cond string) {
	if !isInSlice(gs.Conditions, cond) {
	if index := isInSlice(gs.Conditions, cond); index == -1 {
		gs.Conditions = append(gs.Conditions, cond)
	}
}

// RemoveCondition removes a condtition from the list of met conditions if it exists.
func (gs *GameState) RemoveCondition(cond string) {
	if index := isInSlice(gs.Conditions, cond); index > -1 {
		gs.Conditions[index] = gs.Conditions[len(gs.Conditions)-1]
		gs.Conditions = gs.Conditions[:len(gs.Conditions)-1]
	}
}

// ConditionMet checks to see if a condition has been met.
func (gs GameState) ConditionMet(cond string) bool {
	return isInSlice(gs.Conditions, cond)
	return isInSlice(gs.Conditions, cond) > -1
}

// UseExit applies an exit's consequences to the GameState


@@ 29,15 37,19 @@ func (gs GameState) UseExit(exit Exit) GameState {
		newState.MeetCondition(exit.SetCondition)
	}

	if exit.UnsetCondition != "" {
		newState.RemoveCondition(exit.UnsetCondition)
	}

	return newState
}

func isInSlice(slice []string, val string) bool {
	for _, item := range slice {
func isInSlice(slice []string, val string) int {
	for index, item := range slice {
		if item == val {
			return true
			return index
		}
	}

	return false
	return -1
}

M pkg/gamemanager/gamestate_test.go => pkg/gamemanager/gamestate_test.go +61 -0
@@ 96,3 96,64 @@ func TestMeetCondition(t *testing.T) {
		})
	}
}

func TestRemoveCondition(t *testing.T) {
	arrayTests := []struct {
		name      string
		state     gamemanager.GameState
		condition string   // input
		expected  []string // expected result
	}{
		{
			"Remove condition not met",
			gamemanager.GameState{
				StoryID:     "a",
				CurrentRoom: "a",
				Conditions:  []string{},
			},
			"did_thing",
			[]string{},
		},
		{
			"Remove condition already met",
			gamemanager.GameState{
				StoryID:     "a",
				CurrentRoom: "a",
				Conditions:  []string{"did_thing"},
			},
			"did_thing",
			[]string{},
		},
		{
			"Remove when met with several other conditions",
			gamemanager.GameState{
				StoryID:     "a",
				CurrentRoom: "a",
				Conditions:  []string{"something", "did_thing", "something_else", "more_stuff", "and_again"},
			},
			"did_thing",
			[]string{"something", "and_again", "something_else", "more_stuff"},
		},
		{
			"Remove when met as last in list",
			gamemanager.GameState{
				StoryID:     "a",
				CurrentRoom: "a",
				Conditions:  []string{"something", "something_else", "more_stuff", "and_again", "did_thing"},
			},
			"did_thing",
			[]string{"something", "something_else", "more_stuff", "and_again"},
		},
	}

	t.Parallel()

	for _, tc := range arrayTests {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			tc.state.RemoveCondition(tc.condition)
			assert.True(t, assert.ObjectsAreEqualValues(tc.expected, tc.state.Conditions))
		})
	}
}

M pkg/gamemanager/room.go => pkg/gamemanager/room.go +7 -6
@@ 12,12 12,13 @@ type Room struct {

// Exit describes actions applied to GameState as part of leaving a Room.
type Exit struct {
	ID           string `yaml:"exit_id" json:"exit_id" binding:"uuid"`
	Description  string `yaml:"exit_description" json:"exit_description"`
	Destination  string `yaml:"destination_id" json:"destination_id" binding:"uuid"`
	SetCondition string `yaml:"set_condition,omitempty" json:"set_condition"`
	IfCondition  string `yaml:"if_condition,omitempty" json:"if_condition"`
	NotCondition string `yaml:"not_condition,omitempty" json:"not_condition"`
	ID             string `yaml:"exit_id" json:"exit_id" binding:"uuid"`
	Description    string `yaml:"exit_description" json:"exit_description"`
	Destination    string `yaml:"destination_id" json:"destination_id" binding:"uuid"`
	SetCondition   string `yaml:"set_condition,omitempty" json:"set_condition"`
	IfCondition    string `yaml:"if_condition,omitempty" json:"if_condition"`
	NotCondition   string `yaml:"not_condition,omitempty" json:"not_condition"`
	UnsetCondition string `yaml:"unset_condition,omitempty" json:"unset_condition"`
}

func (r Room) filterExits(gameState GameState) []Exit {