57c778f9b2
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 54s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s
Collapse the two per-language Telegram bots into one unified bot and
replace language-based variant gating with explicit per-user variant
preferences.
- Telegram: one bot; drop service_language and the supported_languages
set everywhere (DB, account, auth, FlatBuffers Session wire, gateway,
connector proto). The single bot renders chat and out-of-app push in
the recipient's preferred_language; remove the game-language push
routing override (notify Intent.Language / push Event.language).
- Preferences: new accounts.variant_preferences (text[], DB default
{erudit_ru}, CHECK non-empty + subset of the three variants). Gates
the New Game picker, vs-AI and the friend invitation the player
creates, enforced server-side (HTTP 400 otherwise); an invited friend
may still accept any variant. Edited on the Settings screen; variants
are Erudit-first everywhere.
- Admin: drop the per-bot language selectors (broadcast / send-to-user)
and the feedback channel_lang column/field.
- Env/CI: collapse TELEGRAM_BOT_TOKEN_{EN,RU}, TELEGRAM_GAME_CHANNEL_ID_{EN,RU},
VITE_TELEGRAM_LINK{,_EN,_RU} and VITE_TELEGRAM_GAME_CHANNEL_NAME_{EN,RU}
to single unsuffixed names; drop GATEWAY_DEFAULT_SUPPORTED_LANGUAGES.
- Docs updated (ARCHITECTURE, FUNCTIONAL + _ru, platform/telegram, gateway,
backend, ui, UI_DESIGN, PRERELEASE).
The migration squash is deferred to a follow-up PR.
64 lines
2.6 KiB
Go
64 lines
2.6 KiB
Go
package account
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// TestUpdateProfileValidation checks that bad fields are rejected before any
|
|
// database access, so a nil-backed Store is enough to exercise the guards. It also
|
|
// confirms UpdateProfile wires the validators (name format, away window,
|
|
// offset/IANA timezone), not just their unit tests in validate_test.go.
|
|
func TestUpdateProfileValidation(t *testing.T) {
|
|
s := &Store{}
|
|
base := ProfileUpdate{DisplayName: "Kaya", PreferredLanguage: "en", TimeZone: "UTC", VariantPreferences: []string{"erudit_ru"}}
|
|
hm := func(h, m int) time.Time { return time.Date(0, 1, 1, h, m, 0, 0, time.UTC) }
|
|
tests := []struct {
|
|
name string
|
|
mut func(p *ProfileUpdate)
|
|
}{
|
|
{"unknown language", func(p *ProfileUpdate) { p.PreferredLanguage = "fr" }},
|
|
{"invalid timezone", func(p *ProfileUpdate) { p.TimeZone = "Mars/Olympus" }},
|
|
{"bad offset timezone", func(p *ProfileUpdate) { p.TimeZone = "+15:00" }},
|
|
{"over-long name", func(p *ProfileUpdate) { p.DisplayName = strings.Repeat("x", maxDisplayName+1) }},
|
|
{"bad name layout", func(p *ProfileUpdate) { p.DisplayName = "Bad__Name" }},
|
|
{"away over 12h", func(p *ProfileUpdate) { p.AwayStart, p.AwayEnd = hm(8, 0), hm(21, 0) }},
|
|
{"empty variant preferences", func(p *ProfileUpdate) { p.VariantPreferences = nil }},
|
|
{"unknown variant preference", func(p *ProfileUpdate) { p.VariantPreferences = []string{"chess"} }},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
p := base
|
|
tc.mut(&p)
|
|
if _, err := s.UpdateProfile(context.Background(), uuid.New(), p); !errors.Is(err, ErrInvalidProfile) {
|
|
t.Fatalf("err = %v, want ErrInvalidProfile", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestValidateVariantPreferences checks the cleaning of a profile's variant set:
|
|
// duplicates collapse, the result is canonically ordered (Erudit, Russian Scrabble,
|
|
// English) regardless of input order, and an empty or unknown set is rejected.
|
|
func TestValidateVariantPreferences(t *testing.T) {
|
|
got, err := validateVariantPreferences([]string{"scrabble_en", "erudit_ru", "scrabble_en"})
|
|
if err != nil {
|
|
t.Fatalf("validate: %v", err)
|
|
}
|
|
if want := []string{"erudit_ru", "scrabble_en"}; !slices.Equal(got, want) {
|
|
t.Fatalf("got %v, want %v", got, want)
|
|
}
|
|
if _, err := validateVariantPreferences(nil); !errors.Is(err, ErrInvalidProfile) {
|
|
t.Fatalf("empty err = %v, want ErrInvalidProfile", err)
|
|
}
|
|
if _, err := validateVariantPreferences([]string{"chess"}); !errors.Is(err, ErrInvalidProfile) {
|
|
t.Fatalf("unknown err = %v, want ErrInvalidProfile", err)
|
|
}
|
|
}
|