Files
sm-user-plugin/classes/handle_generator_test.go
Jakub Zych 15b19c7021 feat(19-04): add users.handle, HandleGenerator and user.Created
Generate a unique mention handle on API register and admin create, and fire user.Created from both paths so listeners run in the same write.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-17 21:44:59 +02:00

79 lines
2.3 KiB
Go

package classes
import "testing"
// TestHandle: generator charset is a subset of MentionParser, uniqueness
// uses the smallest free integer suffix, and short names fall back to user{id}.
func TestHandle(t *testing.T) {
t.Parallel()
g := HandleGenerator{}
if got := g.Slug("Paweł"); got != "pawel" {
t.Fatalf("slug Paweł = %q, want pawel", got)
}
if got := g.Slug("John Doe"); got != "johndoe" {
t.Fatalf("slug John Doe = %q, want johndoe", got)
}
if got := g.Slug("Zoë"); got != "zoe" {
t.Fatalf("slug Zoë = %q, want zoe", got)
}
handle := g.Generate("James", func(string) bool { return false }, nil)
if handle != "james" {
t.Fatalf("generate James = %q, want james", handle)
}
if !validHandle(handle) {
t.Fatalf("generated %q is outside [a-z0-9._-]{2,255}", handle)
}
taken := map[string]struct{}{}
isTaken := func(c string) bool {
_, ok := taken[c]
return ok
}
first := g.Generate("James", isTaken, nil)
taken[first] = struct{}{}
second := g.Generate("James", isTaken, nil)
taken[second] = struct{}{}
third := g.Generate("James", isTaken, nil)
if first != "james" || second != "james1" || third != "james2" {
t.Fatalf("suffixes = %q %q %q, want james james1 james2", first, second, third)
}
id := uint(42)
if got := g.Generate("", func(string) bool { return false }, &id); got != "user42" {
t.Fatalf("empty name = %q, want user42", got)
}
if got := g.Generate("x", func(string) bool { return false }, &id); got != "user42" {
t.Fatalf("short name = %q, want user42", got)
}
if got := g.Generate("???", func(string) bool { return false }, &id); got != "user42" {
t.Fatalf("punctuation name = %q, want user42", got)
}
if got := g.Generate("", func(string) bool { return false }, nil); got != "user" {
t.Fatalf("no id = %q, want user", got)
}
names := []string{"James", "Paweł", "John Doe", "???", "a", "Zoë", " ", "....", "O'Brien"}
for _, name := range names {
got := g.Generate(name, func(string) bool { return false }, &id)
if !validHandle(got) {
t.Errorf("generated %q for %q is outside the MentionParser subset", got, name)
}
}
}
func validHandle(s string) bool {
if len(s) < 2 || len(s) > 255 {
return false
}
for _, r := range s {
switch {
case r >= 'a' && r <= 'z', r >= '0' && r <= '9', r == '.', r == '_', r == '-':
default:
return false
}
}
return true
}