test: start migrating dbauthz tests to mocked db (#19257)
This PR adds a framework to move to a mocked db. And therefore massively speed up these tests.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v7"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Fake will populate any zero fields in the provided struct with fake data.
|
||||
// Non-zero fields will remain unchanged.
|
||||
// Usage:
|
||||
//
|
||||
// key := Fake(t, faker, database.APIKey{
|
||||
// TokenName: "keep-my-name",
|
||||
// })
|
||||
func Fake[T any](t *testing.T, faker *gofakeit.Faker, seed T) T {
|
||||
t.Helper()
|
||||
|
||||
var tmp T
|
||||
err := faker.Struct(&tmp)
|
||||
require.NoError(t, err, "failed to generate fake data for type %T", tmp)
|
||||
|
||||
mergeZero(&seed, tmp)
|
||||
return seed
|
||||
}
|
||||
|
||||
// mergeZero merges the fields of src into dst, but only if the field in dst is
|
||||
// currently the zero value.
|
||||
// Make sure `dst` is a pointer to a struct, otherwise the fields are not assignable.
|
||||
func mergeZero(dst any, src any) {
|
||||
srcv := reflect.ValueOf(src)
|
||||
if srcv.Kind() == reflect.Ptr {
|
||||
srcv = srcv.Elem()
|
||||
}
|
||||
remain := [][2]reflect.Value{
|
||||
{reflect.ValueOf(dst).Elem(), srcv},
|
||||
}
|
||||
|
||||
// Traverse the struct fields and set them only if they are currently zero.
|
||||
// This is a breadth-first traversal of the struct fields. Struct definitions
|
||||
// Should not be that deep, so we should not hit any stack overflow issues.
|
||||
for {
|
||||
if len(remain) == 0 {
|
||||
return
|
||||
}
|
||||
dv, sv := remain[0][0], remain[0][1]
|
||||
remain = remain[1:] //
|
||||
for i := 0; i < dv.NumField(); i++ {
|
||||
df := dv.Field(i)
|
||||
sf := sv.Field(i)
|
||||
if !df.CanSet() {
|
||||
continue
|
||||
}
|
||||
if df.IsZero() { // only write if currently zero
|
||||
df.Set(sf)
|
||||
continue
|
||||
}
|
||||
|
||||
if dv.Field(i).Kind() == reflect.Struct {
|
||||
// If the field is a struct, we need to traverse it as well.
|
||||
remain = append(remain, [2]reflect.Value{df, sf})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package testutil_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v7"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
type simpleStruct struct {
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
Description string
|
||||
Age int `fake:"{number:18,60}"`
|
||||
}
|
||||
|
||||
type nestedStruct struct {
|
||||
Person simpleStruct
|
||||
Address string
|
||||
}
|
||||
|
||||
func TestFake(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("Simple", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
faker := gofakeit.New(0)
|
||||
person := testutil.Fake(t, faker, simpleStruct{
|
||||
Name: "alice",
|
||||
})
|
||||
require.Equal(t, "alice", person.Name)
|
||||
require.NotEqual(t, uuid.Nil, person.ID)
|
||||
require.NotEmpty(t, person.Description)
|
||||
require.Greater(t, person.Age, 17, "Age should be greater than 17")
|
||||
require.Less(t, person.Age, 61, "Age should be less than 61")
|
||||
})
|
||||
|
||||
t.Run("Nested", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
faker := gofakeit.New(0)
|
||||
person := testutil.Fake(t, faker, nestedStruct{
|
||||
Person: simpleStruct{
|
||||
Name: "alice",
|
||||
},
|
||||
})
|
||||
require.Equal(t, "alice", person.Person.Name)
|
||||
require.NotEqual(t, uuid.Nil, person.Person.ID)
|
||||
require.NotEmpty(t, person.Person.Description)
|
||||
require.Greater(t, person.Person.Age, 17, "Age should be greater than 17")
|
||||
require.NotEmpty(t, person.Address)
|
||||
})
|
||||
|
||||
t.Run("DatabaseType", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
faker := gofakeit.New(0)
|
||||
id := uuid.New()
|
||||
key := testutil.Fake(t, faker, database.APIKey{
|
||||
UserID: id,
|
||||
TokenName: "keep-my-name",
|
||||
})
|
||||
require.Equal(t, id, key.UserID)
|
||||
require.NotEmpty(t, key.TokenName)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user