slate-v3-1770514618/services/preferences-api/internal/domain/preferences_test.go
rdev-worker 1afe983cd6
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
build: /implement-feature user-preferences
2026-02-08 02:02:18 +00:00

212 lines
5.1 KiB
Go

package domain
import (
"testing"
)
func TestNewDefaultPreferences(t *testing.T) {
p := NewDefaultPreferences("user-123")
if p.UserID != "user-123" {
t.Errorf("expected UserID 'user-123', got '%s'", p.UserID)
}
if p.Theme != "system" {
t.Errorf("expected theme 'system', got '%s'", p.Theme)
}
if p.Language != "en" {
t.Errorf("expected language 'en', got '%s'", p.Language)
}
if !p.Notifications.Email {
t.Error("expected email=true")
}
if !p.Notifications.Push {
t.Error("expected push=true")
}
if p.Notifications.Digest != "weekly" {
t.Errorf("expected digest 'weekly', got '%s'", p.Notifications.Digest)
}
if p.UpdatedAt.IsZero() {
t.Error("expected non-zero UpdatedAt")
}
}
func TestPreferences_Validate(t *testing.T) {
tests := []struct {
name string
modify func(p *Preferences)
wantErr error
}{
{
name: "valid defaults",
modify: func(p *Preferences) {},
wantErr: nil,
},
{
name: "valid light theme",
modify: func(p *Preferences) { p.Theme = "light" },
wantErr: nil,
},
{
name: "valid dark theme",
modify: func(p *Preferences) { p.Theme = "dark" },
wantErr: nil,
},
{
name: "invalid theme",
modify: func(p *Preferences) { p.Theme = "blue" },
wantErr: ErrInvalidTheme,
},
{
name: "empty theme",
modify: func(p *Preferences) { p.Theme = "" },
wantErr: ErrInvalidTheme,
},
{
name: "valid language es",
modify: func(p *Preferences) { p.Language = "es" },
wantErr: nil,
},
{
name: "invalid language - too long",
modify: func(p *Preferences) { p.Language = "eng" },
wantErr: ErrInvalidLanguage,
},
{
name: "invalid language - uppercase",
modify: func(p *Preferences) { p.Language = "EN" },
wantErr: ErrInvalidLanguage,
},
{
name: "invalid language - empty",
modify: func(p *Preferences) { p.Language = "" },
wantErr: ErrInvalidLanguage,
},
{
name: "valid digest daily",
modify: func(p *Preferences) { p.Notifications.Digest = "daily" },
wantErr: nil,
},
{
name: "valid digest never",
modify: func(p *Preferences) { p.Notifications.Digest = "never" },
wantErr: nil,
},
{
name: "invalid digest",
modify: func(p *Preferences) { p.Notifications.Digest = "monthly" },
wantErr: ErrInvalidDigest,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := NewDefaultPreferences("user-1")
tt.modify(p)
err := p.Validate()
if err != tt.wantErr {
t.Errorf("expected error %v, got %v", tt.wantErr, err)
}
})
}
}
func TestPreferences_MergeFrom(t *testing.T) {
strPtr := func(s string) *string { return &s }
boolPtr := func(b bool) *bool { return &b }
t.Run("nil update does nothing", func(t *testing.T) {
p := NewDefaultPreferences("user-1")
p.MergeFrom(nil)
if p.Theme != "system" {
t.Errorf("expected theme 'system', got '%s'", p.Theme)
}
})
t.Run("partial update - theme only", func(t *testing.T) {
p := NewDefaultPreferences("user-1")
p.MergeFrom(&PreferencesUpdate{
Theme: strPtr("dark"),
})
if p.Theme != "dark" {
t.Errorf("expected theme 'dark', got '%s'", p.Theme)
}
if p.Language != "en" {
t.Errorf("expected language 'en' unchanged, got '%s'", p.Language)
}
})
t.Run("partial update - language only", func(t *testing.T) {
p := NewDefaultPreferences("user-1")
p.MergeFrom(&PreferencesUpdate{
Language: strPtr("fr"),
})
if p.Language != "fr" {
t.Errorf("expected language 'fr', got '%s'", p.Language)
}
if p.Theme != "system" {
t.Errorf("expected theme 'system' unchanged, got '%s'", p.Theme)
}
})
t.Run("notifications sub-field merge", func(t *testing.T) {
p := NewDefaultPreferences("user-1")
p.MergeFrom(&PreferencesUpdate{
Notifications: &NotificationSettingsUpdate{
Email: boolPtr(false),
},
})
if p.Notifications.Email != false {
t.Error("expected email=false")
}
if p.Notifications.Push != true {
t.Error("expected push=true unchanged")
}
if p.Notifications.Digest != "weekly" {
t.Errorf("expected digest 'weekly' unchanged, got '%s'", p.Notifications.Digest)
}
})
t.Run("full update", func(t *testing.T) {
p := NewDefaultPreferences("user-1")
p.MergeFrom(&PreferencesUpdate{
Theme: strPtr("light"),
Language: strPtr("es"),
Notifications: &NotificationSettingsUpdate{
Email: boolPtr(false),
Push: boolPtr(false),
Digest: strPtr("daily"),
},
})
if p.Theme != "light" {
t.Errorf("expected theme 'light', got '%s'", p.Theme)
}
if p.Language != "es" {
t.Errorf("expected language 'es', got '%s'", p.Language)
}
if p.Notifications.Email != false {
t.Error("expected email=false")
}
if p.Notifications.Push != false {
t.Error("expected push=false")
}
if p.Notifications.Digest != "daily" {
t.Errorf("expected digest 'daily', got '%s'", p.Notifications.Digest)
}
})
}
func TestUserID(t *testing.T) {
id := UserID("test-123")
if id.String() != "test-123" {
t.Errorf("expected 'test-123', got '%s'", id.String())
}
if id.IsZero() {
t.Error("expected non-zero")
}
var empty UserID
if !empty.IsZero() {
t.Error("expected zero")
}
}