Files
apskel-pos-backend/internal/processor/game_leaderboard_db_test.go
T
efrilmandClaude Opus 5.5 3af16a1fbc feat(enakgame): weekly leaderboard
GET /customer/enakgame/leaderboard ranks the customers of an organization by
the EnakCoin they won from games in one week, Monday to Sunday in
Asia/Jakarta. Only completed, unflagged sessions count, by the week they
started; entry cost is not subtracted. Equal totals rank by who reached
theirs first, so no two customers share a rank.

Optional date (any day of the week, this week by default), game_id and limit
(10, at most 100). The response holds the top ranks and, under me, the
customer asking wherever they rank. Other customers are shown by a masked
name: the first word whole, the others by their first letter; a word with a
digit keeps only its first character.

The EnakGame web game shows it: integration-enakgame.md §10 (checklist now
§11) and RFC §11. The DB test needs TEST_DATABASE_URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:15:02 +07:00

96 lines
4.2 KiB
Go

package processor
import (
"fmt"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"apskel-pos-be/internal/models"
)
// The weekly leaderboard against Postgres. Needs TEST_DATABASE_URL; see
// internal/repository/wallet_repository_test.go.
func TestGameSession_Leaderboard(t *testing.T) {
e := newEnakGameEnv(t)
ctx := e.ctx()
dave := uuid.New()
require.NoError(t, e.db.Exec(`INSERT INTO customers (id, organization_id, name) VALUES (?, ?, 'Dave Lee')`, dave, e.orgA).Error)
runner := e.playableGame(e.orgA, "runner", 1)
other := e.playableGame(e.orgA, "other", 1)
theirs := e.playableGame(e.orgB, "theirs", 1)
for _, c := range []uuid.UUID{e.alice, e.bob, e.carol, dave} {
e.coins(c, 100, nil)
}
n := 0
// play completes a session of game for customer and returns its id; every
// playableGame pays 10.
play := func(customer, game uuid.UUID) uuid.UUID {
t.Helper()
n++
s, err := e.sessions.Start(ctx, customer, game, fmt.Sprintf("k%d", n))
require.NoError(t, err)
_, err = e.sessions.Complete(ctx, customer, s.SessionID, models.GameSessionCompleteInput{})
require.NoError(t, err)
return s.SessionID
}
play(e.alice, runner.ID)
play(e.alice, other.ID)
bobs := play(e.bob, runner.ID)
play(dave, runner.ID)
play(e.carol, theirs.ID)
// Bob reached 10 before Dave did.
require.NoError(t, e.db.Exec(`UPDATE game_sessions SET ended_at = ended_at - INTERVAL '1 minute' WHERE id = ?`, bobs).Error)
flagged := play(e.bob, runner.ID)
require.NoError(t, e.db.Exec(`UPDATE game_sessions SET flagged = TRUE WHERE id = ?`, flagged).Error)
lastWeek := play(dave, runner.ID)
require.NoError(t, e.db.Exec(`UPDATE game_sessions SET started_at = started_at - INTERVAL '7 days' WHERE id = ?`, lastWeek).Error)
_, err := e.sessions.Start(ctx, e.alice, runner.ID, "open")
require.NoError(t, err, "a STARTED session counts for nothing")
board, err := e.sessions.Leaderboard(ctx, e.bob, models.EnakGameLeaderboardQuery{})
require.NoError(t, err)
week := leaderboardWeekStart(e.now)
assert.Equal(t, week.Format("2006-01-02"), board.WeekStart)
assert.Equal(t, week.AddDate(0, 0, 6).Format("2006-01-02"), board.WeekEnd)
assert.Equal(t, []models.EnakGameLeaderboardEntry{
{Rank: 1, Name: "Alice", Coins: 20},
{Rank: 2, Name: "Bob", Coins: 10, IsMe: true},
{Rank: 3, Name: "Dave L**", Coins: 10},
}, board.Entries, "the flagged play, last week's play and another organization's do not count")
require.NotNil(t, board.Me)
assert.Equal(t, 2, board.Me.Rank)
board, err = e.sessions.Leaderboard(ctx, dave, models.EnakGameLeaderboardQuery{Limit: 1})
require.NoError(t, err)
require.Len(t, board.Entries, 1)
assert.Equal(t, "Alice", board.Entries[0].Name)
require.NotNil(t, board.Me, "the customer asking is returned below the limit")
assert.Equal(t, models.EnakGameLeaderboardEntry{Rank: 3, Name: "Dave L**", Coins: 10, IsMe: true}, *board.Me)
board, err = e.sessions.Leaderboard(ctx, e.alice, models.EnakGameLeaderboardQuery{GameID: other.ID.String()})
require.NoError(t, err)
assert.Equal(t, []models.EnakGameLeaderboardEntry{{Rank: 1, Name: "Alice", Coins: 10, IsMe: true}}, board.Entries)
assert.Equal(t, &other.ID, board.GameID)
board, err = e.sessions.Leaderboard(ctx, dave, models.EnakGameLeaderboardQuery{Date: week.AddDate(0, 0, -3).Format("2006-01-02")})
require.NoError(t, err)
assert.Equal(t, []models.EnakGameLeaderboardEntry{{Rank: 1, Name: "Dave L**", Coins: 10, IsMe: true}}, board.Entries, "last week")
board, err = e.sessions.Leaderboard(ctx, e.carol, models.EnakGameLeaderboardQuery{})
require.NoError(t, err)
assert.Equal(t, []models.EnakGameLeaderboardEntry{{Rank: 1, Name: "Carol", Coins: 10, IsMe: true}}, board.Entries, "only the customer's organization")
board, err = e.sessions.Leaderboard(ctx, e.bob, models.EnakGameLeaderboardQuery{GameID: uuid.NewString(), Date: "2020-01-01"})
require.NoError(t, err)
assert.Empty(t, board.Entries)
assert.Nil(t, board.Me)
_, err = e.sessions.Leaderboard(ctx, e.bob, models.EnakGameLeaderboardQuery{Date: "12-10-2026"})
assert.ErrorIs(t, err, ErrGameSessionRejected)
_, err = e.sessions.Leaderboard(ctx, e.bob, models.EnakGameLeaderboardQuery{GameID: "runner"})
assert.ErrorIs(t, err, ErrGameSessionRejected)
}