635f2fd9fc
- #3 nudge-on-own-turn: distinct result code nudge_own_turn + i18n (was reused 'not_your_turn') - #2 sanitize connector registration name to the editable format; Player/Игрок-XXXXX fallback - #5 variant-aware robot name pools (composed full/colloquial first + surname forms; ru gets <=20% latin) - #4 move-number-aware robot move timing (early 1-5min -> late 10-90min, skew k=4) - #7 emit move event to the actor too (multi-device sync); opponent_moved stays in-app only - #1 live game_move_duration{variant,phase} histogram + admin console per-user min/avg/max columns and an inline-SVG move-time-by-move-number chart (offline from the journal) - ProvisionRobot bypasses editor name validation (system names like 'Peter J.')
76 lines
3.0 KiB
Go
76 lines
3.0 KiB
Go
package account
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// TestTelegramSeed covers the pure mapping from Telegram launch fields to the
|
|
// create-time account seed: supported-language detection (bare and region-tagged),
|
|
// the first-name / username display-name precedence, and the sanitization that
|
|
// strips disallowed characters (emoji, digits, punctuation) to the editable format.
|
|
func TestTelegramSeed(t *testing.T) {
|
|
cases := map[string]struct {
|
|
languageCode, username, firstName string
|
|
wantLang, wantName string
|
|
}{
|
|
"ru bare": {"ru", "user", "Иван", "ru", "Иван"},
|
|
"en region-tagged": {"en-US", "user", "John", "en", "John"},
|
|
"ru region-tagged": {"ru-RU", "", "Пётр", "ru", "Пётр"},
|
|
"unknown language": {"fr", "frodo", "Frodo", "", "Frodo"},
|
|
"empty language": {"", "neo", "Neo", "", "Neo"},
|
|
"first name wins": {"en", "handle", "Real Name", "en", "Real Name"},
|
|
"username fallback": {"en", "handle", "", "en", "handle"},
|
|
"trimmed": {" RU ", " ", " Anna ", "ru", "Anna"},
|
|
"emoji stripped": {"en", "user", "🎮Kaya🎮", "en", "Kaya"},
|
|
"punct to space": {"en", "user", "John❤Doe", "en", "John Doe"},
|
|
"digits dropped": {"ru", "user", "Маша123", "ru", "Маша"},
|
|
"garbage to username": {"en", "good", "123!@#", "en", "good"},
|
|
}
|
|
for name, tc := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
got := telegramSeed(tc.languageCode, tc.username, tc.firstName)
|
|
if got.preferredLanguage != tc.wantLang {
|
|
t.Errorf("preferredLanguage = %q, want %q", got.preferredLanguage, tc.wantLang)
|
|
}
|
|
if got.displayName != tc.wantName {
|
|
t.Errorf("displayName = %q, want %q", got.displayName, tc.wantName)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTelegramSeedPlaceholder checks that a name with no usable letters falls back to
|
|
// a generated placeholder in the seeded language ("Player-NNNNN" / "Игрок-NNNNN").
|
|
func TestTelegramSeedPlaceholder(t *testing.T) {
|
|
cases := map[string]struct {
|
|
languageCode, username, firstName string
|
|
wantRe string
|
|
}{
|
|
"en empty": {"en", "", "", `^Player-\d{5}$`},
|
|
"ru empty": {"ru", "", "", `^Игрок-\d{5}$`},
|
|
"default en": {"fr", "", "", `^Player-\d{5}$`},
|
|
"both garbage": {"ru", "123", "!!!", `^Игрок-\d{5}$`},
|
|
}
|
|
for name, tc := range cases {
|
|
t.Run(name, func(t *testing.T) {
|
|
got := telegramSeed(tc.languageCode, tc.username, tc.firstName).displayName
|
|
if !regexp.MustCompile(tc.wantRe).MatchString(got) {
|
|
t.Errorf("displayName = %q, want match %s", got, tc.wantRe)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestTelegramSeedTruncatesLongName checks an over-long Telegram name is capped to
|
|
// maxDisplayName runes (counted in runes, not bytes).
|
|
func TestTelegramSeedTruncatesLongName(t *testing.T) {
|
|
long := strings.Repeat("я", maxDisplayName+5)
|
|
got := telegramSeed("ru", "", long)
|
|
if n := utf8.RuneCountInString(got.displayName); n != maxDisplayName {
|
|
t.Errorf("display name rune count = %d, want %d", n, maxDisplayName)
|
|
}
|
|
}
|