From 2415af38b2f782e10177feb38b116c668ef67701 Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 30 Sep 2025 17:09:19 -0400 Subject: [PATCH] Fixing struct generation - ready to test - slice size bugfixes - appending empty struct before filling it out --- .gitignore | 2 ++ lazy.go | 72 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/.gitignore b/.gitignore index 5b90e79..05d91d7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ go.work.sum # env file .env +#ignore test files +cmd/ \ No newline at end of file diff --git a/lazy.go b/lazy.go index b72bc90..ff77f46 100644 --- a/lazy.go +++ b/lazy.go @@ -20,7 +20,6 @@ const ( var ( 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") - ErrTypeCast = errors.New("unable to type cast the mock structs") //contains placeholder to inject type on runtime ErrUnsupportedType = errors.New("type: %s is not yet supported") ) @@ -33,13 +32,19 @@ type RowMock struct { } // configuration for RandomGenerate -type Config struct { +type RowConfig struct { Query string Example any Keys []any RowCount int } +type StructConfig struct { + Query string + Example any + RowCount int +} + type StructMock struct { Query string Args []driver.Value @@ -48,35 +53,35 @@ type StructMock struct { } // 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 - if m.Example == nil { + if r.Example == nil { return nil, ErrBadExample } - if reflect.ValueOf(m.Example).Kind() != reflect.Struct { + if reflect.ValueOf(r.Example).Kind() != reflect.Struct { return nil, ErrBadExample } //any weirdness, just pull one row - if m.RowCount <= 0 { - m.RowCount = 1 + if r.RowCount <= 0 { + r.RowCount = 1 } //if keys are set, ensure there are enough to populate requested rows primaryKey := false - if len(m.Keys) > 0 { + if len(r.Keys) > 0 { primaryKey = true - if len(m.Keys) != m.RowCount { + if len(r.Keys) != r.RowCount { return nil, ErrMissingKeys } } - retType := reflect.TypeOf(m.Example) + retType := reflect.TypeOf(r.Example) maxFieldCount := retType.NumField() columns := make([]string, 0, maxFieldCount) 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)) for x := 0; x < maxFieldCount; 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.Tag.Get(LAZY_TAG) == KEY_VALUE && primaryKey { - rows[y] = append(rows[y], m.Keys[y]) + rows[y] = append(rows[y], r.Keys[y]) continue } @@ -108,16 +113,16 @@ func RandomRows(m Config) (*RowMock, error) { return &RowMock{ //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, Rows: rows, }, nil } -// we are close to something here but the mock structs aren't it chief. Maybe the users can make() them first? -// I don't love that either tho tbh -// hmmmmm -func RandomStruct(c Config) (*StructMock, error) { +// generates mock data for insert statements to be used for sqlmock, it also returns a slice of +// example structs with mock data +func RandomStruct(c StructConfig) (*StructMock, error) { + //make sure we have an example struct if c.Example == nil { return nil, ErrBadExample } @@ -125,50 +130,56 @@ func RandomStruct(c Config) (*StructMock, error) { return nil, ErrBadExample } + //we need at least one row if c.RowCount <= 0 { c.RowCount = 1 } + //get example type and number of fields retType := reflect.TypeOf(c.Example) maxFieldCount := retType.NumField() - //create slice of structs + + //create slice of example structs ft := reflect.SliceOf(retType) filled := reflect.MakeSlice(ft, 0, c.RowCount) - // filled := reflect.MakeSlice(ft, 0, c.RowCount).Elem() - args := make([]driver.Value, 0, maxFieldCount) + args := make([]driver.Value, 0) //args for sqlmock WithArgs 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++ { field := retType.Field(y) dbTag := field.Tag.Get(DB_TAG) + // no db tag, we skip if dbTag == "" { continue } + //get random value nv := kindToRandom(field) if nv == nil { return nil, fmt.Errorf(ErrUnsupportedType.Error(), field.Type.Name()) } - args[y] = nv - // if x == 0 { - nf := filled.Index(x).Field(y) - if nf.CanSet() { + //add to sqlmock args + args = append(args, nv) + + if filled.Index(x).Field(y).CanSet() { filled.Index(x).Field(y).Set(reflect.ValueOf(nv)) } - // } } } - ms, ok := reflect.ValueOf(filled).Interface().([]any) - if !ok { - return nil, ErrTypeCast + //convert reflect.Value to []any + retStructs := make([]any, 0) + for x := 0; x < filled.Len(); x++ { + retStructs = append(retStructs, filled.Index(x)) } return &StructMock{ Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(c.Query)), Args: args, - MockStructs: ms, + MockStructs: retStructs, Result: &sqlResult{ rowsAffected: int64(c.RowCount), err: nil, @@ -176,7 +187,8 @@ func RandomStruct(c Config) (*StructMock, error) { }, 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 { kind := field.Type.Kind() switch kind {