Files
sendico/api/server/internal/server/accountapiimp/signup_test.go
Stephan D d367dddbbd
Some checks failed
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/fx/1 Pipeline failed
ci/woodpecker/push/nats Pipeline was successful
ci/woodpecker/push/fx/2 Pipeline failed
fx build fix
2025-11-08 00:40:01 +01:00

243 lines
6.2 KiB
Go

package accountapiimp
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/server/interface/api/srequest"
)
// Helper function to create string pointers
func stringPtr(s string) *string {
return &s
}
// TestTimezoneValidation tests timezone validation logic separately
func TestTimezoneValidation(t *testing.T) {
t.Run("ValidTimezones", func(t *testing.T) {
validTimezones := []string{
"UTC",
"America/New_York",
"Europe/London",
"Asia/Tokyo",
"Australia/Sydney",
}
for _, tz := range validTimezones {
t.Run(tz, func(t *testing.T) {
_, err := time.LoadLocation(tz)
assert.NoError(t, err, "Timezone %s should be valid", tz)
})
}
})
t.Run("InvalidTimezones", func(t *testing.T) {
invalidTimezones := []string{
"Invalid/Timezone",
"Not/A/Timezone",
"BadTimezone",
"America/NotACity",
}
for _, tz := range invalidTimezones {
t.Run(tz, func(t *testing.T) {
_, err := time.LoadLocation(tz)
assert.Error(t, err, "Timezone %s should be invalid", tz)
})
}
})
}
// TestCreateValidSignupRequest tests the helper function for creating valid requests
func TestCreateValidSignupRequest(t *testing.T) {
request := srequest.Signup{
Account: model.AccountData{
LoginData: model.LoginData{
UserDataBase: model.UserDataBase{
Login: "test@example.com",
},
Password: "TestPassword123!",
},
Name: "Test User",
},
OrganizationName: "Test Organization",
OrganizationTimeZone: "UTC",
AnonymousUser: model.Describable{
Name: "Anonymous User",
},
OwnerRole: model.Describable{
Name: "Owner",
},
AnonymousRole: model.Describable{
Name: "Anonymous",
},
}
// Validate the request structure
assert.Equal(t, "test@example.com", request.Account.Login)
assert.Equal(t, "TestPassword123!", request.Account.Password)
assert.Equal(t, "Test User", request.Account.Name)
assert.Equal(t, "Test Organization", request.OrganizationName)
assert.Equal(t, "UTC", request.OrganizationTimeZone)
}
// TestSignupRequestValidation tests various signup request validation scenarios
func TestSignupRequestValidation(t *testing.T) {
t.Run("ValidRequest", func(t *testing.T) {
request := srequest.Signup{
Account: model.AccountData{
LoginData: model.LoginData{
UserDataBase: model.UserDataBase{
Login: "test@example.com",
},
Password: "TestPassword123!",
},
Name: "Test User",
},
OrganizationName: "Test Organization",
OrganizationTimeZone: "UTC",
}
// Basic validation - all required fields present
assert.NotEmpty(t, request.Account.Login)
assert.NotEmpty(t, request.Account.Password)
assert.NotEmpty(t, request.Account.Name)
assert.NotEmpty(t, request.OrganizationName)
assert.NotEmpty(t, request.OrganizationTimeZone)
})
t.Run("EmailFormats", func(t *testing.T) {
validEmails := []string{
"test@example.com",
"user.name@example.com",
"user+tag@example.org",
"test123@domain.co.uk",
}
for _, email := range validEmails {
t.Run(email, func(t *testing.T) {
request := srequest.Signup{
Account: model.AccountData{
LoginData: model.LoginData{
UserDataBase: model.UserDataBase{
Login: email,
},
},
},
}
assert.Equal(t, email, request.Account.Login)
assert.Contains(t, email, "@")
assert.Contains(t, email, ".")
})
}
})
t.Run("PasswordComplexity", func(t *testing.T) {
passwordTests := []struct {
name string
password string
valid bool
}{
{"Strong", "TestPassword123!", true},
{"WithNumbers", "MyPass123!", true},
{"WithSymbols", "Complex@Pass1", true},
{"TooShort", "Test1!", false},
{"NoNumbers", "TestPassword!", false},
{"NoSymbols", "TestPassword123", false},
{"NoUppercase", "testpassword123!", false},
{"NoLowercase", "TESTPASSWORD123!", false},
}
for _, tt := range passwordTests {
t.Run(tt.name, func(t *testing.T) {
request := srequest.Signup{
Account: model.AccountData{
LoginData: model.LoginData{
Password: tt.password,
},
},
}
// Basic structure validation
assert.Equal(t, tt.password, request.Account.Password)
if tt.valid {
assert.True(t, len(tt.password) >= 8, "Password should be at least 8 characters")
} else {
// For invalid passwords, at least one condition should fail
hasDigit := false
hasUpper := false
hasLower := false
hasSpecial := false
for _, char := range tt.password {
switch {
case char >= '0' && char <= '9':
hasDigit = true
case char >= 'A' && char <= 'Z':
hasUpper = true
case char >= 'a' && char <= 'z':
hasLower = true
case char >= '!' && char <= '/' || char >= ':' && char <= '@':
hasSpecial = true
}
}
// At least one requirement should fail for invalid passwords
if len(tt.password) >= 8 {
assert.False(t, hasDigit && hasUpper && hasLower && hasSpecial,
"Password %s should fail at least one requirement", tt.password)
}
}
})
}
})
}
// TestAccountDataToAccount tests the ToAccount method
func TestAccountDataToAccount(t *testing.T) {
accountData := model.AccountData{
LoginData: model.LoginData{
UserDataBase: model.UserDataBase{
Login: "test@example.com",
},
Password: "TestPassword123!",
},
Name: "Test User",
}
account := accountData.ToAccount()
assert.Equal(t, accountData.Login, account.Login)
assert.Equal(t, accountData.Password, account.Password)
assert.Equal(t, accountData.Name, account.Name)
// Verify the account has proper structure
assert.NotNil(t, account)
assert.IsType(t, &model.Account{}, account)
}
// TestColorValidation tests that colors are properly formatted
func TestColorValidation(t *testing.T) {
validColors := []string{
"#FF0000", // Red
"#00FF00", // Green
"#0000FF", // Blue
"#FFFFFF", // White
"#000000", // Black
"#FF8000", // Orange
}
for _, color := range validColors {
t.Run(color, func(t *testing.T) {
colorPtr := stringPtr(color)
assert.NotNil(t, colorPtr)
assert.Equal(t, color, *colorPtr)
assert.True(t, len(color) == 7, "Color should be 7 characters long")
assert.True(t, color[0] == '#', "Color should start with #")
})
}
}