220 lines
6.1 KiB
Go
220 lines
6.1 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.threesix.ai/jordan/slack5-1770544098/pkg/logging"
|
|
"git.threesix.ai/jordan/slack5-1770544098/services/preferences-api/internal/domain"
|
|
"git.threesix.ai/jordan/slack5-1770544098/services/preferences-api/internal/port"
|
|
)
|
|
|
|
// mockPreferencesRepository implements port.PreferencesRepository for testing.
|
|
type mockPreferencesRepository struct {
|
|
prefs map[string]*domain.UserPreferences
|
|
err error // inject error for testing error paths
|
|
}
|
|
|
|
var _ port.PreferencesRepository = (*mockPreferencesRepository)(nil)
|
|
|
|
func newMockPreferencesRepository() *mockPreferencesRepository {
|
|
return &mockPreferencesRepository{
|
|
prefs: make(map[string]*domain.UserPreferences),
|
|
}
|
|
}
|
|
|
|
func (m *mockPreferencesRepository) Get(ctx context.Context, userID string) (*domain.UserPreferences, error) {
|
|
if m.err != nil {
|
|
return nil, m.err
|
|
}
|
|
p, ok := m.prefs[userID]
|
|
if !ok {
|
|
return nil, nil
|
|
}
|
|
cp := *p
|
|
return &cp, nil
|
|
}
|
|
|
|
func (m *mockPreferencesRepository) Upsert(ctx context.Context, userID string, prefs map[string]any) (*domain.UserPreferences, error) {
|
|
if m.err != nil {
|
|
return nil, m.err
|
|
}
|
|
|
|
existing, ok := m.prefs[userID]
|
|
if !ok {
|
|
existing = &domain.UserPreferences{
|
|
UserID: userID,
|
|
Preferences: map[string]any{},
|
|
}
|
|
}
|
|
|
|
// Merge preferences
|
|
merged := make(map[string]any)
|
|
for k, v := range existing.Preferences {
|
|
merged[k] = v
|
|
}
|
|
for k, v := range prefs {
|
|
merged[k] = v
|
|
}
|
|
|
|
result := &domain.UserPreferences{
|
|
UserID: userID,
|
|
Preferences: merged,
|
|
}
|
|
m.prefs[userID] = result
|
|
return result, nil
|
|
}
|
|
|
|
func TestPreferencesService_Get(t *testing.T) {
|
|
t.Run("returns empty preferences for new user", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
prefs, err := svc.Get(context.Background(), "user-1")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if prefs.UserID != "user-1" {
|
|
t.Errorf("expected user_id 'user-1', got '%s'", prefs.UserID)
|
|
}
|
|
if len(prefs.Preferences) != 0 {
|
|
t.Errorf("expected empty preferences, got %v", prefs.Preferences)
|
|
}
|
|
})
|
|
|
|
t.Run("returns existing preferences", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
repo.prefs["user-1"] = &domain.UserPreferences{
|
|
UserID: "user-1",
|
|
Preferences: map[string]any{"theme": "dark"},
|
|
}
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
prefs, err := svc.Get(context.Background(), "user-1")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if prefs.Preferences["theme"] != "dark" {
|
|
t.Errorf("expected theme 'dark', got '%v'", prefs.Preferences["theme"])
|
|
}
|
|
})
|
|
|
|
t.Run("returns error on repository failure", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
repo.err = errors.New("db connection failed")
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
_, err := svc.Get(context.Background(), "user-1")
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestPreferencesService_Update(t *testing.T) {
|
|
t.Run("updates with valid preferences", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
result, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"theme": "dark",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if result.Preferences["theme"] != "dark" {
|
|
t.Errorf("expected theme 'dark', got '%v'", result.Preferences["theme"])
|
|
}
|
|
})
|
|
|
|
t.Run("rejects unknown preference key", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
_, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"unknown_key": "value",
|
|
})
|
|
if !errors.Is(err, domain.ErrInvalidPreferenceKey) {
|
|
t.Errorf("expected ErrInvalidPreferenceKey, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("rejects invalid theme value", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
_, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"theme": "blue",
|
|
})
|
|
if !errors.Is(err, domain.ErrInvalidPreferenceValue) {
|
|
t.Errorf("expected ErrInvalidPreferenceValue, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("rejects invalid language format", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
_, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"language": "english",
|
|
})
|
|
if !errors.Is(err, domain.ErrInvalidPreferenceValue) {
|
|
t.Errorf("expected ErrInvalidPreferenceValue, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("rejects non-boolean notifications_enabled", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
_, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"notifications_enabled": "yes",
|
|
})
|
|
if !errors.Is(err, domain.ErrInvalidPreferenceValue) {
|
|
t.Errorf("expected ErrInvalidPreferenceValue, got %v", err)
|
|
}
|
|
})
|
|
|
|
t.Run("returns error on repository failure", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
repo.err = errors.New("db write failed")
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
_, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"theme": "dark",
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
})
|
|
|
|
t.Run("merges with existing preferences", func(t *testing.T) {
|
|
repo := newMockPreferencesRepository()
|
|
svc := NewPreferencesService(repo, logging.Nop())
|
|
|
|
// Set initial preference
|
|
_, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"theme": "dark",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error on first update: %v", err)
|
|
}
|
|
|
|
// Update with different key
|
|
result, err := svc.Update(context.Background(), "user-1", map[string]any{
|
|
"language": "en",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error on second update: %v", err)
|
|
}
|
|
|
|
if result.Preferences["theme"] != "dark" {
|
|
t.Errorf("expected theme 'dark' to be preserved, got '%v'", result.Preferences["theme"])
|
|
}
|
|
if result.Preferences["language"] != "en" {
|
|
t.Errorf("expected language 'en', got '%v'", result.Preferences["language"])
|
|
}
|
|
})
|
|
}
|