From ff62b371c9c9b30a2953b3969d6d19c014b79fa8 Mon Sep 17 00:00:00 2001 From: jake Date: Mon, 29 Sep 2025 15:47:51 -0400 Subject: [PATCH] adding new functionality for mocks - working through insert logic - ideally return struct with 'good' fields - also return list of args to prevent relooping --- driver.go | 17 ++++++++++++ lazy.go | 82 +++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 driver.go diff --git a/driver.go b/driver.go new file mode 100644 index 0000000..907aa03 --- /dev/null +++ b/driver.go @@ -0,0 +1,17 @@ +package lazy + +//driver.Result does not play well with creation, overriding it here so we can return the results without +//requiring sqlmock +type sqlResult struct { + insertID int64 + rowsAffected int64 + err error +} + +func (r *sqlResult) LastInsertId() (int64, error) { + return r.insertID, r.err +} + +func (r *sqlResult) RowsAffected() (int64, error) { + return r.rowsAffected, r.err +} diff --git a/lazy.go b/lazy.go index 4a7bcc4..40c8469 100644 --- a/lazy.go +++ b/lazy.go @@ -17,29 +17,47 @@ const ( KEY_VALUE = "key" //tag value for LAZY_TAG ) +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") + ErrUnsupportedType = errors.New("") +) + // mock data generated based on config -type Mock struct { +type RowMock struct { Query string Columns []string Rows [][]driver.Value } // configuration for RandomGenerate -type Config struct { +type RowConfig struct { Query string Example any - Keys []any //length of keys must = RowCount, if set + Keys []any //if set, length of keys must = RowCount RowCount int } +type StructMock struct { + Query string + Args []any + MockStruct any + Result driver.Result +} + +type StructConfig struct { + Query string + Example any +} + // generates mock data based on configuration to be used for sqlmock -func RandomGenerate(m Config) (*Mock, error) { +func RandomRows(m RowConfig) (*RowMock, error) { //example struct cannot be nil and must be a struct if m.Example == nil { - return nil, errors.New("example value cannot be nil") + return nil, ErrBadExample } if reflect.ValueOf(m.Example).Kind() != reflect.Struct { - return nil, errors.New("example value must be a struct") + return nil, ErrBadExample } //any weirdness, just pull one row @@ -52,7 +70,7 @@ func RandomGenerate(m Config) (*Mock, error) { if len(m.Keys) > 0 { primaryKey = true if len(m.Keys) != m.RowCount { - return nil, errors.New("you must provide a key for each row") + return nil, ErrMissingKeys } } @@ -84,14 +102,14 @@ func RandomGenerate(m Config) (*Mock, error) { //generate random values nv := kindToRandom(field) if nv == nil { - return nil, fmt.Errorf("could not match type: %s", retType.Name()) + return nil, ErrUnsupportedType } rows[y] = append(rows[y], nv) } } - return &Mock{ + return &RowMock{ //sql is rebound and escaped for sqlmock.ExpectQuery Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(m.Query)), Columns: columns, @@ -99,6 +117,52 @@ func RandomGenerate(m Config) (*Mock, error) { }, nil } +// so this would "work" but only accounts for inserting one row and doesn't allow for hardcoded ID's +// it would also have to account in the results struct these changes for rowsAffected, etc. +func RandomStruct(c StructConfig) (*StructMock, error) { + if c.Example == nil { + return nil, ErrBadExample + } + if reflect.ValueOf(c.Example).Kind() != reflect.Struct { + return nil, ErrBadExample + } + + retType := reflect.TypeOf(c.Example) + maxFieldCount := retType.NumField() + filled := reflect.New(retType).Elem() + args := make([]any, 0, maxFieldCount) + + for y := 0; y < maxFieldCount; y++ { + field := retType.Field(y) + dbTag := field.Tag.Get(DB_TAG) + if dbTag == "" { + continue + } + + nv := kindToRandom(field) + if nv == nil { + return nil, ErrUnsupportedType + } + + args[y] = nv + nf := filled.Field(y) + if nf.CanSet() { + filled.Field(y).Set(reflect.ValueOf(nv)) + } + } + + return &StructMock{ + Query: sqlx.Rebind(sqlx.AT, regexp.QuoteMeta(c.Query)), + Args: args, + MockStruct: filled, + Result: &sqlResult{ + insertID: 1, + rowsAffected: 1, + err: nil, + }, + }, nil +} + // converts basic reflect Kind's to psuedo-random data, slices are given a random amount of entries func kindToRandom(field reflect.StructField) any { kind := field.Type.Kind()