mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: use database for user creation to prevent flake (#10992)
This commit is contained in:
+44
-29
@@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||||
"github.com/coder/coder/v2/coderd/database"
|
"github.com/coder/coder/v2/coderd/database"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||||
|
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||||
"github.com/coder/coder/v2/coderd/rbac"
|
"github.com/coder/coder/v2/coderd/rbac"
|
||||||
"github.com/coder/coder/v2/coderd/util/slice"
|
"github.com/coder/coder/v2/coderd/util/slice"
|
||||||
@@ -1699,7 +1700,7 @@ func TestSuspendedPagination(t *testing.T) {
|
|||||||
// them using different page sizes.
|
// them using different page sizes.
|
||||||
func TestPaginatedUsers(t *testing.T) {
|
func TestPaginatedUsers(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, nil)
|
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||||
coderdtest.CreateFirstUser(t, client)
|
coderdtest.CreateFirstUser(t, client)
|
||||||
|
|
||||||
// This test takes longer than a long time.
|
// This test takes longer than a long time.
|
||||||
@@ -1708,15 +1709,17 @@ func TestPaginatedUsers(t *testing.T) {
|
|||||||
|
|
||||||
me, err := client.User(ctx, codersdk.Me)
|
me, err := client.User(ctx, codersdk.Me)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
orgID := me.OrganizationIDs[0]
|
|
||||||
|
|
||||||
// When 50 users exist
|
// When 50 users exist
|
||||||
total := 50
|
total := 50
|
||||||
allUsers := make([]codersdk.User, total+1) // +1 forme
|
allUsers := make([]database.User, total+1)
|
||||||
allUsers[0] = me
|
allUsers[0] = database.User{
|
||||||
specialUsers := make([]codersdk.User, total/2)
|
Email: me.Email,
|
||||||
|
Username: me.Username,
|
||||||
|
}
|
||||||
|
specialUsers := make([]database.User, total/2)
|
||||||
|
|
||||||
eg, egCtx := errgroup.WithContext(ctx)
|
eg, _ := errgroup.WithContext(ctx)
|
||||||
// Create users
|
// Create users
|
||||||
for i := 0; i < total; i++ {
|
for i := 0; i < total; i++ {
|
||||||
i := i
|
i := i
|
||||||
@@ -1730,21 +1733,14 @@ func TestPaginatedUsers(t *testing.T) {
|
|||||||
if i%3 == 0 {
|
if i%3 == 0 {
|
||||||
username = strings.ToUpper(username)
|
username = strings.ToUpper(username)
|
||||||
}
|
}
|
||||||
// One side effect of having to use the api vs the db calls directly, is you cannot
|
|
||||||
// mock time. Ideally I could pass in mocked times and space these users out.
|
// We used to use the API to ceate users, but that is slow.
|
||||||
//
|
// Instead, we create them directly in the database now
|
||||||
// But this also serves as a good test. Postgres has microsecond precision on its timestamps.
|
// to prevent timeout flakes.
|
||||||
// If 2 users share the same created_at, that could cause an issue if you are strictly paginating via
|
newUser := dbgen.User(t, db, database.User{
|
||||||
// timestamps. The pagination goes by timestamps and uuids.
|
Email: email,
|
||||||
newUser, err := client.CreateUser(egCtx, codersdk.CreateUserRequest{
|
Username: username,
|
||||||
Email: email,
|
|
||||||
Username: username,
|
|
||||||
Password: "MySecurePassword!",
|
|
||||||
OrganizationID: orgID,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
allUsers[i+1] = newUser
|
allUsers[i+1] = newUser
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
specialUsers[i/2] = newUser
|
specialUsers[i/2] = newUser
|
||||||
@@ -1757,8 +1753,8 @@ func TestPaginatedUsers(t *testing.T) {
|
|||||||
require.NoError(t, err, "create users failed")
|
require.NoError(t, err, "create users failed")
|
||||||
|
|
||||||
// Sorting the users will sort by username.
|
// Sorting the users will sort by username.
|
||||||
sortUsers(allUsers)
|
sortDatabaseUsers(allUsers)
|
||||||
sortUsers(specialUsers)
|
sortDatabaseUsers(specialUsers)
|
||||||
|
|
||||||
gmailSearch := func(request codersdk.UsersRequest) codersdk.UsersRequest {
|
gmailSearch := func(request codersdk.UsersRequest) codersdk.UsersRequest {
|
||||||
request.Search = "gmail"
|
request.Search = "gmail"
|
||||||
@@ -1772,7 +1768,7 @@ func TestPaginatedUsers(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
limit int
|
limit int
|
||||||
allUsers []codersdk.User
|
allUsers []database.User
|
||||||
opt func(request codersdk.UsersRequest) codersdk.UsersRequest
|
opt func(request codersdk.UsersRequest) codersdk.UsersRequest
|
||||||
}{
|
}{
|
||||||
{name: "all users", limit: 10, allUsers: allUsers},
|
{name: "all users", limit: 10, allUsers: allUsers},
|
||||||
@@ -1800,7 +1796,7 @@ func TestPaginatedUsers(t *testing.T) {
|
|||||||
// Assert pagination will page through the list of all users using the given
|
// Assert pagination will page through the list of all users using the given
|
||||||
// limit for each page. The 'allUsers' is the expected full list to compare
|
// limit for each page. The 'allUsers' is the expected full list to compare
|
||||||
// against.
|
// against.
|
||||||
func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client, limit int, allUsers []codersdk.User,
|
func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client, limit int, allUsers []database.User,
|
||||||
opt func(request codersdk.UsersRequest) codersdk.UsersRequest,
|
opt func(request codersdk.UsersRequest) codersdk.UsersRequest,
|
||||||
) {
|
) {
|
||||||
var count int
|
var count int
|
||||||
@@ -1817,7 +1813,7 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
require.NoError(t, err, "first page")
|
require.NoError(t, err, "first page")
|
||||||
require.Equalf(t, page.Users, allUsers[:limit], "first page, limit=%d", limit)
|
require.Equalf(t, onlyUsernames(page.Users), onlyUsernames(allUsers[:limit]), "first page, limit=%d", limit)
|
||||||
count += len(page.Users)
|
count += len(page.Users)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -1846,14 +1842,14 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
|
|||||||
}))
|
}))
|
||||||
require.NoError(t, err, "next offset page")
|
require.NoError(t, err, "next offset page")
|
||||||
|
|
||||||
var expected []codersdk.User
|
var expected []database.User
|
||||||
if count+limit > len(allUsers) {
|
if count+limit > len(allUsers) {
|
||||||
expected = allUsers[count:]
|
expected = allUsers[count:]
|
||||||
} else {
|
} else {
|
||||||
expected = allUsers[count : count+limit]
|
expected = allUsers[count : count+limit]
|
||||||
}
|
}
|
||||||
require.Equalf(t, page.Users, expected, "next users, after=%s, limit=%d", afterCursor, limit)
|
require.Equalf(t, onlyUsernames(page.Users), onlyUsernames(expected), "next users, after=%s, limit=%d", afterCursor, limit)
|
||||||
require.Equalf(t, offsetPage.Users, expected, "offset users, offset=%d, limit=%d", count, limit)
|
require.Equalf(t, onlyUsernames(offsetPage.Users), onlyUsernames(expected), "offset users, offset=%d, limit=%d", count, limit)
|
||||||
|
|
||||||
// Also check the before
|
// Also check the before
|
||||||
prevPage, err := client.Users(ctx, opt(codersdk.UsersRequest{
|
prevPage, err := client.Users(ctx, opt(codersdk.UsersRequest{
|
||||||
@@ -1863,7 +1859,7 @@ func assertPagination(ctx context.Context, t *testing.T, client *codersdk.Client
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
require.NoError(t, err, "prev page")
|
require.NoError(t, err, "prev page")
|
||||||
require.Equal(t, allUsers[count-limit:count], prevPage.Users, "prev users")
|
require.Equal(t, onlyUsernames(allUsers[count-limit:count]), onlyUsernames(prevPage.Users), "prev users")
|
||||||
count += len(page.Users)
|
count += len(page.Users)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1875,6 +1871,25 @@ func sortUsers(users []codersdk.User) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sortDatabaseUsers(users []database.User) {
|
||||||
|
slices.SortFunc(users, func(a, b database.User) int {
|
||||||
|
return slice.Ascending(strings.ToLower(a.Username), strings.ToLower(b.Username))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func onlyUsernames[U codersdk.User | database.User](users []U) []string {
|
||||||
|
var out []string
|
||||||
|
for _, u := range users {
|
||||||
|
switch u := (any(u)).(type) {
|
||||||
|
case codersdk.User:
|
||||||
|
out = append(out, u.Username)
|
||||||
|
case database.User:
|
||||||
|
out = append(out, u.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkUsersMe(b *testing.B) {
|
func BenchmarkUsersMe(b *testing.B) {
|
||||||
client := coderdtest.New(b, nil)
|
client := coderdtest.New(b, nil)
|
||||||
_ = coderdtest.CreateFirstUser(b, client)
|
_ = coderdtest.CreateFirstUser(b, client)
|
||||||
|
|||||||
Reference in New Issue
Block a user