feat(enakgame): filter play history by game and status
GET /customer/enakgame/sessions takes optional game_id and status, so a game reloaded mid-play finds the session it was running (status=STARTED) instead of starting a new one and charging EnakCoin again. An invalid game_id or status is refused. integration-enakgame.md §4.4 now describes recovery after a reload: keep the session_id in sessionStorage, continue a STARTED session before expires_at, and call complete again for a COMPLETED one to get the full answer, prize included. The mobile guide and RFC §11 mention the filters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b5d2cd491a
commit
52e8fe11c6
@@ -675,12 +675,34 @@ func TestGameSession_CustomerReads(t *testing.T) {
|
||||
_, err = e.sessions.GetSession(ctx, e.bob, started.SessionID)
|
||||
assert.ErrorIs(t, err, repository.ErrGameSessionNotFound, "another customer's session")
|
||||
|
||||
page, err := e.sessions.ListSessions(ctx, e.alice, 1, 10)
|
||||
page, err := e.sessions.ListSessions(ctx, e.alice, models.GameSessionListQuery{Page: 1, Limit: 10})
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 1, page.Pagination.Total)
|
||||
page, err = e.sessions.ListSessions(ctx, e.bob, 1, 10)
|
||||
page, err = e.sessions.ListSessions(ctx, e.bob, models.GameSessionListQuery{Page: 1, Limit: 10})
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, page.Data)
|
||||
|
||||
// After a reload, the game finds the play it was running by game and status.
|
||||
other := e.playableGame(e.orgA, "other", 1)
|
||||
otherStarted, err := e.sessions.Start(ctx, e.alice, other.ID, "k2")
|
||||
require.NoError(t, err)
|
||||
_, err = e.sessions.Complete(ctx, e.alice, otherStarted.SessionID, models.GameSessionCompleteInput{})
|
||||
require.NoError(t, err)
|
||||
running, err := e.sessions.ListSessions(ctx, e.alice, models.GameSessionListQuery{GameID: game.ID.String(), Status: "started"})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, running.Data, 1)
|
||||
assert.Equal(t, started.SessionID, running.Data[0].ID)
|
||||
running, err = e.sessions.ListSessions(ctx, e.alice, models.GameSessionListQuery{GameID: other.ID.String(), Status: constants.GameSessionStatusStarted})
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, running.Data, "the other game's play is completed")
|
||||
all, err := e.sessions.ListSessions(ctx, e.alice, models.GameSessionListQuery{})
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 2, all.Pagination.Total)
|
||||
|
||||
_, err = e.sessions.ListSessions(ctx, e.alice, models.GameSessionListQuery{Status: "PLAYING"})
|
||||
assert.ErrorIs(t, err, ErrGameSessionRejected)
|
||||
_, err = e.sessions.ListSessions(ctx, e.alice, models.GameSessionListQuery{GameID: "runner"})
|
||||
assert.ErrorIs(t, err, ErrGameSessionRejected)
|
||||
}
|
||||
|
||||
// gameWith makes an ACTIVE game of org A with the given result rules and an active
|
||||
|
||||
@@ -419,10 +419,26 @@ func (p *GameSessionProcessor) ListGames(ctx context.Context, customerID uuid.UU
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListSessions returns a page of the customer's sessions, newest first.
|
||||
func (p *GameSessionProcessor) ListSessions(ctx context.Context, customerID uuid.UUID, page, limit int) (*models.PaginatedResponse[models.CustomerGameSession], error) {
|
||||
page, limit = enakGamePage(page, limit)
|
||||
sessions, total, err := p.sessions.ListCustomerSessions(ctx, customerID, (page-1)*limit, limit)
|
||||
// ListSessions returns a page of the customer's sessions, newest first, of one game
|
||||
// or status when asked.
|
||||
func (p *GameSessionProcessor) ListSessions(ctx context.Context, customerID uuid.UUID, q models.GameSessionListQuery) (*models.PaginatedResponse[models.CustomerGameSession], error) {
|
||||
page, limit := enakGamePage(q.Page, q.Limit)
|
||||
filter := repository.CustomerSessionFilter{CustomerID: customerID, Offset: (page - 1) * limit, Limit: limit}
|
||||
if s := strings.TrimSpace(q.GameID); s != "" {
|
||||
id, err := uuid.Parse(s)
|
||||
if err != nil {
|
||||
return nil, gameSessionRejected("game_id must be a UUID")
|
||||
}
|
||||
filter.GameID = &id
|
||||
}
|
||||
switch status := strings.ToUpper(strings.TrimSpace(q.Status)); status {
|
||||
case "", constants.GameSessionStatusStarted, constants.GameSessionStatusCompleted,
|
||||
constants.GameSessionStatusRefunded, constants.GameSessionStatusExpired:
|
||||
filter.Status = status
|
||||
default:
|
||||
return nil, gameSessionRejected("status must be STARTED, COMPLETED, REFUNDED or EXPIRED")
|
||||
}
|
||||
sessions, total, err := p.sessions.ListCustomerSessions(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user