From 0d4b60a8720279f402503491905f77a2a27b9d1e Mon Sep 17 00:00:00 2001 From: Norm MacLennan Date: Sun, 6 Dec 2020 18:13:16 -0500 Subject: [PATCH] Add a few more tests --- pkg/gamemanager/engineconfig.go | 15 ++-- pkg/gamemanager/engineconfig_test.go | 120 +++++++++++++++++++++++++++ pkg/gamemanager/gamemanager.go | 8 +- pkg/gamemanager/gamestate.go | 24 ++++-- pkg/gamemanager/gamestate_test.go | 61 ++++++++++++++ pkg/gamemanager/room.go | 13 +-- 6 files changed, 219 insertions(+), 22 deletions(-) create mode 100644 pkg/gamemanager/engineconfig_test.go diff --git a/pkg/gamemanager/engineconfig.go b/pkg/gamemanager/engineconfig.go index 2113c52..9bbc7ae 100644 --- a/pkg/gamemanager/engineconfig.go +++ b/pkg/gamemanager/engineconfig.go @@ -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) } diff --git a/pkg/gamemanager/engineconfig_test.go b/pkg/gamemanager/engineconfig_test.go new file mode 100644 index 0000000..eb5a4d6 --- /dev/null +++ b/pkg/gamemanager/engineconfig_test.go @@ -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))) + }) + } +} diff --git a/pkg/gamemanager/gamemanager.go b/pkg/gamemanager/gamemanager.go index 9f43029..af5ffc2 100644 --- a/pkg/gamemanager/gamemanager.go +++ b/pkg/gamemanager/gamemanager.go @@ -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) diff --git a/pkg/gamemanager/gamestate.go b/pkg/gamemanager/gamestate.go index 49ab206..75cc39b 100644 --- a/pkg/gamemanager/gamestate.go +++ b/pkg/gamemanager/gamestate.go @@ -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 } diff --git a/pkg/gamemanager/gamestate_test.go b/pkg/gamemanager/gamestate_test.go index 8e0c9af..097bf94 100644 --- a/pkg/gamemanager/gamestate_test.go +++ b/pkg/gamemanager/gamestate_test.go @@ -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)) + }) + } +} diff --git a/pkg/gamemanager/room.go b/pkg/gamemanager/room.go index e5cdbe0..7db1e6f 100644 --- a/pkg/gamemanager/room.go +++ b/pkg/gamemanager/room.go @@ -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 { -- 2.34.2