Files
kandev/apps/backend/internal/user/service/service_test.go
T
2026-02-26 23:08:15 +00:00

188 lines
5.6 KiB
Go

package service
import (
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/kandev/kandev/internal/user/models"
)
func ptr[T any](v T) *T { return &v }
func makeLayouts(n int) []models.SavedLayout {
layouts := make([]models.SavedLayout, n)
for i := range layouts {
layouts[i] = models.SavedLayout{
ID: fmt.Sprintf("layout-%d", i),
Name: fmt.Sprintf("Layout %d", i),
IsDefault: false,
Layout: json.RawMessage(`{}`),
CreatedAt: "2026-01-01T00:00:00Z",
}
}
return layouts
}
func TestApplyBasicSettings_ReleaseNotes(t *testing.T) {
t.Run("nil fields leave settings unchanged", func(t *testing.T) {
settings := &models.UserSettings{
ShowReleaseNotification: true,
ReleaseNotesLastSeenVersion: "1.0.0",
}
req := &UpdateUserSettingsRequest{}
if err := applyBasicSettings(settings, req); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if settings.ShowReleaseNotification != true {
t.Fatalf("expected ShowReleaseNotification=true, got %v", settings.ShowReleaseNotification)
}
if settings.ReleaseNotesLastSeenVersion != "1.0.0" {
t.Fatalf("expected ReleaseNotesLastSeenVersion=1.0.0, got %s", settings.ReleaseNotesLastSeenVersion)
}
})
t.Run("ShowReleaseNotification set to false", func(t *testing.T) {
settings := &models.UserSettings{ShowReleaseNotification: true}
req := &UpdateUserSettingsRequest{ShowReleaseNotification: ptr(false)}
if err := applyBasicSettings(settings, req); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if settings.ShowReleaseNotification != false {
t.Fatalf("expected ShowReleaseNotification=false, got %v", settings.ShowReleaseNotification)
}
})
t.Run("ShowReleaseNotification set to true", func(t *testing.T) {
settings := &models.UserSettings{ShowReleaseNotification: false}
req := &UpdateUserSettingsRequest{ShowReleaseNotification: ptr(true)}
if err := applyBasicSettings(settings, req); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if settings.ShowReleaseNotification != true {
t.Fatalf("expected ShowReleaseNotification=true, got %v", settings.ShowReleaseNotification)
}
})
t.Run("ReleaseNotesLastSeenVersion updated", func(t *testing.T) {
settings := &models.UserSettings{ReleaseNotesLastSeenVersion: "1.0.0"}
req := &UpdateUserSettingsRequest{ReleaseNotesLastSeenVersion: ptr("2.0.0")}
if err := applyBasicSettings(settings, req); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if settings.ReleaseNotesLastSeenVersion != "2.0.0" {
t.Fatalf("expected ReleaseNotesLastSeenVersion=2.0.0, got %s", settings.ReleaseNotesLastSeenVersion)
}
})
t.Run("ReleaseNotesLastSeenVersion cleared with empty string", func(t *testing.T) {
settings := &models.UserSettings{ReleaseNotesLastSeenVersion: "1.0.0"}
req := &UpdateUserSettingsRequest{ReleaseNotesLastSeenVersion: ptr("")}
if err := applyBasicSettings(settings, req); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if settings.ReleaseNotesLastSeenVersion != "" {
t.Fatalf("expected empty ReleaseNotesLastSeenVersion, got %s", settings.ReleaseNotesLastSeenVersion)
}
})
}
func TestApplySavedLayouts(t *testing.T) {
tests := []struct {
name string
req *UpdateUserSettingsRequest
wantErr string
wantCount int
wantApplied bool
}{
{
name: "nil request is a no-op",
req: &UpdateUserSettingsRequest{SavedLayouts: nil},
wantApplied: false,
},
{
name: "empty slice is accepted",
req: &UpdateUserSettingsRequest{SavedLayouts: ptr([]models.SavedLayout{})},
wantCount: 0,
wantApplied: true,
},
{
name: "valid single layout is applied",
req: &UpdateUserSettingsRequest{
SavedLayouts: ptr(makeLayouts(1)),
},
wantCount: 1,
wantApplied: true,
},
{
name: "exactly max layouts is accepted",
req: &UpdateUserSettingsRequest{
SavedLayouts: ptr(makeLayouts(maxSavedLayouts)),
},
wantCount: maxSavedLayouts,
wantApplied: true,
},
{
name: "exceeding max layouts returns error",
req: &UpdateUserSettingsRequest{
SavedLayouts: ptr(makeLayouts(maxSavedLayouts + 1)),
},
wantErr: fmt.Sprintf("saved_layouts: max %d layouts allowed", maxSavedLayouts),
},
{
name: "empty name returns error",
req: &UpdateUserSettingsRequest{
SavedLayouts: ptr([]models.SavedLayout{
{ID: "l1", Name: "", Layout: json.RawMessage(`{}`)},
}),
},
wantErr: "saved_layouts: layout name must not be empty",
},
{
name: "whitespace-only name returns error",
req: &UpdateUserSettingsRequest{
SavedLayouts: ptr([]models.SavedLayout{
{ID: "l1", Name: " ", Layout: json.RawMessage(`{}`)},
}),
},
wantErr: "saved_layouts: layout name must not be empty",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
settings := &models.UserSettings{
SavedLayouts: makeLayouts(2), // pre-existing layouts
}
err := applySavedLayouts(settings, tt.req)
if tt.wantErr != "" {
if err == nil {
t.Fatalf("expected error %q, got nil", tt.wantErr)
}
if !strings.Contains(err.Error(), tt.wantErr) {
t.Fatalf("expected error containing %q, got %q", tt.wantErr, err.Error())
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !tt.wantApplied {
// Nil request should leave settings unchanged
if len(settings.SavedLayouts) != 2 {
t.Fatalf("expected settings unchanged (2 layouts), got %d", len(settings.SavedLayouts))
}
return
}
if len(settings.SavedLayouts) != tt.wantCount {
t.Fatalf("expected %d layouts, got %d", tt.wantCount, len(settings.SavedLayouts))
}
})
}
}