//go:build integration package inttest import ( "context" "errors" "testing" "github.com/google/uuid" "scrabble/backend/internal/game" ) // TestHideFinishedGame covers Stage 17 per-account game hiding: an active game cannot be // hidden, a finished game is removed from the hider's own list while staying visible to the // other player, an outsider cannot hide it, and the action is idempotent. func TestHideFinishedGame(t *testing.T) { ctx := context.Background() svc, gameID, seats, _ := newDraftGame(t) // Hiding while the game is still active is refused. if err := svc.HideGame(ctx, seats[0], gameID); !errors.Is(err, game.ErrGameActive) { t.Fatalf("hide active = %v, want ErrGameActive", err) } // Finish the game by seat 0 resigning. if _, err := svc.Resign(ctx, gameID, seats[0]); err != nil { t.Fatalf("resign: %v", err) } // A non-player cannot hide it. if err := svc.HideGame(ctx, provisionAccount(t), gameID); !errors.Is(err, game.ErrNotAPlayer) { t.Fatalf("hide by outsider = %v, want ErrNotAPlayer", err) } // Seat 0 hides the finished game; hiding again is a no-op success. if err := svc.HideGame(ctx, seats[0], gameID); err != nil { t.Fatalf("hide: %v", err) } if err := svc.HideGame(ctx, seats[0], gameID); err != nil { t.Fatalf("hide twice: %v", err) } // It is gone from seat 0's list but still in seat 1's (hiding is per-account). if containsGame(t, svc, seats[0], gameID) { t.Error("hidden game still listed for the hider") } if !containsGame(t, svc, seats[1], gameID) { t.Error("hidden game should remain listed for the other player") } } // containsGame reports whether the account's lobby list includes gameID. func containsGame(t *testing.T, svc *game.Service, accountID, gameID uuid.UUID) bool { t.Helper() games, err := svc.ListForAccount(context.Background(), accountID) if err != nil { t.Fatalf("list for account: %v", err) } for _, g := range games { if g.ID == gameID { return true } } return false }