Files
tiennm99bot/internal/modules/wordledaily/score_test.go
T
tiennm99 629b513c91 feat(games): move /wordle and /loldle to the web games and add /loldledaily
/wordle and /loldle now send the wordle and loldle BotFather games in an
unlimited mode: each player gets their own word or champion, saved after
every guess, with a New game button. Cards the bot did not record as
unlimited, including the daily cards, play the daily puzzle. The helper
commands keep working on the player's round.

/loldledaily mirrors /wordledaily: one champion per day at 07:00 ICT,
subscribe and unsubscribe, the morning recap and the live group results.

The daily and unlimited machinery is shared in util/guessgame, the old
in-chat wordle module is removed, and a one-time startup migration carries
each player's stats and unfinished round over without changing old data.
2026-10-10 09:29:48 +07:00

78 lines
2.6 KiB
Go

package wordledaily
import (
"errors"
"testing"
"github.com/tiennm99/tiennm99bot/internal/modules/util/guessgame"
"github.com/tiennm99/tiennm99bot/internal/modules/util/htmlgame"
)
func TestScore_WinReportsSevenMinusGuessesOncePerCard(t *testing.T) {
h := newHarness(t)
card := h.tokenFor(1, groupChat, 7, 0, "Alice")
h.guess(card, 1, h.wrong(1)[0])
h.guess(card, 1, h.wrong(2)[1])
h.guess(card, 1, h.answer())
calls := h.rep.snapshot()
if len(calls) != 1 || calls[0].score != 4 || calls[0].user != 1 || calls[0].addr != (htmlgame.Address{ChatID: groupChat, MessageID: 7}) {
t.Fatalf("reports = %+v", calls)
}
// Reopening the same card does not report again.
h.state(card)
if n := len(h.rep.snapshot()); n != 1 {
t.Fatalf("reopen reported again: %d", n)
}
// Opening another card reports the day's result there, once.
other := h.tokenFor(1, -200, 9, 0, "Alice")
h.state(other)
h.state(other)
inline, _ := h.svc.signToken(guessgame.Claims{UserID: 1, InlineID: "BAAAInline", Expiry: h.clock.now().Add(guessgame.TokenTTL).Unix()})
h.state(inline)
calls = h.rep.snapshot()
if len(calls) != 3 || calls[1].addr != (htmlgame.Address{ChatID: -200, MessageID: 9}) || calls[1].score != 4 ||
calls[2].addr != (htmlgame.Address{InlineID: "BAAAInline"}) {
t.Fatalf("reports = %+v", calls)
}
}
func TestScore_LossIsNotReported(t *testing.T) {
h := newHarness(t)
tok := h.dmToken(1)
for _, w := range h.wrong(6) {
h.guess(tok, 1, w)
}
h.state(h.tokenFor(1, groupChat, 7, 0, "Alice"))
if n := len(h.rep.snapshot()); n != 0 {
t.Fatalf("loss reported %d times", n)
}
}
// A score that does not beat the player's best on the card is Telegram's
// expected answer: the card counts as reported and is not tried again.
func TestScore_NotModifiedCountsAsReported(t *testing.T) {
h := newHarness(t)
h.rep.err = errors.New("Bad Request: BOT_SCORE_NOT_MODIFIED")
h.win(2)
h.state(h.dmToken(2))
if calls := h.rep.snapshot(); len(calls) != 1 || calls[0].score != 6 {
t.Fatalf("reports = %+v", calls)
}
}
// A report that failed (a timeout, a 5xx) leaves the card unrecorded, so
// opening it again reports the win until Telegram accepts it.
func TestScore_FailedReportIsRetriedOnTheNextLoad(t *testing.T) {
h := newHarness(t)
card := h.tokenFor(1, groupChat, 7, 0, "Alice")
h.rep.err = errors.New("context deadline exceeded")
h.guess(card, 1, h.answer())
h.rep.err = nil
h.state(card)
h.state(card)
calls := h.rep.snapshot()
if len(calls) != 2 || calls[1].score != 6 || calls[1].addr != (htmlgame.Address{ChatID: groupChat, MessageID: 7}) {
t.Fatalf("reports = %+v", calls)
}
}