From f668ef5f4c79732afd65dddd0256008bd0769a10 Mon Sep 17 00:00:00 2001 From: Daithi Hearn Date: Fri, 19 Jan 2024 21:29:47 +0100 Subject: [PATCH] feat: buys cards --- cmd/api/main.go | 1 + pkg/game/deck-utils.go | 23 ++++- pkg/game/deck-utils_test.go | 176 ++++++++++++++++++++++++++++++++++++ pkg/game/game-handler.go | 51 +++++++++++ pkg/game/game-methods.go | 75 ++++++++++++++- pkg/game/game-service.go | 26 ++++++ pkg/game/game-utils.go | 5 +- pkg/game/game-utils_test.go | 71 +++++++++++++++ pkg/game/game_test.go | 126 +++++++++++++++++++------- pkg/game/testdata.go | 32 +++++++ 10 files changed, 550 insertions(+), 36 deletions(-) create mode 100644 pkg/game/deck-utils_test.go create mode 100644 pkg/game/game-utils_test.go diff --git a/cmd/api/main.go b/cmd/api/main.go index c57154b..8e6b45e 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -131,6 +131,7 @@ func main() { router.GET("/api/v1/game/:gameId/state", auth.EnsureValidTokenGin([]string{auth.ReadGame}), gameHandler.GetState) router.PUT("/api/v1/game/:gameId/call", auth.EnsureValidTokenGin([]string{auth.WriteGame}), gameHandler.Call) router.PUT("/api/v1/game/:gameId/suit", auth.EnsureValidTokenGin([]string{auth.WriteGame}), gameHandler.SelectSuit) + router.PUT("/api/v1/game/:gameId/buy", auth.EnsureValidTokenGin([]string{auth.WriteGame}), gameHandler.Buy) router.GET("/api/v1/game/all", auth.EnsureValidTokenGin([]string{auth.ReadGame}), gameHandler.GetAll) router.PUT("/api/v1/game", auth.EnsureValidTokenGin([]string{auth.WriteAdmin}), gameHandler.Create) router.DELETE("/api/v1/game/:gameId", auth.EnsureValidTokenGin([]string{auth.WriteAdmin}), gameHandler.Delete) diff --git a/pkg/game/deck-utils.go b/pkg/game/deck-utils.go index ec95856..9be0416 100644 --- a/pkg/game/deck-utils.go +++ b/pkg/game/deck-utils.go @@ -1,6 +1,7 @@ package game import ( + "fmt" "math/rand" ) @@ -15,16 +16,34 @@ func ShuffleCards(cards []CardName) []CardName { return shuffled } -func DealCards(deck []CardName, numPlayers int) ([]CardName, [][]CardName) { +func DealCards(deck []CardName, numPlayers int) ([]CardName, [][]CardName, error) { hands := make([][]CardName, numPlayers+1) // Deal the cards for i := 0; i < 5; i++ { for j := 0; j < numPlayers+1; j++ { + if len(deck) == 0 { + return nil, nil, fmt.Errorf("deck is empty") + } hands[j] = append(hands[j], deck[0]) deck = deck[1:] } } - return deck, hands + return deck, hands, nil +} + +func BuyCards(deck []CardName, cards []CardName) ([]CardName, []CardName, error) { + for { + if len(cards) == 5 { + break + } + if len(deck) == 0 { + return nil, nil, fmt.Errorf("deck is empty") + } + + cards = append(cards, deck[0]) + deck = deck[1:] + } + return deck, cards, nil } func NewDeck() []CardName { diff --git a/pkg/game/deck-utils_test.go b/pkg/game/deck-utils_test.go new file mode 100644 index 0000000..a48ce80 --- /dev/null +++ b/pkg/game/deck-utils_test.go @@ -0,0 +1,176 @@ +package game + +import "testing" + +func TestDeck_ShuffleCards(t *testing.T) { + tests := []struct { + name string + cards []CardName + }{ + { + name: "Empty deck", + cards: []CardName{}, + }, + { + name: "Single card", + cards: []CardName{ACE_HEARTS}, + }, + { + name: "Multiple cards", + cards: []CardName{ACE_HEARTS, TWO_HEARTS, THREE_HEARTS, FOUR_HEARTS, FIVE_HEARTS, SIX_HEARTS, SEVEN_HEARTS, EIGHT_HEARTS, NINE_HEARTS, TEN_HEARTS, JACK_HEARTS, QUEEN_HEARTS, KING_HEARTS, ACE_DIAMONDS, TWO_DIAMONDS, THREE_DIAMONDS, FOUR_DIAMONDS, FIVE_DIAMONDS, SIX_DIAMONDS, SEVEN_DIAMONDS, EIGHT_DIAMONDS, NINE_DIAMONDS, TEN_DIAMONDS, JACK_DIAMONDS, QUEEN_DIAMONDS, KING_DIAMONDS, ACE_CLUBS, TWO_CLUBS, THREE_CLUBS, FOUR_CLUBS, FIVE_CLUBS, SIX_CLUBS, SEVEN_CLUBS, EIGHT_CLUBS, NINE_CLUBS, TEN_CLUBS, JACK_CLUBS, QUEEN_CLUBS, KING_CLUBS, ACE_SPADES, TWO_SPADES, THREE_SPADES, FOUR_SPADES, FIVE_SPADES, SIX_SPADES, SEVEN_SPADES, EIGHT_SPADES, NINE_SPADES, TEN_SPADES, JACK_SPADES, QUEEN_SPADES, KING_SPADES, JOKER}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + shuffled := ShuffleCards(test.cards) + if len(shuffled) != len(test.cards) { + t.Errorf("expected %d cards, got %d", len(test.cards), len(shuffled)) + } + + if !compare(shuffled, test.cards) { + t.Errorf("expected cards to be the same, got %v", shuffled) + } + + // Cards should not be in the same order as they started (unless the deck is empty or has a small number of cards) + if len(test.cards) > 7 { + var sameOrder = true + for i, card := range test.cards { + if card != shuffled[i] { + sameOrder = false + break + } + } + if sameOrder { + t.Errorf("expected cards to be shuffled, got %v", shuffled) + } + } + }) + } +} + +func TestDeck_DealCards(t *testing.T) { + tests := []struct { + name string + deck []CardName + numPlayers int + expectError bool + }{ + { + name: "Empty deck", + deck: []CardName{}, + numPlayers: 2, + expectError: true, + }, + { + name: "Not enough cards", + deck: []CardName{ACE_HEARTS}, + numPlayers: 2, + expectError: true, + }, + { + name: "Multiple cards", + deck: []CardName{ACE_HEARTS, TWO_HEARTS, THREE_HEARTS, FOUR_HEARTS, FIVE_HEARTS, SIX_HEARTS, SEVEN_HEARTS, EIGHT_HEARTS, NINE_HEARTS, TEN_HEARTS, JACK_HEARTS, QUEEN_HEARTS, KING_HEARTS, ACE_DIAMONDS, TWO_DIAMONDS, THREE_DIAMONDS, FOUR_DIAMONDS, FIVE_DIAMONDS, SIX_DIAMONDS, SEVEN_DIAMONDS, EIGHT_DIAMONDS, NINE_DIAMONDS, TEN_DIAMONDS, JACK_DIAMONDS, QUEEN_DIAMONDS, KING_DIAMONDS, ACE_CLUBS, TWO_CLUBS, THREE_CLUBS, FOUR_CLUBS, FIVE_CLUBS, SIX_CLUBS, SEVEN_CLUBS, EIGHT_CLUBS, NINE_CLUBS, TEN_CLUBS, JACK_CLUBS, QUEEN_CLUBS, KING_CLUBS, ACE_SPADES, TWO_SPADES, THREE_SPADES, FOUR_SPADES, FIVE_SPADES, SIX_SPADES, SEVEN_SPADES, EIGHT_SPADES, NINE_SPADES, TEN_SPADES, JACK_SPADES, QUEEN_SPADES, KING_SPADES, JOKER}, + numPlayers: 2, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + deck, hands, err := DealCards(test.deck, test.numPlayers) + + if test.expectError { + if err == nil { + t.Errorf("expected an error") + } + } else { + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + // The output deck and hands should have all the same cards as the input deck + var outputCards = deck + for _, hand := range hands { + outputCards = append(outputCards, hand...) + } + if !compare(outputCards, test.deck) { + t.Errorf("expected cards to be the same, got %v", outputCards) + } + } + }) + } +} + +func TestDeck_BuyCards(t *testing.T) { + tests := []struct { + name string + deck []CardName + cards []CardName + expectError bool + expectedDeck []CardName + expectedCards []CardName + }{ + { + name: "Empty deck", + deck: []CardName{}, + cards: []CardName{}, + expectError: true, + }, + { + name: "Not enough cards", + deck: []CardName{ACE_HEARTS}, + cards: []CardName{}, + expectError: true, + }, + { + name: "Not enough cards", + deck: []CardName{ACE_DIAMONDS}, + cards: []CardName{ACE_HEARTS}, + expectError: true, + }, + { + name: "Enough cards", + deck: []CardName{ACE_DIAMONDS}, + cards: []CardName{ACE_HEARTS, TWO_HEARTS, THREE_HEARTS, FOUR_HEARTS}, + expectedDeck: []CardName{}, + expectedCards: []CardName{ACE_HEARTS, TWO_HEARTS, THREE_HEARTS, FOUR_HEARTS, ACE_DIAMONDS}, + }, + { + name: "Loads of cards", + deck: []CardName{ACE_HEARTS, TWO_HEARTS, THREE_HEARTS, FOUR_HEARTS, FIVE_HEARTS, SIX_HEARTS, SEVEN_HEARTS, EIGHT_HEARTS, NINE_HEARTS, TEN_HEARTS, JACK_HEARTS, QUEEN_HEARTS, KING_HEARTS, ACE_DIAMONDS, TWO_DIAMONDS, THREE_DIAMONDS, FOUR_DIAMONDS, FIVE_DIAMONDS, SIX_DIAMONDS, SEVEN_DIAMONDS, EIGHT_DIAMONDS, NINE_DIAMONDS, TEN_DIAMONDS, JACK_DIAMONDS, QUEEN_DIAMONDS, KING_DIAMONDS, ACE_CLUBS, TWO_CLUBS, THREE_CLUBS, FOUR_CLUBS, FIVE_CLUBS, SIX_CLUBS, SEVEN_CLUBS, EIGHT_CLUBS, NINE_CLUBS, TEN_CLUBS, JACK_CLUBS, QUEEN_CLUBS, KING_CLUBS, ACE_SPADES, TWO_SPADES, THREE_SPADES, FOUR_SPADES, FIVE_SPADES, SIX_SPADES, SEVEN_SPADES, EIGHT_SPADES, NINE_SPADES, TEN_SPADES, JACK_SPADES, QUEEN_SPADES, KING_SPADES, JOKER}, + cards: []CardName{}, + expectedDeck: []CardName{SIX_HEARTS, SEVEN_HEARTS, EIGHT_HEARTS, NINE_HEARTS, TEN_HEARTS, JACK_HEARTS, QUEEN_HEARTS, KING_HEARTS, ACE_DIAMONDS, TWO_DIAMONDS, THREE_DIAMONDS, FOUR_DIAMONDS, FIVE_DIAMONDS, SIX_DIAMONDS, SEVEN_DIAMONDS, EIGHT_DIAMONDS, NINE_DIAMONDS, TEN_DIAMONDS, JACK_DIAMONDS, QUEEN_DIAMONDS, KING_DIAMONDS, ACE_CLUBS, TWO_CLUBS, THREE_CLUBS, FOUR_CLUBS, FIVE_CLUBS, SIX_CLUBS, SEVEN_CLUBS, EIGHT_CLUBS, NINE_CLUBS, TEN_CLUBS, JACK_CLUBS, QUEEN_CLUBS, KING_CLUBS, ACE_SPADES, TWO_SPADES, THREE_SPADES, FOUR_SPADES, FIVE_SPADES, SIX_SPADES, SEVEN_SPADES, EIGHT_SPADES, NINE_SPADES, TEN_SPADES, JACK_SPADES, QUEEN_SPADES, KING_SPADES, JOKER}, + expectedCards: []CardName{ACE_HEARTS, TWO_HEARTS, THREE_HEARTS, FOUR_HEARTS, FIVE_HEARTS}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + deck, cards, err := BuyCards(test.deck, test.cards) + + if test.expectError { + if err == nil { + t.Errorf("expected an error") + } + } else { + if err != nil { + t.Errorf("expected no error, got %v", err) + } + + if !compare(deck, test.expectedDeck) { + t.Errorf("expected deck to be %v, got %v", test.expectedDeck, deck) + } + if !compare(cards, test.expectedCards) { + t.Errorf("expected cards to be %v, got %v", test.expectedCards, cards) + } + } + }) + } +} + +func TestDeck_NewDeck(t *testing.T) { + deck := NewDeck() + if len(deck) != 53 { + t.Errorf("expected 53 cards, got %d", len(deck)) + } +} diff --git a/pkg/game/game-handler.go b/pkg/game/game-handler.go index 118bbc9..d85d1c1 100644 --- a/pkg/game/game-handler.go +++ b/pkg/game/game-handler.go @@ -320,3 +320,54 @@ func (h *Handler) SelectSuit(c *gin.Context) { } c.IndentedJSON(http.StatusOK, state) } + +type BuyRequest struct { + Cards []CardName `json:"cards"` +} + +// Buy @Summary Buy cards +// @Description When in the Buying state, the Goer can buy cards from the deck +// @Tags Game +// @ID buy +// @Produce json +// @Security Bearer +// @Param gameId path string true "Game ID" +// @Para body BuyRequest true "Buy Request" +// @Success 200 {object} State +// @Failure 400 {object} api.ErrorResponse +// @Failure 500 {object} api.ErrorResponse +// @Router /game/{gameId}/buy [put] +func (h *Handler) Buy(c *gin.Context) { + // Check the user is correctly authenticated + id, ok := auth.CheckValidated(c) + if !ok { + return + } + + // Get the context from the request + ctx := c.Request.Context() + + // Get the game ID from the request + gameId := c.Param("gameId") + + // Get the request body + var req BuyRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, api.ErrorResponse{Message: err.Error()}) + return + } + + // Buy the cards + game, err := h.S.Buy(ctx, gameId, id, req.Cards) + + if err != nil { + c.JSON(http.StatusInternalServerError, api.ErrorResponse{Message: err.Error()}) + return + } + state, err := game.GetState(id) + if err != nil { + c.JSON(http.StatusInternalServerError, api.ErrorResponse{Message: err.Error()}) + return + } + c.IndentedJSON(http.StatusOK, state) +} diff --git a/pkg/game/game-methods.go b/pkg/game/game-methods.go index 5c7e295..a0467db 100644 --- a/pkg/game/game-methods.go +++ b/pkg/game/game-methods.go @@ -86,7 +86,10 @@ func (g *Game) EndRound() error { } // Deal the cards - deck, hands := DealCards(ShuffleCards(NewDeck()), len(g.Players)) + deck, hands, err := DealCards(ShuffleCards(NewDeck()), len(g.Players)) + if err != nil { + return err + } var dummy []CardName for i, hand := range hands { if i >= len(g.Players) { @@ -320,3 +323,73 @@ func (g *Game) SelectSuit(playerID string, suit Suit, cards []CardName) error { return nil } + +func (g *Game) Buy(id string, cards []CardName) error { + // Verify the at the round is in the buying state + if g.CurrentRound.Status != Buying { + return fmt.Errorf("round must be in the buying state to buy cards") + } + + // Verify that is the player's go + if g.CurrentRound.CurrentHand.CurrentPlayerID != id { + return fmt.Errorf("only the current player can buy cards") + } + + // Verify the number of cards selected is valid (<=5 and >= minKeep) + minKeep, err := g.MinKeep() + if err != nil { + return err + } + if len(cards) > 5 || len(cards) < minKeep { + return fmt.Errorf("invalid number of cards selected") + } + + // Verify the cards are valid (must be either in the player's hand or the dummy's hand and must be unique + state, err := g.GetState(id) + if err != nil { + return err + } + if !containsAllUnique(state.Cards, cards) { + return fmt.Errorf("invalid card selected") + } + + // Get cards from the deck so the player has 5 cards + deck, cards, err := BuyCards(g.Deck, cards) + if err != nil { + return err + } + + g.Deck = deck + + // Set my cards + for i, p := range g.Players { + if p.ID == id { + g.Players[i].Cards = cards + break + } + } + + // If the current player is the dealer update the round status to playing + if g.CurrentRound.DealerID == id { + g.CurrentRound.Status = Playing + + // Set the next player when the dealer buys + np, err := nextPlayer(g.Players, g.CurrentRound.GoerID) + if err != nil { + return err + } + g.CurrentRound.CurrentHand.CurrentPlayerID = np.ID + } else { + // Set the next player + np, err := nextPlayer(g.Players, id) + if err != nil { + return err + } + g.CurrentRound.CurrentHand.CurrentPlayerID = np.ID + } + + // Increment revision + g.Revision++ + + return nil +} diff --git a/pkg/game/game-service.go b/pkg/game/game-service.go index 7459c5b..39d885c 100644 --- a/pkg/game/game-service.go +++ b/pkg/game/game-service.go @@ -16,6 +16,7 @@ type ServiceI interface { Delete(ctx context.Context, gameId string, adminId string) error Call(ctx context.Context, gameId string, playerId string, call Call) (Game, error) SelectSuit(ctx context.Context, id string, id2 string, suit Suit, cards []CardName) (Game, error) + Buy(ctx context.Context, id string, id2 string, cards []CardName) (Game, error) } type Service struct { @@ -150,3 +151,28 @@ func (s *Service) SelectSuit(ctx context.Context, gameId string, playerID string } return game, nil } + +// Buy cards +func (s *Service) Buy(ctx context.Context, gameId string, playerID string, cards []CardName) (Game, error) { + // Get the game from the database. + game, has, err := s.Get(ctx, gameId) + if err != nil { + return Game{}, err + } + if !has { + return Game{}, errors.New("game not found") + } + + // Buy the cards. + err = game.Buy(playerID, cards) + if err != nil { + return Game{}, err + } + + // Save the game to the database. + err = s.Col.UpdateOne(ctx, game, game.ID) + if err != nil { + return Game{}, err + } + return game, nil +} diff --git a/pkg/game/game-utils.go b/pkg/game/game-utils.go index 63ccde5..fdb72f1 100644 --- a/pkg/game/game-utils.go +++ b/pkg/game/game-utils.go @@ -155,7 +155,10 @@ func NewGame(playerIDs []string, name string, adminID string) (Game, error) { round, err := createFirstRound(players, dealer) // Deal the cards - deck, hands := DealCards(ShuffleCards(NewDeck()), len(players)) + deck, hands, err := DealCards(ShuffleCards(NewDeck()), len(players)) + if err != nil { + return Game{}, err + } var dummy []CardName for i, hand := range hands { if i >= len(players) { diff --git a/pkg/game/game-utils_test.go b/pkg/game/game-utils_test.go new file mode 100644 index 0000000..4556fb7 --- /dev/null +++ b/pkg/game/game-utils_test.go @@ -0,0 +1,71 @@ +package game + +import "testing" + +func TestGame_ParseCall(t *testing.T) { + tests := []struct { + name string + callStr string + expectedCall Call + expectingError bool + }{ + { + name: "Valid call - 0", + callStr: "0", + expectedCall: Pass, + }, + { + name: "Valid call - 10", + callStr: "10", + expectedCall: Ten, + }, + { + name: "Valid call - 15", + callStr: "15", + expectedCall: Fifteen, + }, + { + name: "Valid call - 20", + callStr: "20", + expectedCall: Twenty, + }, + { + name: "Valid call - 25", + callStr: "25", + expectedCall: TwentyFive, + }, + { + name: "Valid call - 30", + callStr: "30", + expectedCall: Jink, + }, + { + name: "Invalid call - 5", + callStr: "5", + expectingError: true, + }, + { + name: "Invalid call - 35", + callStr: "35", + expectingError: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + call, err := ParseCall(test.callStr) + if test.expectingError { + if err == nil { + t.Errorf("expected an error, got nil") + } + } else { + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if call != test.expectedCall { + t.Errorf("expected call to be %d, got %d", test.expectedCall, call) + } + } + }) + } +} diff --git a/pkg/game/game_test.go b/pkg/game/game_test.go index 3483474..c87c1d7 100644 --- a/pkg/game/game_test.go +++ b/pkg/game/game_test.go @@ -451,58 +451,101 @@ func TestGame_SelectSuit(t *testing.T) { } } -func TestGame_ParseCall(t *testing.T) { +func TestGame_Buy(t *testing.T) { tests := []struct { - name string - callStr string - expectedCall Call - expectingError bool + name string + game Game + playerID string + cards []CardName + expectedStatus RoundStatus + expectedRevision int + expectedNextPlayerID string + expectingError bool }{ { - name: "Valid call - 0", - callStr: "0", - expectedCall: Pass, + name: "Valid selection - keep 1 from my hand", + game: BuyingGame("3"), + playerID: "2", + cards: []CardName{SEVEN_HEARTS}, + expectedStatus: Buying, + expectedRevision: 1, + expectedNextPlayerID: "3", }, { - name: "Valid call - 10", - callStr: "10", - expectedCall: Ten, + name: "Valid selection - keep all 5", + game: BuyingGame("1"), + playerID: "2", + cards: []CardName{SEVEN_HEARTS, EIGHT_HEARTS, NINE_HEARTS, TEN_HEARTS, JACK_HEARTS}, + expectedStatus: Buying, + expectedRevision: 1, + expectedNextPlayerID: "3", }, { - name: "Valid call - 15", - callStr: "15", - expectedCall: Fifteen, + name: "dealer buying should cause status change", + game: BuyingGame("2"), + playerID: "2", + cards: []CardName{SEVEN_HEARTS, EIGHT_HEARTS, NINE_HEARTS, TEN_HEARTS, JACK_HEARTS}, + expectedStatus: Playing, + expectedRevision: 1, + expectedNextPlayerID: "1", }, { - name: "Valid call - 20", - callStr: "20", - expectedCall: Twenty, + name: "Not current player", + game: BuyingGame("3"), + playerID: "1", + cards: []CardName{TWO_HEARTS}, + expectedRevision: 0, + expectingError: true, }, { - name: "Valid call - 25", - callStr: "25", - expectedCall: TwentyFive, + name: "Valid selection - not keeping any cards", + game: BuyingGame("3"), + playerID: "2", + cards: []CardName{}, + expectedStatus: Buying, + expectedRevision: 1, }, { - name: "Valid call - 30", - callStr: "30", - expectedCall: Jink, + name: "Invalid state", + game: TwoPlayerGame(), + playerID: "1", + cards: []CardName{ACE_HEARTS}, + expectedStatus: Buying, + expectingError: true, + expectedRevision: 0, }, { - name: "Invalid call - 5", - callStr: "5", - expectingError: true, + name: "Invalid number of cards", + game: BuyingGame("3"), + playerID: "2", + cards: []CardName{ACE_SPADES, KING_SPADES, QUEEN_SPADES, JACK_SPADES, JOKER, ACE_DIAMONDS}, + expectedStatus: Buying, + expectingError: true, + expectedRevision: 0, }, { - name: "Invalid call - 35", - callStr: "35", - expectingError: true, + name: "Invalid card - not in hand", + game: BuyingGame("3"), + playerID: "2", + cards: []CardName{FIVE_CLUBS}, + expectedStatus: Buying, + expectingError: true, + expectedRevision: 0, + }, + { + name: "Duplicate card", + game: BuyingGame("3"), + playerID: "2", + cards: []CardName{ACE_DIAMONDS, ACE_DIAMONDS}, + expectedStatus: Buying, + expectingError: true, + expectedRevision: 0, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - call, err := ParseCall(test.callStr) + err := test.game.Buy(test.playerID, test.cards) if test.expectingError { if err == nil { t.Errorf("expected an error, got nil") @@ -511,9 +554,28 @@ func TestGame_ParseCall(t *testing.T) { if err != nil { t.Errorf("expected no error, got %v", err) } - if call != test.expectedCall { - t.Errorf("expected call to be %d, got %d", test.expectedCall, call) + if test.game.CurrentRound.Status != test.expectedStatus { + t.Errorf("expected round status to be %s, got %s", test.expectedStatus, test.game.CurrentRound.Status) } + // Check that he has all of retained the cards he selected + state, err := test.game.GetState(test.playerID) + if err != nil { + t.Errorf("expected no error, got %v", err) + } + if len(state.Cards) != 5 { + t.Errorf("expected player to have 5 cards, got %d", len(state.Cards)) + } + if !containsAllUnique(state.Cards, test.cards) { + t.Errorf("expected player to have all of the selected cards %v, got %v", test.cards, state.Cards) + } + if test.expectedNextPlayerID != "" { + if test.game.CurrentRound.CurrentHand.CurrentPlayerID != test.expectedNextPlayerID { + t.Errorf("expected next player to be %s, got %s", test.expectedNextPlayerID, test.game.CurrentRound.CurrentHand.CurrentPlayerID) + } + } + } + if test.game.Revision != test.expectedRevision { + t.Errorf("expected revision to be %d, got %d", test.expectedRevision, test.game.Revision) } }) } diff --git a/pkg/game/testdata.go b/pkg/game/testdata.go index 435c88f..bfaa3d8 100644 --- a/pkg/game/testdata.go +++ b/pkg/game/testdata.go @@ -233,6 +233,38 @@ func CalledGame() Game { } } +func BuyingGame(dealerId string) Game { + deck := NewDeck() + + p1 := Player1() + p1.Cards = []CardName{deck[0], deck[1], deck[2], deck[3], deck[4]} + deck = deck[5:] + p2 := Player2() + p2.Cards = []CardName{deck[0], deck[1], deck[2], deck[3], deck[4]} + deck = deck[5:] + p3 := Player3() + p3.Cards = []CardName{deck[0], deck[1], deck[2], deck[3], deck[4]} + deck = deck[5:] + + return Game{ + ID: "1", + Name: "Test Game", + Status: Active, + Timestamp: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), + Players: []Player{p1, p2, p3}, + CurrentRound: Round{ + DealerID: dealerId, + GoerID: "3", + Status: Buying, + CurrentHand: Hand{ + CurrentPlayerID: "2", + }, + }, + AdminID: "1", + Deck: deck, + } +} + func CompletedGame() Game { return Game{ ID: "2",