Files
verdande/internal/store/names_test.go
T
kristianwindandClaude Opus 4.8 247277e139 feat(users): always capitalise a display name's first letter
Andreas signed up as "andreas" and was shown that way everywhere. A name
is now capitalised on the way in — the first letter of each word raised,
the rest left as typed — in CreateUser (signup and invite-accept) and
UpdateProfile, so display, mail and export all agree.

Existing accounts are fixed by an idempotent startup pass
(NormalizeUserNames), the same shape as SealNotes: the rule is Unicode-aware
and per word, which SQLite's UPPER cannot spell, so it lives in Go rather
than a migration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-30 09:25:50 +02:00

82 lines
2.3 KiB
Go

package store
import (
"context"
"testing"
)
func TestNormalizeName(t *testing.T) {
cases := map[string]string{
"andreas": "Andreas",
"Andreas": "Andreas",
"anne mette": "Anne Mette",
" kristian ": "Kristian",
"anne mette": "Anne Mette",
"McDonald": "McDonald", // an existing inner capital is kept
"óscar": "Óscar", // Unicode initial
"æblegaard": "Æblegaard",
"": "",
" ": "",
"mette-marie sø": "Mette-marie Sø", // only word-initials are raised
"iPhone listen": "IPhone Listen", // deliberately: the first letter is raised
}
for in, want := range cases {
if got := NormalizeName(in); got != want {
t.Errorf("NormalizeName(%q) = %q, want %q", in, got, want)
}
}
}
func TestCreateUserCapitalisesName(t *testing.T) {
db := openTest(t)
ctx := context.Background()
u := &User{Email: "andreas@example.dk", Name: "andreas", PasswordHash: "x"}
if err := db.CreateUser(ctx, u, "Inbox"); err != nil {
t.Fatalf("create user: %v", err)
}
got, err := db.UserByEmail(ctx, "andreas@example.dk")
if err != nil {
t.Fatalf("read back: %v", err)
}
if got.Name != "Andreas" {
t.Errorf("stored name = %q, want %q", got.Name, "Andreas")
}
}
func TestNormalizeUserNamesBackfill(t *testing.T) {
db := openTest(t)
ctx := context.Background()
// An account as it looked before the app capitalised names: made properly,
// then set back to lower case behind the store's back to stand in for an old row.
u := &User{Email: "a@example.dk", Name: "Andreas", PasswordHash: "x"}
if err := db.CreateUser(ctx, u, "Inbox"); err != nil {
t.Fatalf("create user: %v", err)
}
if _, err := db.ExecContext(ctx, `UPDATE users SET name = 'andreas' WHERE id = ?`, u.ID); err != nil {
t.Fatalf("downgrade name: %v", err)
}
fixed, err := db.NormalizeUserNames(ctx)
if err != nil {
t.Fatalf("normalize: %v", err)
}
if fixed != 1 {
t.Fatalf("fixed = %d, want 1", fixed)
}
got, _ := db.UserByID(ctx, u.ID)
if got.Name != "Andreas" {
t.Errorf("name after backfill = %q, want %q", got.Name, "Andreas")
}
// Idempotent: nothing left to do on a second pass.
again, err := db.NormalizeUserNames(ctx)
if err != nil {
t.Fatalf("normalize again: %v", err)
}
if again != 0 {
t.Errorf("second pass fixed = %d, want 0", again)
}
}