Files
miti99bot/internal/modules/lolschedule/format_test.go
T
tiennm99 b81b6501c5 refactor(storage): replace KVStore with generic typed DocStore[T]
Delete the byte-oriented KVStore/VersionedStore abstraction and the
DynamoDB/memory KV backends. Add a generic typed store (DocStore[T] with
Provider/Collection/Typed) persisting each value as a flattened native
Mongo document (storedDoc[T] via bson inline) — no value envelope.

- MongoDB is the only runtime backend; memory kept for tests/local.
- All modules + deploynotify use typed stores; persisted structs carry
  bson tags == json names (incl. nested lolschedule/wordle types).
- lolschedule wraps its array/scalar values in named structs.
- migrate-dynamo-to-mongo writes the flattened shape via Typed[bson.M]
  with wrap rules; Scan/--dry-run/--verify retained.

Verified: go vet/build clean; full go test green hermetically and
in-container vs real Mongo 7 + DynamoDB Local (storage integration +
migrator e2e).
2026-06-28 18:02:11 +07:00

186 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package lolschedule
import (
"strings"
"testing"
"time"
)
func mkEvent(state, slug, name, t1Code, t2Code, startISO string) ScheduleEvent {
return ScheduleEvent{
StartTime: startISO,
State: state,
League: League{Slug: slug, Name: name},
Match: Match{
Teams: []Team{{Code: t1Code}, {Code: t2Code}},
Strategy: Strategy{Type: "bestOf", Count: 3},
},
}
}
func TestFormatEventLine_Unstarted(t *testing.T) {
e := mkEvent("unstarted", "lck", "LCK", "T1", "GEN", "2026-05-09T05:00:00Z")
got := formatEventLine(e)
if !strings.Contains(got, "🕒") {
t.Errorf("missing clock emoji: %q", got)
}
if !strings.Contains(got, "T1 vs GEN") {
t.Errorf("missing team labels: %q", got)
}
if !strings.Contains(got, "Bo3") {
t.Errorf("missing Bo3: %q", got)
}
// 05:00 UTC == 12:00 ICT.
if !strings.Contains(got, "12:00") {
t.Errorf("ICT time wrong; got %q (expected 12:00)", got)
}
}
func TestFormatEventLine_Completed_BoldsWinner(t *testing.T) {
winResult := &TeamResult{Outcome: "win", GameWins: 3}
loseResult := &TeamResult{Outcome: "loss", GameWins: 1}
e := ScheduleEvent{
StartTime: "2026-05-09T05:00:00Z",
State: "completed",
League: League{Slug: "lck", Name: "LCK"},
Match: Match{
Teams: []Team{
{Code: "T1", Result: winResult},
{Code: "GEN", Result: loseResult},
},
Strategy: Strategy{Count: 5},
},
}
got := formatEventLine(e)
if !strings.Contains(got, "✅") {
t.Errorf("missing completed emoji: %q", got)
}
if !strings.Contains(got, "<b>T1</b>") {
t.Errorf("winner not bolded: %q", got)
}
if !strings.Contains(got, "31") {
t.Errorf("score missing: %q", got)
}
if strings.Contains(got, "<b>GEN</b>") {
t.Errorf("loser should not be bolded: %q", got)
}
}
func TestFormatEventLine_InProgress(t *testing.T) {
w := &TeamResult{GameWins: 1}
e := ScheduleEvent{
StartTime: "2026-05-09T05:00:00Z",
State: "inProgress",
League: League{Slug: "lck"},
Match: Match{
Teams: []Team{{Code: "T1", Result: w}, {Code: "GEN", Result: w}},
Strategy: Strategy{Count: 5},
},
}
got := formatEventLine(e)
if !strings.Contains(got, "🔴 LIVE") {
t.Errorf("missing LIVE marker: %q", got)
}
if !strings.Contains(got, "11") {
t.Errorf("score missing: %q", got)
}
}
func TestRenderToday_GroupsByLeagueInOrder(t *testing.T) {
day := time.Date(2026, 5, 9, 0, 0, 0, 0, IctLocation)
events := []ScheduleEvent{
mkEvent("unstarted", "lcs", "LCS", "TL", "C9", "2026-05-09T18:00:00Z"),
mkEvent("unstarted", "lck", "LCK", "T1", "GEN", "2026-05-09T05:00:00Z"),
mkEvent("unstarted", "lpl", "LPL", "JDG", "BLG", "2026-05-09T08:00:00Z"),
}
got := RenderToday(events, day)
// LEAGUE_ORDER puts LCK before LPL before LCS.
idxLck := strings.Index(got, "<b>LCK</b>")
idxLpl := strings.Index(got, "<b>LPL</b>")
idxLcs := strings.Index(got, "<b>LCS</b>")
if idxLck < 0 || idxLpl < 0 || idxLcs < 0 {
t.Fatalf("missing league section; got:\n%s", got)
}
if idxLck >= idxLpl || idxLpl >= idxLcs {
t.Errorf("league order wrong: lck=%d lpl=%d lcs=%d\n%s", idxLck, idxLpl, idxLcs, got)
}
// Header in ICT.
if !strings.Contains(got, "LoL — Sat May 9</b> (ICT)") {
t.Errorf("header wrong: %q", got)
}
}
func TestRenderToday_EmptyShowsNoMatches(t *testing.T) {
day := time.Date(2026, 5, 9, 0, 0, 0, 0, IctLocation)
got := RenderToday(nil, day)
if !strings.Contains(got, "No matches today.") {
t.Errorf("empty render missing 'No matches today.': %q", got)
}
}
func TestRenderWeek_GroupsByLeagueAndDay(t *testing.T) {
from := time.Date(2026, 5, 9, 0, 0, 0, 0, IctLocation)
to := from.AddDate(0, 0, 7)
events := []ScheduleEvent{
mkEvent("unstarted", "lck", "LCK", "T1", "GEN", "2026-05-09T05:00:00Z"),
mkEvent("unstarted", "lck", "LCK", "DK", "KT", "2026-05-10T05:00:00Z"),
}
got := RenderWeek(events, from, to)
if !strings.Contains(got, "<b>LCK</b>") {
t.Errorf("missing LCK section: %q", got)
}
// Both days should appear under LCK.
if !strings.Contains(got, "Sat May 9") {
t.Errorf("missing Sat May 9: %q", got)
}
if !strings.Contains(got, "Sun May 10") {
t.Errorf("missing Sun May 10: %q", got)
}
}
func TestFilterMajor(t *testing.T) {
events := []ScheduleEvent{
{League: League{Slug: "lck"}},
{League: League{Slug: "lpl"}},
{League: League{Slug: "tcl"}}, // Turkish league — not in allowlist
{League: League{Slug: "lja"}}, // Japan academy — not in allowlist
{League: League{Slug: "msi"}},
}
got := FilterMajor(events)
if len(got) != 3 {
t.Errorf("filtered count = %d, want 3", len(got))
}
for _, e := range got {
if e.League.Slug == "tcl" || e.League.Slug == "lja" {
t.Errorf("non-major league leaked: %s", e.League.Slug)
}
}
}
func TestFormatEventLine_EscapesUserStrings(t *testing.T) {
e := ScheduleEvent{
StartTime: "2026-05-09T05:00:00Z",
State: "unstarted",
BlockName: "<script>",
League: League{Slug: "lck"},
Match: Match{
Teams: []Team{
{Name: "Tom & Jerry"},
{Name: `"Quotes"`},
},
Strategy: Strategy{Count: 1},
},
}
got := formatEventLine(e)
if strings.Contains(got, "<script>") {
t.Errorf("raw <script> leaked: %q", got)
}
if !strings.Contains(got, "&lt;script&gt;") {
t.Errorf("BlockName not escaped: %q", got)
}
if strings.Contains(got, "Tom & Jerry") {
// & should be escaped to &amp;
t.Errorf("ampersand not escaped: %q", got)
}
}