- Replaced the jaevor/go-nanoid library with the updated matoous/go-nanoid/v2 for generating unique IDs. - Implemented user retrieval methods in the user provider, including GetUsers, PaginateUsers, and CountUsers, enhancing user management capabilities. - Improved error handling and ensured default select fields are set for user queries, streamlining the user data access process.
374 lines
10 KiB
Go
374 lines
10 KiB
Go
package user_test
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/yaoapp/gou/model"
|
|
"github.com/yaoapp/kun/maps"
|
|
)
|
|
|
|
func TestUserListOperations(t *testing.T) {
|
|
prepare(t)
|
|
defer clean()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create multiple test users for list operations
|
|
// Use UUID to ensure unique identifiers
|
|
testUUID := strings.ReplaceAll(uuid.New().String(), "-", "")[:8] // 8 char UUID
|
|
testUsers := []*TestUserData{
|
|
createTestUserData("listops" + testUUID + "01"),
|
|
createTestUserData("listops" + testUUID + "02"),
|
|
createTestUserData("listops" + testUUID + "03"),
|
|
createTestUserData("listops" + testUUID + "04"),
|
|
createTestUserData("listops" + testUUID + "05"),
|
|
}
|
|
|
|
// Store created user IDs
|
|
userIDs := make([]string, len(testUsers))
|
|
|
|
// Create test users with varied data for testing
|
|
for i, userData := range testUsers {
|
|
// Vary some data for testing filters
|
|
if i%2 == 0 {
|
|
userData.Status = "active"
|
|
userData.RoleID = "admin"
|
|
} else {
|
|
userData.Status = "pending"
|
|
userData.RoleID = "user"
|
|
}
|
|
|
|
_, userID := setupTestUser(t, ctx, userData)
|
|
userIDs[i] = userID
|
|
}
|
|
|
|
// Test GetUsers
|
|
t.Run("GetUsers_All", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
}
|
|
users, err := testProvider.GetUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, len(users), 5) // At least our 5 test users
|
|
|
|
// Check that basic fields are returned by default
|
|
if len(users) > 0 {
|
|
user := users[0]
|
|
assert.Contains(t, user, "user_id")
|
|
assert.Contains(t, user, "preferred_username")
|
|
// Should not contain sensitive fields in basic view
|
|
assert.NotContains(t, user, "password_hash")
|
|
}
|
|
})
|
|
|
|
t.Run("GetUsers_WithFilters", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
{Column: "status", Value: "active"},
|
|
},
|
|
}
|
|
users, err := testProvider.GetUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, len(users), 3) // At least 3 active users (indexes 0, 2, 4)
|
|
|
|
// All returned users should be active
|
|
for _, user := range users {
|
|
if userID, ok := user["user_id"].(string); ok {
|
|
// Only check our test users
|
|
for _, testUserID := range userIDs {
|
|
if userID == testUserID {
|
|
assert.Equal(t, "active", user["status"])
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("GetUsers_WithCustomFields", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Select: []interface{}{"user_id", "preferred_username", "status"},
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
Limit: 2,
|
|
}
|
|
users, err := testProvider.GetUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.LessOrEqual(t, len(users), 2) // Respects limit
|
|
|
|
if len(users) > 0 {
|
|
user := users[0]
|
|
assert.Contains(t, user, "user_id")
|
|
assert.Contains(t, user, "preferred_username")
|
|
assert.Contains(t, user, "status")
|
|
}
|
|
})
|
|
|
|
t.Run("GetUsers_WithOrdering", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
Orders: []model.QueryOrder{
|
|
{Column: "preferred_username", Option: "asc"},
|
|
},
|
|
Limit: 3,
|
|
}
|
|
users, err := testProvider.GetUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.LessOrEqual(t, len(users), 3)
|
|
|
|
// Check ordering (should be sorted by preferred_username ascending)
|
|
if len(users) >= 2 {
|
|
first := users[0]["preferred_username"].(string)
|
|
second := users[1]["preferred_username"].(string)
|
|
assert.LessOrEqual(t, first, second)
|
|
}
|
|
})
|
|
|
|
// Test PaginateUsers
|
|
t.Run("PaginateUsers_FirstPage", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
Orders: []model.QueryOrder{
|
|
{Column: "preferred_username", Option: "asc"},
|
|
},
|
|
}
|
|
result, err := testProvider.PaginateUsers(ctx, param, 1, 3)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
|
|
// Check pagination structure
|
|
assert.Contains(t, result, "data")
|
|
assert.Contains(t, result, "total")
|
|
assert.Contains(t, result, "page")
|
|
assert.Contains(t, result, "pagesize")
|
|
|
|
data, ok := result["data"].([]maps.MapStr)
|
|
assert.True(t, ok)
|
|
assert.LessOrEqual(t, len(data), 3) // Page size limit
|
|
|
|
// Handle different total types
|
|
totalInterface, exists := result["total"]
|
|
assert.True(t, exists)
|
|
|
|
var total int64
|
|
switch v := totalInterface.(type) {
|
|
case int:
|
|
total = int64(v)
|
|
case int32:
|
|
total = int64(v)
|
|
case int64:
|
|
total = v
|
|
case uint:
|
|
total = int64(v)
|
|
case uint32:
|
|
total = int64(v)
|
|
case uint64:
|
|
total = int64(v)
|
|
default:
|
|
t.Errorf("unexpected total type: %T, value: %v", totalInterface, totalInterface)
|
|
}
|
|
assert.GreaterOrEqual(t, total, int64(0)) // Could be 0 or more
|
|
|
|
assert.Equal(t, 1, result["page"])
|
|
assert.Equal(t, 3, result["pagesize"])
|
|
})
|
|
|
|
t.Run("PaginateUsers_SecondPage", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
Orders: []model.QueryOrder{
|
|
{Column: "preferred_username", Option: "asc"},
|
|
},
|
|
}
|
|
result, err := testProvider.PaginateUsers(ctx, param, 2, 3)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
|
|
assert.Equal(t, 2, result["page"])
|
|
assert.Equal(t, 3, result["pagesize"])
|
|
|
|
_, ok := result["data"].([]maps.MapStr)
|
|
assert.True(t, ok)
|
|
// Second page may have fewer items depending on total count
|
|
})
|
|
|
|
t.Run("PaginateUsers_WithFilters", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
{Column: "role_id", Value: "admin"},
|
|
},
|
|
}
|
|
result, err := testProvider.PaginateUsers(ctx, param, 1, 10)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
|
|
data, ok := result["data"].([]maps.MapStr)
|
|
assert.True(t, ok)
|
|
assert.GreaterOrEqual(t, len(data), 0) // Could be 0 or more
|
|
|
|
// Verify admin filter works by checking our test users only
|
|
for _, user := range data {
|
|
if userID, ok := user["user_id"].(string); ok {
|
|
// Only check our test users
|
|
for _, testUserID := range userIDs {
|
|
if userID == testUserID {
|
|
assert.Equal(t, "admin", user["role_id"])
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
// Test CountUsers
|
|
t.Run("CountUsers_All", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
}
|
|
count, err := testProvider.CountUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, count, int64(0)) // At least 0 users
|
|
})
|
|
|
|
t.Run("CountUsers_WithFilters", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
{Column: "status", Value: "active"},
|
|
},
|
|
}
|
|
count, err := testProvider.CountUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, count, int64(3)) // At least 3 active users (indexes 0, 2, 4)
|
|
})
|
|
|
|
t.Run("CountUsers_SpecificRole", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
{Column: "role_id", Value: "user"},
|
|
},
|
|
}
|
|
count, err := testProvider.CountUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, count, int64(2)) // At least 2 regular users (indexes 1, 3)
|
|
})
|
|
|
|
t.Run("CountUsers_NoResults", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", Value: "nonexistent_user_id"},
|
|
},
|
|
}
|
|
count, err := testProvider.CountUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(0), count)
|
|
})
|
|
}
|
|
|
|
func TestUserListErrorHandling(t *testing.T) {
|
|
prepare(t)
|
|
defer clean()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Use UUID for unique test identifiers
|
|
testUUID := strings.ReplaceAll(uuid.New().String(), "-", "")[:8]
|
|
|
|
t.Run("GetUsers_EmptyResult", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", Value: "nonexistent_" + testUUID},
|
|
},
|
|
}
|
|
users, err := testProvider.GetUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 0, len(users)) // Empty slice, not nil
|
|
})
|
|
|
|
t.Run("PaginateUsers_EmptyResult", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", Value: "nonexistent_" + testUUID},
|
|
},
|
|
}
|
|
result, err := testProvider.PaginateUsers(ctx, param, 1, 10)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
|
|
data, ok := result["data"].([]maps.MapStr)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 0, len(data))
|
|
|
|
// Handle different total types
|
|
totalInterface, exists := result["total"]
|
|
assert.True(t, exists)
|
|
|
|
var total int64
|
|
switch v := totalInterface.(type) {
|
|
case int:
|
|
total = int64(v)
|
|
case int32:
|
|
total = int64(v)
|
|
case int64:
|
|
total = v
|
|
case uint:
|
|
total = int64(v)
|
|
case uint32:
|
|
total = int64(v)
|
|
case uint64:
|
|
total = int64(v)
|
|
default:
|
|
t.Errorf("unexpected total type: %T, value: %v", totalInterface, totalInterface)
|
|
}
|
|
assert.Equal(t, int64(0), total)
|
|
})
|
|
|
|
t.Run("PaginateUsers_LargePage", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
},
|
|
}
|
|
result, err := testProvider.PaginateUsers(ctx, param, 100, 10) // Page way beyond data
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, result)
|
|
|
|
data, ok := result["data"].([]maps.MapStr)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, 0, len(data)) // No data on this page
|
|
|
|
assert.Equal(t, 100, result["page"])
|
|
assert.Equal(t, 10, result["pagesize"])
|
|
})
|
|
|
|
t.Run("CountUsers_ComplexFilters", func(t *testing.T) {
|
|
param := model.QueryParam{
|
|
Wheres: []model.QueryWhere{
|
|
{Column: "user_id", OP: "like", Value: "test_%"},
|
|
{Column: "status", OP: "in", Value: []interface{}{"active", "pending"}},
|
|
{Column: "email_verified", Value: true},
|
|
},
|
|
}
|
|
count, err := testProvider.CountUsers(ctx, param)
|
|
assert.NoError(t, err)
|
|
assert.GreaterOrEqual(t, count, int64(0)) // Should handle complex filters without error
|
|
})
|
|
}
|