yao/openapi/oauth/providers/user/user_list_test.go
Max 11f69b8c38 Update go-nanoid dependency and implement user retrieval methods
- 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.
2025-08-02 18:10:04 +08:00

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
})
}