package main
+
+import "errors"
+
+// Chunk splits a slice into chunks of specified size
+func Chunk(slice []int, size int) ([][]int, error) {
+ if size <= 0 {
+ return nil, errors.New("chunk size must be positive")
+ }
+
+ chunks := [][]int{}
+ for i := 0; i < len(slice); i += size {
+ end := i + size
+ if end > len(slice) {
+ end = len(slice)
+ }
+ chunks = append(chunks, slice[i:end])
+ }
+ return chunks, nil
+}
+
+// Unique returns a slice with duplicate elements removed
+func Unique(slice []int) []int {
+ seen := make(map[int]bool)
+ result := []int{}
+
+ for _, val := range slice {
+ if !seen[val] {
+ seen[val] = true
+ result = append(result, val)
+ }
+ }
+ return result
+}
+
+// Flatten flattens a 2D slice into a 1D slice
+func Flatten(slice [][]int) []int {
+ result := []int{}
+ for _, subSlice := range slice {
+ result = append(result, subSlice...)
+ }
+ return result
+}
+
+// Sum returns the sum of all elements in a slice
+func Sum(slice []int) int {
+ total := 0
+ for _, val := range slice {
+ total += val
+ }
+ return total
+}
+
+// Max returns the maximum value in a slice
+func Max(slice []int) (int, error) {
+ if len(slice) == 0 {
+ return 0, errors.New("slice is empty")
+ }
+
+ max := slice[0]
+ for _, val := range slice[1:] {
+ if val > max {
+ max = val
+ }
+ }
+ return max, nil
+}
+
+// Min returns the minimum value in a slice
+func Min(slice []int) (int, error) {
+ if len(slice) == 0 {
+ return 0, errors.New("slice is empty")
+ }
+
+ min := slice[0]
+ for _, val := range slice[1:] {
+ if val < min {
+ min = val
+ }
+ }
+ return min, nil
+}
+
+// Reverse reverses a slice
+func Reverse(slice []int) []int {
+ result := make([]int, len(slice))
+ for i, val := range slice {
+ result[len(slice)-1-i] = val
+ }
+ return result
+}
+
+// Filter returns elements that satisfy the predicate
+func Filter(slice []int, predicate func(int) bool) []int {
+ result := []int{}
+ for _, val := range slice {
+ if predicate(val) {
+ result = append(result, val)
+ }
+ }
+ return result
+}
+
+// Map applies a function to each element
+func Map(slice []int, fn func(int) int) []int {
+ result := make([]int, len(slice))
+ for i, val := range slice {
+ result[i] = fn(val)
+ }
+ return result
+}
+
package main
+
+import (
+ "errors"
+ "math"
+)
+
+// Clamp restricts a value to be within a specified range
+func Clamp(value, min, max float64) float64 {
+ if value < min {
+ return min
+ }
+ if value > max {
+ return max
+ }
+ return value
+}
+
+// Average calculates the mean of a slice of numbers
+func Average(numbers []float64) float64 {
+ if len(numbers) == 0 {
+ return 0
+ }
+ sum := 0.0
+ for _, num := range numbers {
+ sum += num
+ }
+ return sum / float64(len(numbers))
+}
+
+// Median calculates the median of a slice of numbers
+func Median(numbers []float64) float64 {
+ if len(numbers) == 0 {
+ return 0
+ }
+
+ // Create a copy and sort it
+ sorted := make([]float64, len(numbers))
+ copy(sorted, numbers)
+
+ // Simple bubble sort for demonstration
+ for i := 0; i < len(sorted); i++ {
+ for j := i + 1; j < len(sorted); j++ {
+ if sorted[i] > sorted[j] {
+ sorted[i], sorted[j] = sorted[j], sorted[i]
+ }
+ }
+ }
+
+ mid := len(sorted) / 2
+ if len(sorted)%2 == 0 {
+ return (sorted[mid-1] + sorted[mid]) / 2
+ }
+ return sorted[mid]
+}
+
+// Factorial calculates the factorial of n
+func Factorial(n int) (int, error) {
+ if n < 0 {
+ return 0, errors.New("factorial is not defined for negative numbers")
+ }
+ if n == 0 || n == 1 {
+ return 1, nil
+ }
+ result := 1
+ for i := 2; i <= n; i++ {
+ result *= i
+ }
+ return result, nil
+}
+
+// IsPrime checks if a number is prime
+func IsPrime(n int) bool {
+ if n < 2 {
+ return false
+ }
+ if n == 2 {
+ return true
+ }
+ if n%2 == 0 {
+ return false
+ }
+
+ sqrt := int(math.Sqrt(float64(n)))
+ for i := 3; i <= sqrt; i += 2 {
+ if n%i == 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// GCD calculates the greatest common divisor
+func GCD(a, b int) int {
+ a = abs(a)
+ b = abs(b)
+
+ for b != 0 {
+ a, b = b, a%b
+ }
+ return a
+}
+
+// LCM calculates the least common multiple
+func LCM(a, b int) int {
+ if a == 0 || b == 0 {
+ return 0
+ }
+ return abs(a*b) / GCD(a, b)
+}
+
+// Abs returns the absolute value
+func abs(n int) int {
+ if n < 0 {
+ return -n
+ }
+ return n
+}
+
+// Power calculates base^exponent
+func Power(base, exponent int) int {
+ if exponent == 0 {
+ return 1
+ }
+ if exponent < 0 {
+ return 0 // Integer division would give 0 anyway
+ }
+
+ result := 1
+ for i := 0; i < exponent; i++ {
+ result *= base
+ }
+ return result
+}
+
+// IsEven checks if a number is even
+func IsEven(n int) bool {
+ return n%2 == 0
+}
+
+// IsOdd checks if a number is odd
+func IsOdd(n int) bool {
+ return n%2 != 0
+}
+
+
+
package main
+
+import (
+ "regexp"
+ "strings"
+)
+
+// Capitalize returns a string with the first letter capitalized
+func Capitalize(s string) string {
+ if s == "" {
+ return ""
+ }
+ return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
+}
+
+// Truncate shortens a string to maxLength, adding "..." if truncated
+func Truncate(s string, maxLength int) string {
+ if s == "" {
+ return ""
+ }
+ if len(s) <= maxLength {
+ return s
+ }
+ if maxLength <= 3 {
+ return "..."
+ }
+ return s[:maxLength-3] + "..."
+}
+
+// Slugify converts a string to a URL-friendly slug
+func Slugify(s string) string {
+ s = strings.ToLower(s)
+ s = strings.TrimSpace(s)
+
+ // Remove non-alphanumeric characters except spaces and hyphens
+ reg := regexp.MustCompile(`[^\w\s-]`)
+ s = reg.ReplaceAllString(s, "")
+
+ // Replace spaces and underscores with hyphens
+ reg = regexp.MustCompile(`[\s_-]+`)
+ s = reg.ReplaceAllString(s, "-")
+
+ // Remove leading and trailing hyphens
+ s = strings.Trim(s, "-")
+
+ return s
+}
+
+// IsEmail checks if a string is a valid email format
+func IsEmail(s string) bool {
+ emailRegex := regexp.MustCompile(`^[^\s@]+@[^\s@]+\.[^\s@]+$`)
+ return emailRegex.MatchString(s)
+}
+
+// CountWords returns the number of words in a string
+func CountWords(s string) int {
+ if s == "" {
+ return 0
+ }
+ words := strings.Fields(s)
+ return len(words)
+}
+
+// ReverseString reverses a string
+func ReverseString(s string) string {
+ runes := []rune(s)
+ for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
+ runes[i], runes[j] = runes[j], runes[i]
+ }
+ return string(runes)
+}
+
+// IsPalindrome checks if a string is a palindrome
+func IsPalindrome(s string) bool {
+ // Remove non-alphanumeric and convert to lowercase
+ reg := regexp.MustCompile(`[^a-z0-9]`)
+ cleaned := reg.ReplaceAllString(strings.ToLower(s), "")
+
+ for i := 0; i < len(cleaned)/2; i++ {
+ if cleaned[i] != cleaned[len(cleaned)-1-i] {
+ return false
+ }
+ }
+ return true
+}
+
+// Contains checks if a string contains a substring
+func Contains(s, substr string) bool {
+ return strings.Contains(s, substr)
+}
+
+// Repeat repeats a string n times
+func Repeat(s string, n int) string {
+ if n < 0 {
+ return ""
+ }
+ return strings.Repeat(s, n)
+}
+
+
+
package main
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+// User represents a user in the system
+type User struct {
+ ID string
+ Email string
+ Name string
+ Age int
+ IsActive bool
+ CreatedAt time.Time
+}
+
+// UserService manages user operations
+type UserService struct {
+ users map[string]*User
+ idCounter int
+}
+
+// NewUserService creates a new UserService
+func NewUserService() *UserService {
+ return &UserService{
+ users: make(map[string]*User),
+ idCounter: 1,
+ }
+}
+
+// CreateUser creates a new user
+func (s *UserService) CreateUser(email, name string, age int) (*User, error) {
+ if email == "" || name == "" {
+ return nil, errors.New("email and name are required")
+ }
+ if age < 0 || age > 150 {
+ return nil, errors.New("invalid age")
+ }
+ if !IsEmail(email) {
+ return nil, errors.New("invalid email format")
+ }
+
+ id := fmt.Sprintf("user_%d", s.idCounter)
+ s.idCounter++
+
+ user := &User{
+ ID: id,
+ Email: email,
+ Name: name,
+ Age: age,
+ IsActive: true,
+ CreatedAt: time.Now(),
+ }
+
+ s.users[id] = user
+ return user, nil
+}
+
+// GetUserByID retrieves a user by ID
+func (s *UserService) GetUserByID(id string) (*User, error) {
+ user, exists := s.users[id]
+ if !exists {
+ return nil, errors.New("user not found")
+ }
+ return user, nil
+}
+
+// UpdateUser updates user information
+func (s *UserService) UpdateUser(id string, email, name string, age int, isActive bool) (*User, error) {
+ user, exists := s.users[id]
+ if !exists {
+ return nil, errors.New("user not found")
+ }
+
+ if age < 0 || age > 150 {
+ return nil, errors.New("invalid age")
+ }
+ if email != "" && !IsEmail(email) {
+ return nil, errors.New("invalid email format")
+ }
+
+ if email != "" {
+ user.Email = email
+ }
+ if name != "" {
+ user.Name = name
+ }
+ user.Age = age
+ user.IsActive = isActive
+
+ return user, nil
+}
+
+// DeleteUser removes a user
+func (s *UserService) DeleteUser(id string) error {
+ if _, exists := s.users[id]; !exists {
+ return errors.New("user not found")
+ }
+ delete(s.users, id)
+ return nil
+}
+
+// GetAllUsers returns all users
+func (s *UserService) GetAllUsers() []*User {
+ users := make([]*User, 0, len(s.users))
+ for _, user := range s.users {
+ users = append(users, user)
+ }
+ return users
+}
+
+// GetActiveUsers returns only active users
+func (s *UserService) GetActiveUsers() []*User {
+ users := []*User{}
+ for _, user := range s.users {
+ if user.IsActive {
+ users = append(users, user)
+ }
+ }
+ return users
+}
+
+// GetUsersByAgeRange returns users within an age range
+func (s *UserService) GetUsersByAgeRange(minAge, maxAge int) []*User {
+ users := []*User{}
+ for _, user := range s.users {
+ if user.Age >= minAge && user.Age <= maxAge {
+ users = append(users, user)
+ }
+ }
+ return users
+}
+
+// SearchUsersByName searches users by name
+func (s *UserService) SearchUsersByName(query string) []*User {
+ users := []*User{}
+ for _, user := range s.users {
+ if Contains(user.Name, query) {
+ users = append(users, user)
+ }
+ }
+ return users
+}
+
+// CountUsers returns the total number of users
+func (s *UserService) CountUsers() int {
+ return len(s.users)
+}
+
+
+
+
+
+
diff --git a/coverage.out b/coverage.out
new file mode 100644
index 0000000..19ad35c
--- /dev/null
+++ b/coverage.out
@@ -0,0 +1,164 @@
+mode: count
+coverage-demo/arrayutils.go:6.52,7.15 1 7
+coverage-demo/arrayutils.go:7.15,9.3 1 2
+coverage-demo/arrayutils.go:11.2,12.40 2 5
+coverage-demo/arrayutils.go:12.40,14.23 2 8
+coverage-demo/arrayutils.go:14.23,16.4 1 2
+coverage-demo/arrayutils.go:17.3,17.40 1 8
+coverage-demo/arrayutils.go:19.2,19.20 1 5
+coverage-demo/arrayutils.go:23.32,27.28 3 5
+coverage-demo/arrayutils.go:27.28,28.17 1 18
+coverage-demo/arrayutils.go:28.17,31.4 2 12
+coverage-demo/arrayutils.go:33.2,33.15 1 5
+coverage-demo/arrayutils.go:37.35,39.33 2 5
+coverage-demo/arrayutils.go:39.33,41.3 1 9
+coverage-demo/arrayutils.go:42.2,42.15 1 5
+coverage-demo/arrayutils.go:46.27,48.28 2 5
+coverage-demo/arrayutils.go:48.28,50.3 1 10
+coverage-demo/arrayutils.go:51.2,51.14 1 5
+coverage-demo/arrayutils.go:55.36,56.21 1 5
+coverage-demo/arrayutils.go:56.21,58.3 1 1
+coverage-demo/arrayutils.go:60.2,61.32 2 4
+coverage-demo/arrayutils.go:61.32,62.16 1 8
+coverage-demo/arrayutils.go:62.16,64.4 1 4
+coverage-demo/arrayutils.go:66.2,66.17 1 4
+coverage-demo/arrayutils.go:70.36,71.21 1 5
+coverage-demo/arrayutils.go:71.21,73.3 1 1
+coverage-demo/arrayutils.go:75.2,76.32 2 4
+coverage-demo/arrayutils.go:76.32,77.16 1 8
+coverage-demo/arrayutils.go:77.16,79.4 1 2
+coverage-demo/arrayutils.go:81.2,81.17 1 4
+coverage-demo/arrayutils.go:85.33,87.28 2 4
+coverage-demo/arrayutils.go:87.28,89.3 1 10
+coverage-demo/arrayutils.go:90.2,90.15 1 4
+coverage-demo/arrayutils.go:94.58,96.28 2 4
+coverage-demo/arrayutils.go:96.28,97.21 1 13
+coverage-demo/arrayutils.go:97.21,99.4 1 4
+coverage-demo/arrayutils.go:101.2,101.15 1 4
+coverage-demo/arrayutils.go:105.47,107.28 2 3
+coverage-demo/arrayutils.go:107.28,109.3 1 6
+coverage-demo/arrayutils.go:110.2,110.15 1 3
+coverage-demo/main.go:5.13,11.16 4 0
+coverage-demo/main.go:11.16,14.3 2 0
+coverage-demo/main.go:16.2,16.62 1 0
+coverage-demo/mathutils.go:9.45,10.17 1 5
+coverage-demo/mathutils.go:10.17,12.3 1 1
+coverage-demo/mathutils.go:13.2,13.17 1 4
+coverage-demo/mathutils.go:13.17,15.3 1 1
+coverage-demo/mathutils.go:16.2,16.14 1 3
+coverage-demo/mathutils.go:20.41,21.23 1 5
+coverage-demo/mathutils.go:21.23,23.3 1 1
+coverage-demo/mathutils.go:24.2,25.30 2 4
+coverage-demo/mathutils.go:25.30,27.3 1 9
+coverage-demo/mathutils.go:28.2,28.36 1 4
+coverage-demo/mathutils.go:32.40,33.23 1 6
+coverage-demo/mathutils.go:33.23,35.3 1 1
+coverage-demo/mathutils.go:38.2,42.35 3 5
+coverage-demo/mathutils.go:42.35,43.40 1 15
+coverage-demo/mathutils.go:43.40,44.29 1 18
+coverage-demo/mathutils.go:44.29,46.5 1 6
+coverage-demo/mathutils.go:50.2,51.24 2 5
+coverage-demo/mathutils.go:51.24,53.3 1 2
+coverage-demo/mathutils.go:54.2,54.20 1 3
+coverage-demo/mathutils.go:58.36,59.11 1 6
+coverage-demo/mathutils.go:59.11,61.3 1 1
+coverage-demo/mathutils.go:62.2,62.22 1 5
+coverage-demo/mathutils.go:62.22,64.3 1 2
+coverage-demo/mathutils.go:65.2,66.26 2 3
+coverage-demo/mathutils.go:66.26,68.3 1 15
+coverage-demo/mathutils.go:69.2,69.20 1 3
+coverage-demo/mathutils.go:73.26,74.11 1 11
+coverage-demo/mathutils.go:74.11,76.3 1 3
+coverage-demo/mathutils.go:77.2,77.12 1 8
+coverage-demo/mathutils.go:77.12,79.3 1 1
+coverage-demo/mathutils.go:80.2,80.14 1 7
+coverage-demo/mathutils.go:80.14,82.3 1 3
+coverage-demo/mathutils.go:84.2,85.32 2 4
+coverage-demo/mathutils.go:85.32,86.15 1 5
+coverage-demo/mathutils.go:86.15,88.4 1 0
+coverage-demo/mathutils.go:90.2,90.13 1 4
+coverage-demo/mathutils.go:94.24,98.13 3 12
+coverage-demo/mathutils.go:98.13,100.3 1 26
+coverage-demo/mathutils.go:101.2,101.10 1 12
+coverage-demo/mathutils.go:105.24,106.22 1 6
+coverage-demo/mathutils.go:106.22,108.3 1 2
+coverage-demo/mathutils.go:109.2,109.29 1 4
+coverage-demo/mathutils.go:113.21,114.11 1 32
+coverage-demo/mathutils.go:114.11,116.3 1 4
+coverage-demo/mathutils.go:117.2,117.10 1 28
+coverage-demo/mathutils.go:121.36,122.19 1 7
+coverage-demo/mathutils.go:122.19,124.3 1 1
+coverage-demo/mathutils.go:125.2,125.18 1 6
+coverage-demo/mathutils.go:125.18,127.3 1 1
+coverage-demo/mathutils.go:129.2,130.32 2 5
+coverage-demo/mathutils.go:130.32,132.3 1 14
+coverage-demo/mathutils.go:133.2,133.15 1 5
+coverage-demo/mathutils.go:137.25,139.2 1 7
+coverage-demo/mathutils.go:142.24,144.2 1 7
+coverage-demo/stringutils.go:9.34,10.13 1 5
+coverage-demo/stringutils.go:10.13,12.3 1 1
+coverage-demo/stringutils.go:13.2,13.56 1 4
+coverage-demo/stringutils.go:17.47,18.13 1 7
+coverage-demo/stringutils.go:18.13,20.3 1 1
+coverage-demo/stringutils.go:21.2,21.25 1 6
+coverage-demo/stringutils.go:21.25,23.3 1 3
+coverage-demo/stringutils.go:24.2,24.20 1 3
+coverage-demo/stringutils.go:24.20,26.3 1 2
+coverage-demo/stringutils.go:27.2,27.32 1 1
+coverage-demo/stringutils.go:31.31,47.2 8 8
+coverage-demo/stringutils.go:50.29,53.2 2 35
+coverage-demo/stringutils.go:56.31,57.13 1 6
+coverage-demo/stringutils.go:57.13,59.3 1 1
+coverage-demo/stringutils.go:60.2,61.19 2 5
+coverage-demo/stringutils.go:65.37,67.54 2 5
+coverage-demo/stringutils.go:67.54,69.3 1 7
+coverage-demo/stringutils.go:70.2,70.22 1 5
+coverage-demo/stringutils.go:74.34,79.38 3 9
+coverage-demo/stringutils.go:79.38,80.46 1 21
+coverage-demo/stringutils.go:80.46,82.4 1 3
+coverage-demo/stringutils.go:84.2,84.13 1 6
+coverage-demo/stringutils.go:88.38,90.2 1 21
+coverage-demo/stringutils.go:93.37,94.11 1 5
+coverage-demo/stringutils.go:94.11,96.3 1 1
+coverage-demo/stringutils.go:97.2,97.29 1 4
+coverage-demo/userservice.go:26.36,31.2 1 10
+coverage-demo/userservice.go:34.78,35.31 1 28
+coverage-demo/userservice.go:35.31,37.3 1 2
+coverage-demo/userservice.go:38.2,38.26 1 26
+coverage-demo/userservice.go:38.26,40.3 1 2
+coverage-demo/userservice.go:41.2,41.21 1 24
+coverage-demo/userservice.go:41.21,43.3 1 1
+coverage-demo/userservice.go:45.2,58.18 5 23
+coverage-demo/userservice.go:62.61,64.13 2 3
+coverage-demo/userservice.go:64.13,66.3 1 2
+coverage-demo/userservice.go:67.2,67.18 1 1
+coverage-demo/userservice.go:71.104,73.13 2 9
+coverage-demo/userservice.go:73.13,75.3 1 1
+coverage-demo/userservice.go:77.2,77.26 1 8
+coverage-demo/userservice.go:77.26,79.3 1 2
+coverage-demo/userservice.go:80.2,80.36 1 6
+coverage-demo/userservice.go:80.36,82.3 1 1
+coverage-demo/userservice.go:84.2,84.17 1 5
+coverage-demo/userservice.go:84.17,86.3 1 2
+coverage-demo/userservice.go:87.2,87.16 1 5
+coverage-demo/userservice.go:87.16,89.3 1 2
+coverage-demo/userservice.go:90.2,93.18 3 5
+coverage-demo/userservice.go:97.51,98.39 1 3
+coverage-demo/userservice.go:98.39,100.3 1 1
+coverage-demo/userservice.go:101.2,102.12 2 2
+coverage-demo/userservice.go:106.45,108.31 2 3
+coverage-demo/userservice.go:108.31,110.3 1 6
+coverage-demo/userservice.go:111.2,111.14 1 3
+coverage-demo/userservice.go:115.48,117.31 2 2
+coverage-demo/userservice.go:117.31,118.20 1 6
+coverage-demo/userservice.go:118.20,120.4 1 3
+coverage-demo/userservice.go:122.2,122.14 1 2
+coverage-demo/userservice.go:126.70,128.31 2 4
+coverage-demo/userservice.go:128.31,129.47 1 16
+coverage-demo/userservice.go:129.47,131.4 1 8
+coverage-demo/userservice.go:133.2,133.14 1 4
+coverage-demo/userservice.go:137.63,139.31 2 4
+coverage-demo/userservice.go:139.31,140.33 1 16
+coverage-demo/userservice.go:140.33,142.4 1 9
+coverage-demo/userservice.go:144.2,144.14 1 4
+coverage-demo/userservice.go:148.40,150.2 1 4
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..833705b
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module coverage-demo
+
+go 1.25.3
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..884d9da
--- /dev/null
+++ b/main.go
@@ -0,0 +1,17 @@
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Coverage Demo Application")
+
+ // Example usage
+ userService := NewUserService()
+ user, err := userService.CreateUser("test@example.com", "Test User", 25)
+ if err != nil {
+ fmt.Printf("Error: %v\n", err)
+ return
+ }
+
+ fmt.Printf("Created user: %s (%s)\n", user.Name, user.Email)
+}
diff --git a/mathutils.go b/mathutils.go
new file mode 100644
index 0000000..0c269bf
--- /dev/null
+++ b/mathutils.go
@@ -0,0 +1,144 @@
+package main
+
+import (
+ "errors"
+ "math"
+)
+
+// Clamp restricts a value to be within a specified range
+func Clamp(value, min, max float64) float64 {
+ if value < min {
+ return min
+ }
+ if value > max {
+ return max
+ }
+ return value
+}
+
+// Average calculates the mean of a slice of numbers
+func Average(numbers []float64) float64 {
+ if len(numbers) == 0 {
+ return 0
+ }
+ sum := 0.0
+ for _, num := range numbers {
+ sum += num
+ }
+ return sum / float64(len(numbers))
+}
+
+// Median calculates the median of a slice of numbers
+func Median(numbers []float64) float64 {
+ if len(numbers) == 0 {
+ return 0
+ }
+
+ // Create a copy and sort it
+ sorted := make([]float64, len(numbers))
+ copy(sorted, numbers)
+
+ // Simple bubble sort for demonstration
+ for i := 0; i < len(sorted); i++ {
+ for j := i + 1; j < len(sorted); j++ {
+ if sorted[i] > sorted[j] {
+ sorted[i], sorted[j] = sorted[j], sorted[i]
+ }
+ }
+ }
+
+ mid := len(sorted) / 2
+ if len(sorted)%2 == 0 {
+ return (sorted[mid-1] + sorted[mid]) / 2
+ }
+ return sorted[mid]
+}
+
+// Factorial calculates the factorial of n
+func Factorial(n int) (int, error) {
+ if n < 0 {
+ return 0, errors.New("factorial is not defined for negative numbers")
+ }
+ if n == 0 || n == 1 {
+ return 1, nil
+ }
+ result := 1
+ for i := 2; i <= n; i++ {
+ result *= i
+ }
+ return result, nil
+}
+
+// IsPrime checks if a number is prime
+func IsPrime(n int) bool {
+ if n < 2 {
+ return false
+ }
+ if n == 2 {
+ return true
+ }
+ if n%2 == 0 {
+ return false
+ }
+
+ sqrt := int(math.Sqrt(float64(n)))
+ for i := 3; i <= sqrt; i += 2 {
+ if n%i == 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// GCD calculates the greatest common divisor
+func GCD(a, b int) int {
+ a = abs(a)
+ b = abs(b)
+
+ for b != 0 {
+ a, b = b, a%b
+ }
+ return a
+}
+
+// LCM calculates the least common multiple
+func LCM(a, b int) int {
+ if a == 0 || b == 0 {
+ return 0
+ }
+ return abs(a*b) / GCD(a, b)
+}
+
+// Abs returns the absolute value
+func abs(n int) int {
+ if n < 0 {
+ return -n
+ }
+ return n
+}
+
+// Power calculates base^exponent
+func Power(base, exponent int) int {
+ if exponent == 0 {
+ return 1
+ }
+ if exponent < 0 {
+ return 0 // Integer division would give 0 anyway
+ }
+
+ result := 1
+ for i := 0; i < exponent; i++ {
+ result *= base
+ }
+ return result
+}
+
+// IsEven checks if a number is even
+func IsEven(n int) bool {
+ return n%2 == 0
+}
+
+// IsOdd checks if a number is odd
+func IsOdd(n int) bool {
+ return n%2 != 0
+}
diff --git a/mathutils_test.go b/mathutils_test.go
new file mode 100644
index 0000000..6542d9b
--- /dev/null
+++ b/mathutils_test.go
@@ -0,0 +1,263 @@
+package main
+
+import (
+ "math"
+ "testing"
+)
+
+func TestClamp(t *testing.T) {
+ tests := []struct {
+ value float64
+ min float64
+ max float64
+ expected float64
+ }{
+ {5.0, 0.0, 10.0, 5.0},
+ {-5.0, 0.0, 10.0, 0.0},
+ {15.0, 0.0, 10.0, 10.0},
+ {5.0, 5.0, 5.0, 5.0},
+ {3.5, 2.0, 8.0, 3.5},
+ }
+
+ for _, tt := range tests {
+ result := Clamp(tt.value, tt.min, tt.max)
+ if result != tt.expected {
+ t.Errorf("Clamp(%f, %f, %f) = %f, want %f", tt.value, tt.min, tt.max, result, tt.expected)
+ }
+ }
+}
+
+func TestAverage(t *testing.T) {
+ tests := []struct {
+ input []float64
+ expected float64
+ }{
+ {[]float64{1.0, 2.0, 3.0}, 2.0},
+ {[]float64{5.0}, 5.0},
+ {[]float64{}, 0.0},
+ {[]float64{10.0, 20.0, 30.0}, 20.0},
+ {[]float64{-5.0, 5.0}, 0.0},
+ }
+
+ for _, tt := range tests {
+ result := Average(tt.input)
+ if result != tt.expected {
+ t.Errorf("Average(%v) = %f, want %f", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestMedian(t *testing.T) {
+ tests := []struct {
+ input []float64
+ expected float64
+ }{
+ {[]float64{1.0, 2.0, 3.0}, 2.0},
+ {[]float64{1.0, 2.0, 3.0, 4.0}, 2.5},
+ {[]float64{5.0}, 5.0},
+ {[]float64{}, 0.0},
+ {[]float64{3.0, 1.0, 2.0}, 2.0},
+ {[]float64{4.0, 1.0, 3.0, 2.0}, 2.5},
+ }
+
+ for _, tt := range tests {
+ result := Median(tt.input)
+ if result != tt.expected {
+ t.Errorf("Median(%v) = %f, want %f", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestFactorial(t *testing.T) {
+ tests := []struct {
+ input int
+ expected int
+ expectError bool
+ }{
+ {0, 1, false},
+ {1, 1, false},
+ {5, 120, false},
+ {10, 3628800, false},
+ {-1, 0, true},
+ {3, 6, false},
+ }
+
+ for _, tt := range tests {
+ result, err := Factorial(tt.input)
+ if tt.expectError {
+ if err == nil {
+ t.Errorf("Factorial(%d) expected error, got nil", tt.input)
+ }
+ } else {
+ if err != nil {
+ t.Errorf("Factorial(%d) unexpected error: %v", tt.input, err)
+ }
+ if result != tt.expected {
+ t.Errorf("Factorial(%d) = %d, want %d", tt.input, result, tt.expected)
+ }
+ }
+ }
+}
+
+func TestIsPrime(t *testing.T) {
+ tests := []struct {
+ input int
+ expected bool
+ }{
+ {2, true},
+ {3, true},
+ {4, false},
+ {5, true},
+ {17, true},
+ {20, false},
+ {1, false},
+ {0, false},
+ {-5, false},
+ {97, true},
+ {100, false},
+ }
+
+ for _, tt := range tests {
+ result := IsPrime(tt.input)
+ if result != tt.expected {
+ t.Errorf("IsPrime(%d) = %v, want %v", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestGCD(t *testing.T) {
+ tests := []struct {
+ a int
+ b int
+ expected int
+ }{
+ {12, 8, 4},
+ {15, 25, 5},
+ {7, 13, 1},
+ {0, 5, 5},
+ {5, 0, 5},
+ {-12, 8, 4},
+ {12, -8, 4},
+ {100, 50, 50},
+ }
+
+ for _, tt := range tests {
+ result := GCD(tt.a, tt.b)
+ if result != tt.expected {
+ t.Errorf("GCD(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected)
+ }
+ }
+}
+
+func TestLCM(t *testing.T) {
+ tests := []struct {
+ a int
+ b int
+ expected int
+ }{
+ {4, 6, 12},
+ {3, 5, 15},
+ {12, 8, 24},
+ {0, 5, 0},
+ {5, 0, 0},
+ {7, 7, 7},
+ }
+
+ for _, tt := range tests {
+ result := LCM(tt.a, tt.b)
+ if result != tt.expected {
+ t.Errorf("LCM(%d, %d) = %d, want %d", tt.a, tt.b, result, tt.expected)
+ }
+ }
+}
+
+func TestAbs(t *testing.T) {
+ tests := []struct {
+ input int
+ expected int
+ }{
+ {5, 5},
+ {-5, 5},
+ {0, 0},
+ {-100, 100},
+ }
+
+ for _, tt := range tests {
+ result := abs(tt.input)
+ if result != tt.expected {
+ t.Errorf("abs(%d) = %d, want %d", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestPower(t *testing.T) {
+ tests := []struct {
+ base int
+ exponent int
+ expected int
+ }{
+ {2, 3, 8},
+ {5, 0, 1},
+ {3, 2, 9},
+ {10, 3, 1000},
+ {2, -1, 0},
+ {0, 5, 0},
+ {7, 1, 7},
+ }
+
+ for _, tt := range tests {
+ result := Power(tt.base, tt.exponent)
+ if result != tt.expected {
+ t.Errorf("Power(%d, %d) = %d, want %d", tt.base, tt.exponent, result, tt.expected)
+ }
+ }
+}
+
+func TestIsEven(t *testing.T) {
+ tests := []struct {
+ input int
+ expected bool
+ }{
+ {2, true},
+ {3, false},
+ {0, true},
+ {-2, true},
+ {-3, false},
+ {100, true},
+ {101, false},
+ }
+
+ for _, tt := range tests {
+ result := IsEven(tt.input)
+ if result != tt.expected {
+ t.Errorf("IsEven(%d) = %v, want %v", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestIsOdd(t *testing.T) {
+ tests := []struct {
+ input int
+ expected bool
+ }{
+ {2, false},
+ {3, true},
+ {0, false},
+ {-2, false},
+ {-3, true},
+ {100, false},
+ {101, true},
+ }
+
+ for _, tt := range tests {
+ result := IsOdd(tt.input)
+ if result != tt.expected {
+ t.Errorf("IsOdd(%d) = %v, want %v", tt.input, result, tt.expected)
+ }
+ }
+}
+
+// Helper function to compare floats with tolerance
+func floatEquals(a, b, tolerance float64) bool {
+ return math.Abs(a-b) < tolerance
+}
diff --git a/stringutils.go b/stringutils.go
new file mode 100644
index 0000000..76a352b
--- /dev/null
+++ b/stringutils.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+ "regexp"
+ "strings"
+)
+
+// Capitalize returns a string with the first letter capitalized
+func Capitalize(s string) string {
+ if s == "" {
+ return ""
+ }
+ return strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
+}
+
+// Truncate shortens a string to maxLength, adding "..." if truncated
+func Truncate(s string, maxLength int) string {
+ if s == "" {
+ return ""
+ }
+ if len(s) <= maxLength {
+ return s
+ }
+ if maxLength <= 3 {
+ return "..."
+ }
+ return s[:maxLength-3] + "..."
+}
+
+// Slugify converts a string to a URL-friendly slug
+func Slugify(s string) string {
+ s = strings.ToLower(s)
+ s = strings.TrimSpace(s)
+
+ // Remove non-alphanumeric characters except spaces and hyphens
+ reg := regexp.MustCompile(`[^\w\s-]`)
+ s = reg.ReplaceAllString(s, "")
+
+ // Replace spaces and underscores with hyphens
+ reg = regexp.MustCompile(`[\s_-]+`)
+ s = reg.ReplaceAllString(s, "-")
+
+ // Remove leading and trailing hyphens
+ s = strings.Trim(s, "-")
+
+ return s
+}
+
+// IsEmail checks if a string is a valid email format
+func IsEmail(s string) bool {
+ emailRegex := regexp.MustCompile(`^[^\s@]+@[^\s@]+\.[^\s@]+$`)
+ return emailRegex.MatchString(s)
+}
+
+// CountWords returns the number of words in a string
+func CountWords(s string) int {
+ if s == "" {
+ return 0
+ }
+ words := strings.Fields(s)
+ return len(words)
+}
+
+// ReverseString reverses a string
+func ReverseString(s string) string {
+ runes := []rune(s)
+ for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
+ runes[i], runes[j] = runes[j], runes[i]
+ }
+ return string(runes)
+}
+
+// IsPalindrome checks if a string is a palindrome
+func IsPalindrome(s string) bool {
+ // Remove non-alphanumeric and convert to lowercase
+ reg := regexp.MustCompile(`[^a-z0-9]`)
+ cleaned := reg.ReplaceAllString(strings.ToLower(s), "")
+
+ for i := 0; i < len(cleaned)/2; i++ {
+ if cleaned[i] != cleaned[len(cleaned)-1-i] {
+ return false
+ }
+ }
+ return true
+}
+
+// Contains checks if a string contains a substring
+func Contains(s, substr string) bool {
+ return strings.Contains(s, substr)
+}
+
+// Repeat repeats a string n times
+func Repeat(s string, n int) string {
+ if n < 0 {
+ return ""
+ }
+ return strings.Repeat(s, n)
+}
diff --git a/stringutils_test.go b/stringutils_test.go
new file mode 100644
index 0000000..90034e2
--- /dev/null
+++ b/stringutils_test.go
@@ -0,0 +1,199 @@
+package main
+
+import "testing"
+
+func TestCapitalize(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"hello", "Hello"},
+ {"HELLO", "Hello"},
+ {"hELLO", "Hello"},
+ {"", ""},
+ {"a", "A"},
+ }
+
+ for _, tt := range tests {
+ result := Capitalize(tt.input)
+ if result != tt.expected {
+ t.Errorf("Capitalize(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestTruncate(t *testing.T) {
+ tests := []struct {
+ input string
+ maxLength int
+ expected string
+ }{
+ {"hello world", 8, "hello..."},
+ {"hello", 10, "hello"},
+ {"hello", 5, "hello"},
+ {"", 5, ""},
+ {"hello world", 3, "..."},
+ {"test", 2, "..."},
+ {"a", 1, "a"},
+ }
+
+ for _, tt := range tests {
+ result := Truncate(tt.input, tt.maxLength)
+ if result != tt.expected {
+ t.Errorf("Truncate(%q, %d) = %q, want %q", tt.input, tt.maxLength, result, tt.expected)
+ }
+ }
+}
+
+func TestSlugify(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"Hello World", "hello-world"},
+ {"Hello World", "hello-world"},
+ {"Hello_World", "hello-world"},
+ {"Hello-World", "hello-world"},
+ {"Hello@World!", "helloworld"},
+ {" Hello World ", "hello-world"},
+ {"---test---", "test"},
+ {"", ""},
+ }
+
+ for _, tt := range tests {
+ result := Slugify(tt.input)
+ if result != tt.expected {
+ t.Errorf("Slugify(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestIsEmail(t *testing.T) {
+ tests := []struct {
+ input string
+ expected bool
+ }{
+ {"test@example.com", true},
+ {"user@domain.co.uk", true},
+ {"invalid", false},
+ {"@example.com", false},
+ {"test@", false},
+ {"test@.com", false},
+ {"", false},
+ {"test @example.com", false},
+ }
+
+ for _, tt := range tests {
+ result := IsEmail(tt.input)
+ if result != tt.expected {
+ t.Errorf("IsEmail(%q) = %v, want %v", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestCountWords(t *testing.T) {
+ tests := []struct {
+ input string
+ expected int
+ }{
+ {"hello world", 2},
+ {"one", 1},
+ {"", 0},
+ {" ", 0},
+ {"one two three", 3},
+ {" hello world ", 2},
+ }
+
+ for _, tt := range tests {
+ result := CountWords(tt.input)
+ if result != tt.expected {
+ t.Errorf("CountWords(%q) = %d, want %d", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestReverseString(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"hello", "olleh"},
+ {"", ""},
+ {"a", "a"},
+ {"12345", "54321"},
+ {"racecar", "racecar"},
+ }
+
+ for _, tt := range tests {
+ result := ReverseString(tt.input)
+ if result != tt.expected {
+ t.Errorf("ReverseString(%q) = %q, want %q", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestIsPalindrome(t *testing.T) {
+ tests := []struct {
+ input string
+ expected bool
+ }{
+ {"racecar", true},
+ {"hello", false},
+ {"A man a plan a canal Panama", true},
+ {"", true},
+ {"a", true},
+ {"Madam", true},
+ {"test", false},
+ {"12321", true},
+ {"12345", false},
+ }
+
+ for _, tt := range tests {
+ result := IsPalindrome(tt.input)
+ if result != tt.expected {
+ t.Errorf("IsPalindrome(%q) = %v, want %v", tt.input, result, tt.expected)
+ }
+ }
+}
+
+func TestContains(t *testing.T) {
+ tests := []struct {
+ str string
+ substr string
+ expected bool
+ }{
+ {"hello world", "world", true},
+ {"hello world", "test", false},
+ {"", "", true},
+ {"test", "", true},
+ {"", "test", false},
+ }
+
+ for _, tt := range tests {
+ result := Contains(tt.str, tt.substr)
+ if result != tt.expected {
+ t.Errorf("Contains(%q, %q) = %v, want %v", tt.str, tt.substr, result, tt.expected)
+ }
+ }
+}
+
+func TestRepeat(t *testing.T) {
+ tests := []struct {
+ str string
+ n int
+ expected string
+ }{
+ {"a", 3, "aaa"},
+ {"ab", 2, "abab"},
+ {"test", 0, ""},
+ {"x", -1, ""},
+ {"", 5, ""},
+ }
+
+ for _, tt := range tests {
+ result := Repeat(tt.str, tt.n)
+ if result != tt.expected {
+ t.Errorf("Repeat(%q, %d) = %q, want %q", tt.str, tt.n, result, tt.expected)
+ }
+ }
+}
diff --git a/userservice.go b/userservice.go
new file mode 100644
index 0000000..f6551d3
--- /dev/null
+++ b/userservice.go
@@ -0,0 +1,150 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+// User represents a user in the system
+type User struct {
+ ID string
+ Email string
+ Name string
+ Age int
+ IsActive bool
+ CreatedAt time.Time
+}
+
+// UserService manages user operations
+type UserService struct {
+ users map[string]*User
+ idCounter int
+}
+
+// NewUserService creates a new UserService
+func NewUserService() *UserService {
+ return &UserService{
+ users: make(map[string]*User),
+ idCounter: 1,
+ }
+}
+
+// CreateUser creates a new user
+func (s *UserService) CreateUser(email, name string, age int) (*User, error) {
+ if email == "" || name == "" {
+ return nil, errors.New("email and name are required")
+ }
+ if age < 0 || age > 150 {
+ return nil, errors.New("invalid age")
+ }
+ if !IsEmail(email) {
+ return nil, errors.New("invalid email format")
+ }
+
+ id := fmt.Sprintf("user_%d", s.idCounter)
+ s.idCounter++
+
+ user := &User{
+ ID: id,
+ Email: email,
+ Name: name,
+ Age: age,
+ IsActive: true,
+ CreatedAt: time.Now(),
+ }
+
+ s.users[id] = user
+ return user, nil
+}
+
+// GetUserByID retrieves a user by ID
+func (s *UserService) GetUserByID(id string) (*User, error) {
+ user, exists := s.users[id]
+ if !exists {
+ return nil, errors.New("user not found")
+ }
+ return user, nil
+}
+
+// UpdateUser updates user information
+func (s *UserService) UpdateUser(id string, email, name string, age int, isActive bool) (*User, error) {
+ user, exists := s.users[id]
+ if !exists {
+ return nil, errors.New("user not found")
+ }
+
+ if age < 0 || age > 150 {
+ return nil, errors.New("invalid age")
+ }
+ if email != "" && !IsEmail(email) {
+ return nil, errors.New("invalid email format")
+ }
+
+ if email != "" {
+ user.Email = email
+ }
+ if name != "" {
+ user.Name = name
+ }
+ user.Age = age
+ user.IsActive = isActive
+
+ return user, nil
+}
+
+// DeleteUser removes a user
+func (s *UserService) DeleteUser(id string) error {
+ if _, exists := s.users[id]; !exists {
+ return errors.New("user not found")
+ }
+ delete(s.users, id)
+ return nil
+}
+
+// GetAllUsers returns all users
+func (s *UserService) GetAllUsers() []*User {
+ users := make([]*User, 0, len(s.users))
+ for _, user := range s.users {
+ users = append(users, user)
+ }
+ return users
+}
+
+// GetActiveUsers returns only active users
+func (s *UserService) GetActiveUsers() []*User {
+ users := []*User{}
+ for _, user := range s.users {
+ if user.IsActive {
+ users = append(users, user)
+ }
+ }
+ return users
+}
+
+// GetUsersByAgeRange returns users within an age range
+func (s *UserService) GetUsersByAgeRange(minAge, maxAge int) []*User {
+ users := []*User{}
+ for _, user := range s.users {
+ if user.Age >= minAge && user.Age <= maxAge {
+ users = append(users, user)
+ }
+ }
+ return users
+}
+
+// SearchUsersByName searches users by name
+func (s *UserService) SearchUsersByName(query string) []*User {
+ users := []*User{}
+ for _, user := range s.users {
+ if Contains(user.Name, query) {
+ users = append(users, user)
+ }
+ }
+ return users
+}
+
+// CountUsers returns the total number of users
+func (s *UserService) CountUsers() int {
+ return len(s.users)
+}
diff --git a/userservice_test.go b/userservice_test.go
new file mode 100644
index 0000000..1511700
--- /dev/null
+++ b/userservice_test.go
@@ -0,0 +1,333 @@
+package main
+
+import (
+ "testing"
+)
+
+func TestNewUserService(t *testing.T) {
+ service := NewUserService()
+ if service == nil {
+ t.Error("NewUserService() returned nil")
+ }
+ if service.users == nil {
+ t.Error("users map not initialized")
+ }
+}
+
+func TestCreateUser(t *testing.T) {
+ service := NewUserService()
+
+ // Valid user creation
+ user, err := service.CreateUser("test@example.com", "Test User", 25)
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if user.Email != "test@example.com" {
+ t.Errorf("Expected email 'test@example.com', got '%s'", user.Email)
+ }
+ if user.Name != "Test User" {
+ t.Errorf("Expected name 'Test User', got '%s'", user.Name)
+ }
+ if user.Age != 25 {
+ t.Errorf("Expected age 25, got %d", user.Age)
+ }
+ if !user.IsActive {
+ t.Error("Expected user to be active")
+ }
+
+ // Test empty email
+ _, err = service.CreateUser("", "Test", 25)
+ if err == nil {
+ t.Error("Expected error for empty email")
+ }
+
+ // Test empty name
+ _, err = service.CreateUser("test@example.com", "", 25)
+ if err == nil {
+ t.Error("Expected error for empty name")
+ }
+
+ // Test invalid age (negative)
+ _, err = service.CreateUser("test@example.com", "Test", -1)
+ if err == nil {
+ t.Error("Expected error for negative age")
+ }
+
+ // Test invalid age (too high)
+ _, err = service.CreateUser("test@example.com", "Test", 151)
+ if err == nil {
+ t.Error("Expected error for age > 150")
+ }
+
+ // Test invalid email format
+ _, err = service.CreateUser("invalid-email", "Test", 25)
+ if err == nil {
+ t.Error("Expected error for invalid email format")
+ }
+
+ // Test boundary ages
+ _, err = service.CreateUser("test@example.com", "Test", 0)
+ if err != nil {
+ t.Errorf("Age 0 should be valid: %v", err)
+ }
+
+ _, err = service.CreateUser("test2@example.com", "Test", 150)
+ if err != nil {
+ t.Errorf("Age 150 should be valid: %v", err)
+ }
+}
+
+func TestGetUserByID(t *testing.T) {
+ service := NewUserService()
+ user, _ := service.CreateUser("test@example.com", "Test User", 25)
+
+ // Valid get
+ found, err := service.GetUserByID(user.ID)
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if found.ID != user.ID {
+ t.Errorf("Expected user ID '%s', got '%s'", user.ID, found.ID)
+ }
+
+ // Non-existent user
+ _, err = service.GetUserByID("nonexistent")
+ if err == nil {
+ t.Error("Expected error for non-existent user")
+ }
+}
+
+func TestUpdateUser(t *testing.T) {
+ service := NewUserService()
+ user, _ := service.CreateUser("test@example.com", "Test User", 25)
+
+ // Valid update
+ updated, err := service.UpdateUser(user.ID, "new@example.com", "New Name", 30, false)
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if updated.Email != "new@example.com" {
+ t.Errorf("Expected email 'new@example.com', got '%s'", updated.Email)
+ }
+ if updated.Name != "New Name" {
+ t.Errorf("Expected name 'New Name', got '%s'", updated.Name)
+ }
+ if updated.Age != 30 {
+ t.Errorf("Expected age 30, got %d", updated.Age)
+ }
+ if updated.IsActive {
+ t.Error("Expected user to be inactive")
+ }
+
+ // Update non-existent user
+ _, err = service.UpdateUser("nonexistent", "test@example.com", "Test", 25, true)
+ if err == nil {
+ t.Error("Expected error for non-existent user")
+ }
+
+ // Invalid age
+ _, err = service.UpdateUser(user.ID, "test@example.com", "Test", -1, true)
+ if err == nil {
+ t.Error("Expected error for negative age")
+ }
+
+ _, err = service.UpdateUser(user.ID, "test@example.com", "Test", 151, true)
+ if err == nil {
+ t.Error("Expected error for age > 150")
+ }
+
+ // Invalid email
+ _, err = service.UpdateUser(user.ID, "invalid-email", "Test", 25, true)
+ if err == nil {
+ t.Error("Expected error for invalid email")
+ }
+
+ // Empty email should keep existing
+ updated, err = service.UpdateUser(user.ID, "", "Another Name", 35, true)
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if updated.Email != "new@example.com" {
+ t.Error("Email should not change when empty string provided")
+ }
+
+ // Empty name should keep existing
+ updated, err = service.UpdateUser(user.ID, "test3@example.com", "", 40, true)
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+ if updated.Name != "Another Name" {
+ t.Error("Name should not change when empty string provided")
+ }
+}
+
+func TestDeleteUser(t *testing.T) {
+ service := NewUserService()
+ user, _ := service.CreateUser("test@example.com", "Test User", 25)
+
+ // Valid delete
+ err := service.DeleteUser(user.ID)
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ }
+
+ // Verify user is deleted
+ _, err = service.GetUserByID(user.ID)
+ if err == nil {
+ t.Error("User should be deleted")
+ }
+
+ // Delete non-existent user
+ err = service.DeleteUser("nonexistent")
+ if err == nil {
+ t.Error("Expected error for non-existent user")
+ }
+}
+
+func TestGetAllUsers(t *testing.T) {
+ service := NewUserService()
+
+ // Empty service
+ users := service.GetAllUsers()
+ if len(users) != 0 {
+ t.Errorf("Expected 0 users, got %d", len(users))
+ }
+
+ // Add users
+ service.CreateUser("test1@example.com", "User 1", 25)
+ service.CreateUser("test2@example.com", "User 2", 30)
+ service.CreateUser("test3@example.com", "User 3", 35)
+
+ users = service.GetAllUsers()
+ if len(users) != 3 {
+ t.Errorf("Expected 3 users, got %d", len(users))
+ }
+}
+
+func TestGetActiveUsers(t *testing.T) {
+ service := NewUserService()
+
+ user1, _ := service.CreateUser("test1@example.com", "User 1", 25)
+ user2, _ := service.CreateUser("test2@example.com", "User 2", 30)
+ service.CreateUser("test3@example.com", "User 3", 35)
+
+ // Deactivate one user
+ service.UpdateUser(user2.ID, "", "", 30, false)
+
+ activeUsers := service.GetActiveUsers()
+ if len(activeUsers) != 2 {
+ t.Errorf("Expected 2 active users, got %d", len(activeUsers))
+ }
+
+ // Verify the inactive user is not in the list
+ for _, user := range activeUsers {
+ if user.ID == user2.ID {
+ t.Error("Inactive user should not be in active users list")
+ }
+ }
+
+ // Deactivate another
+ service.UpdateUser(user1.ID, "", "", 25, false)
+ activeUsers = service.GetActiveUsers()
+ if len(activeUsers) != 1 {
+ t.Errorf("Expected 1 active user, got %d", len(activeUsers))
+ }
+}
+
+func TestGetUsersByAgeRange(t *testing.T) {
+ service := NewUserService()
+
+ service.CreateUser("test1@example.com", "User 1", 20)
+ service.CreateUser("test2@example.com", "User 2", 30)
+ service.CreateUser("test3@example.com", "User 3", 40)
+ service.CreateUser("test4@example.com", "User 4", 50)
+
+ // Test range
+ users := service.GetUsersByAgeRange(25, 45)
+ if len(users) != 2 {
+ t.Errorf("Expected 2 users in range 25-45, got %d", len(users))
+ }
+
+ // Test exact boundaries
+ users = service.GetUsersByAgeRange(30, 40)
+ if len(users) != 2 {
+ t.Errorf("Expected 2 users in range 30-40, got %d", len(users))
+ }
+
+ // Test no matches
+ users = service.GetUsersByAgeRange(60, 70)
+ if len(users) != 0 {
+ t.Errorf("Expected 0 users in range 60-70, got %d", len(users))
+ }
+
+ // Test all users
+ users = service.GetUsersByAgeRange(0, 150)
+ if len(users) != 4 {
+ t.Errorf("Expected 4 users in range 0-150, got %d", len(users))
+ }
+}
+
+func TestSearchUsersByName(t *testing.T) {
+ service := NewUserService()
+
+ service.CreateUser("test1@example.com", "John Doe", 25)
+ service.CreateUser("test2@example.com", "Jane Smith", 30)
+ service.CreateUser("test3@example.com", "John Smith", 35)
+ service.CreateUser("test4@example.com", "Bob Johnson", 40)
+
+ // Search for "John"
+ users := service.SearchUsersByName("John")
+ if len(users) != 3 {
+ t.Errorf("Expected 3 users with 'John', got %d", len(users))
+ }
+
+ // Search for "Smith"
+ users = service.SearchUsersByName("Smith")
+ if len(users) != 2 {
+ t.Errorf("Expected 2 users with 'Smith', got %d", len(users))
+ }
+
+ // Search for non-existent
+ users = service.SearchUsersByName("Nonexistent")
+ if len(users) != 0 {
+ t.Errorf("Expected 0 users with 'Nonexistent', got %d", len(users))
+ }
+
+ // Empty search
+ users = service.SearchUsersByName("")
+ if len(users) != 4 {
+ t.Errorf("Expected 4 users with empty search, got %d", len(users))
+ }
+}
+
+func TestCountUsers(t *testing.T) {
+ service := NewUserService()
+
+ // Empty service
+ count := service.CountUsers()
+ if count != 0 {
+ t.Errorf("Expected 0 users, got %d", count)
+ }
+
+ // Add users
+ service.CreateUser("test1@example.com", "User 1", 25)
+ count = service.CountUsers()
+ if count != 1 {
+ t.Errorf("Expected 1 user, got %d", count)
+ }
+
+ service.CreateUser("test2@example.com", "User 2", 30)
+ service.CreateUser("test3@example.com", "User 3", 35)
+ count = service.CountUsers()
+ if count != 3 {
+ t.Errorf("Expected 3 users, got %d", count)
+ }
+
+ // Delete a user
+ users := service.GetAllUsers()
+ service.DeleteUser(users[0].ID)
+ count = service.CountUsers()
+ if count != 2 {
+ t.Errorf("Expected 2 users after deletion, got %d", count)
+ }
+}