Fixing struct generation
- ready to test - slice size bugfixes - appending empty struct before filling it out
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -25,3 +25,5 @@ go.work.sum
|
|||||||
# env file
|
# env file
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
#ignore test files
|
||||||
|
cmd/
|
72
lazy.go
72
lazy.go
@@ -20,7 +20,6 @@ const (
|
|||||||
var (
|
var (
|
||||||
ErrBadExample = errors.New("example field must be a non-nil struct")
|
ErrBadExample = errors.New("example field must be a non-nil struct")
|
||||||
ErrMissingKeys = errors.New("there must be a key for each row requested with RowCount")
|
ErrMissingKeys = errors.New("there must be a key for each row requested with RowCount")
|
||||||
ErrTypeCast = errors.New("unable to type cast the mock structs")
|
|
||||||
//contains placeholder to inject type on runtime
|
//contains placeholder to inject type on runtime
|
||||||
ErrUnsupportedType = errors.New("type: %s is not yet supported")
|
ErrUnsupportedType = errors.New("type: %s is not yet supported")
|
||||||
)
|
)
|
||||||
@@ -33,13 +32,19 @@ type RowMock struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configuration for RandomGenerate
|
// configuration for RandomGenerate
|
||||||
type Config struct {
|
type RowConfig struct {
|
||||||
Query string
|
Query string
|
||||||
Example any
|
Example any
|
||||||
Keys []any
|
Keys []any
|
||||||
RowCount int
|
RowCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StructConfig struct {
|
||||||
|
Query string
|
||||||
|
Example any
|
||||||
|
RowCount int
|
||||||
|
}
|
||||||
|
|
||||||
type StructMock struct {
|
type StructMock struct {
|
||||||
Query string
|
Query string
|
||||||
Args []driver.Value
|
Args []driver.Value
|
||||||
@@ -48,35 +53,35 @@ type StructMock struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generates mock data based on configuration to be used for sqlmock
|
// generates mock data based on configuration to be used for sqlmock
|
||||||
func RandomRows(m Config) (*RowMock, error) {
|
func RandomRows(r RowConfig) (*RowMock, error) {
|
||||||
//example struct cannot be nil and must be a struct
|
//example struct cannot be nil and must be a struct
|
||||||
if m.Example == nil {
|
if r.Example == nil {
|
||||||
return nil, ErrBadExample
|
return nil, ErrBadExample
|
||||||
}
|
}
|
||||||
if reflect.ValueOf(m.Example).Kind() != reflect.Struct {
|
if reflect.ValueOf(r.Example).Kind() != reflect.Struct {
|
||||||
return nil, ErrBadExample
|
return nil, ErrBadExample
|
||||||
}
|
}
|
||||||
|
|
||||||
//any weirdness, just pull one row
|
//any weirdness, just pull one row
|
||||||
if m.RowCount <= 0 {
|
if r.RowCount <= 0 {
|
||||||
m.RowCount = 1
|
r.RowCount = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
//if keys are set, ensure there are enough to populate requested rows
|
//if keys are set, ensure there are enough to populate requested rows
|
||||||
primaryKey := false
|
primaryKey := false
|
||||||
if len(m.Keys) > 0 {
|
if len(r.Keys) > 0 {
|
||||||
primaryKey = true
|
primaryKey = true
|
||||||
if len(m.Keys) != m.RowCount {
|
if len(r.Keys) != r.RowCount {
|
||||||
return nil, ErrMissingKeys
|
return nil, ErrMissingKeys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
retType := reflect.TypeOf(m.Example)
|
retType := reflect.TypeOf(r.Example)
|
||||||
maxFieldCount := retType.NumField()
|
maxFieldCount := retType.NumField()
|
||||||
columns := make([]string, 0, maxFieldCount)
|
columns := make([]string, 0, maxFieldCount)
|
||||||
rows := make([][]driver.Value, 0)
|
rows := make([][]driver.Value, 0)
|
||||||
|
|
||||||
for y := 0; y < m.RowCount; y++ {
|
for y := 0; y < r.RowCount; y++ {
|
||||||
rows = append(rows, make([]driver.Value, 0))
|
rows = append(rows, make([]driver.Value, 0))
|
||||||
for x := 0; x < maxFieldCount; x++ {
|
for x := 0; x < maxFieldCount; x++ {
|
||||||
field := retType.Field(x)
|
field := retType.Field(x)
|
||||||
@@ -92,7 +97,7 @@ func RandomRows(m Config) (*RowMock, error) {
|
|||||||
|
|
||||||
//if field has lazy:"key" tag and tags are present inject primary key
|
//if field has lazy:"key" tag and tags are present inject primary key
|
||||||
if field.Tag.Get(LAZY_TAG) == KEY_VALUE && primaryKey {
|
if field.Tag.Get(LAZY_TAG) == KEY_VALUE && primaryKey {
|
||||||
rows[y] = append(rows[y], m.Keys[y])
|
rows[y] = append(rows[y], r.Keys[y])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,16 +113,16 @@ func RandomRows(m Config) (*RowMock, error) {
|
|||||||
|
|
||||||
return &RowMock{
|
return &RowMock{
|
||||||
//sql is rebound and escaped for sqlmock.ExpectQuery
|
//sql is rebound and escaped for sqlmock.ExpectQuery
|
||||||
Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(m.Query)),
|
Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(r.Query)),
|
||||||
Columns: columns,
|
Columns: columns,
|
||||||
Rows: rows,
|
Rows: rows,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are close to something here but the mock structs aren't it chief. Maybe the users can make() them first?
|
// generates mock data for insert statements to be used for sqlmock, it also returns a slice of
|
||||||
// I don't love that either tho tbh
|
// example structs with mock data
|
||||||
// hmmmmm
|
func RandomStruct(c StructConfig) (*StructMock, error) {
|
||||||
func RandomStruct(c Config) (*StructMock, error) {
|
//make sure we have an example struct
|
||||||
if c.Example == nil {
|
if c.Example == nil {
|
||||||
return nil, ErrBadExample
|
return nil, ErrBadExample
|
||||||
}
|
}
|
||||||
@@ -125,50 +130,56 @@ func RandomStruct(c Config) (*StructMock, error) {
|
|||||||
return nil, ErrBadExample
|
return nil, ErrBadExample
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//we need at least one row
|
||||||
if c.RowCount <= 0 {
|
if c.RowCount <= 0 {
|
||||||
c.RowCount = 1
|
c.RowCount = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get example type and number of fields
|
||||||
retType := reflect.TypeOf(c.Example)
|
retType := reflect.TypeOf(c.Example)
|
||||||
maxFieldCount := retType.NumField()
|
maxFieldCount := retType.NumField()
|
||||||
//create slice of structs
|
|
||||||
|
//create slice of example structs
|
||||||
ft := reflect.SliceOf(retType)
|
ft := reflect.SliceOf(retType)
|
||||||
filled := reflect.MakeSlice(ft, 0, c.RowCount)
|
filled := reflect.MakeSlice(ft, 0, c.RowCount)
|
||||||
// filled := reflect.MakeSlice(ft, 0, c.RowCount).Elem()
|
args := make([]driver.Value, 0) //args for sqlmock WithArgs
|
||||||
args := make([]driver.Value, 0, maxFieldCount)
|
|
||||||
|
|
||||||
for x := 0; x < c.RowCount; x++ {
|
for x := 0; x < c.RowCount; x++ {
|
||||||
|
//add empty example struct
|
||||||
|
filled = reflect.Append(filled, reflect.ValueOf(c.Example))
|
||||||
for y := 0; y < maxFieldCount; y++ {
|
for y := 0; y < maxFieldCount; y++ {
|
||||||
field := retType.Field(y)
|
field := retType.Field(y)
|
||||||
dbTag := field.Tag.Get(DB_TAG)
|
dbTag := field.Tag.Get(DB_TAG)
|
||||||
|
// no db tag, we skip
|
||||||
if dbTag == "" {
|
if dbTag == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//get random value
|
||||||
nv := kindToRandom(field)
|
nv := kindToRandom(field)
|
||||||
if nv == nil {
|
if nv == nil {
|
||||||
return nil, fmt.Errorf(ErrUnsupportedType.Error(), field.Type.Name())
|
return nil, fmt.Errorf(ErrUnsupportedType.Error(), field.Type.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
args[y] = nv
|
//add to sqlmock args
|
||||||
// if x == 0 {
|
args = append(args, nv)
|
||||||
nf := filled.Index(x).Field(y)
|
|
||||||
if nf.CanSet() {
|
if filled.Index(x).Field(y).CanSet() {
|
||||||
filled.Index(x).Field(y).Set(reflect.ValueOf(nv))
|
filled.Index(x).Field(y).Set(reflect.ValueOf(nv))
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ms, ok := reflect.ValueOf(filled).Interface().([]any)
|
//convert reflect.Value to []any
|
||||||
if !ok {
|
retStructs := make([]any, 0)
|
||||||
return nil, ErrTypeCast
|
for x := 0; x < filled.Len(); x++ {
|
||||||
|
retStructs = append(retStructs, filled.Index(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &StructMock{
|
return &StructMock{
|
||||||
Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(c.Query)),
|
Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(c.Query)),
|
||||||
Args: args,
|
Args: args,
|
||||||
MockStructs: ms,
|
MockStructs: retStructs,
|
||||||
Result: &sqlResult{
|
Result: &sqlResult{
|
||||||
rowsAffected: int64(c.RowCount),
|
rowsAffected: int64(c.RowCount),
|
||||||
err: nil,
|
err: nil,
|
||||||
@@ -176,7 +187,8 @@ func RandomStruct(c Config) (*StructMock, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts basic reflect Kind's to psuedo-random data, slices are given a random amount of entries
|
// converts basic reflect Kind's to psuedo-random data, it is super basic and supports very basic types. Slices and arrays
|
||||||
|
// are supported, slices will get 1-10 entries and arrays will fill length
|
||||||
func kindToRandom(field reflect.StructField) any {
|
func kindToRandom(field reflect.StructField) any {
|
||||||
kind := field.Type.Kind()
|
kind := field.Type.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
|
Reference in New Issue
Block a user