Skip to content

Commit 34bef4b

Browse files
authored
feat: Add custom faker (#52)
* feat: Add custom faker https://github.com/bxcodec/faker had a lot of issues including inherent race conditions and features we didn't need. This introduces custom faker which will make our life easier in mock tests.
1 parent 59f5903 commit 34bef4b

File tree

5 files changed

+211
-7
lines changed

5 files changed

+211
-7
lines changed

codegen/golang.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"reflect"
99
"strings"
1010
"text/template"
11+
"unicode"
1112

1213
"github.com/cloudquery/plugin-sdk/schema"
1314
"github.com/iancoleman/strcase"
@@ -133,6 +134,12 @@ func NewTableFromStruct(name string, obj interface{}, opts ...TableOptions) (*Ta
133134

134135
for i := 0; i < e.NumField(); i++ {
135136
field := e.Type().Field(i)
137+
if len(field.Name) == 0 {
138+
continue
139+
}
140+
if unicode.IsLower(rune(field.Name[0])) {
141+
continue
142+
}
136143
if sliceContains(t.skipFields, field.Name) {
137144
continue
138145
}
@@ -146,7 +153,8 @@ func NewTableFromStruct(name string, obj interface{}, opts ...TableOptions) (*Ta
146153

147154
columnType, err := valueToSchemaType(field.Type)
148155
if err != nil {
149-
return nil, err
156+
fmt.Printf("skipping field %s, got err: %v\n", field.Name, err)
157+
continue
150158
}
151159

152160
// generate a PathResolver to use by default

faker/faker.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package faker
2+
3+
import (
4+
"fmt"
5+
"math/rand"
6+
"reflect"
7+
"time"
8+
)
9+
10+
type faker struct {
11+
max_depth int
12+
// logger zerolog.Logger
13+
}
14+
15+
type FakerOption func(*faker)
16+
17+
func (f faker) getFakedValue(a interface{}) (reflect.Value, error) {
18+
t := reflect.TypeOf(a)
19+
if t == nil {
20+
return reflect.Value{}, fmt.Errorf("interface{} not allowed")
21+
}
22+
f.max_depth--
23+
if f.max_depth < 0 {
24+
return reflect.Value{}, fmt.Errorf("max_depth reached")
25+
}
26+
k := t.Kind()
27+
28+
switch k {
29+
case reflect.Ptr:
30+
v := reflect.New(t.Elem())
31+
var val reflect.Value
32+
var err error
33+
if a != reflect.Zero(reflect.TypeOf(a)).Interface() {
34+
val, err = f.getFakedValue(reflect.ValueOf(a).Elem().Interface())
35+
} else {
36+
val, err = f.getFakedValue(v.Elem().Interface())
37+
}
38+
if err != nil {
39+
return reflect.Value{}, err
40+
}
41+
v.Elem().Set(val.Convert(t.Elem()))
42+
return v, nil
43+
case reflect.Struct:
44+
switch t.String() {
45+
case "time.Time":
46+
ft := time.Now().Add(time.Duration(rand.Int63()))
47+
return reflect.ValueOf(ft), nil
48+
default:
49+
v := reflect.New(t).Elem()
50+
for i := 0; i < v.NumField(); i++ {
51+
if !v.Field(i).CanSet() {
52+
continue // to avoid panic to set on unexported field in struct
53+
}
54+
val, err := f.getFakedValue(v.Field(i).Interface())
55+
if err != nil {
56+
fmt.Println(err)
57+
continue
58+
// return reflect.Value{}, err
59+
}
60+
val = val.Convert(v.Field(i).Type())
61+
v.Field(i).Set(val)
62+
}
63+
return v, nil
64+
}
65+
case reflect.String:
66+
return reflect.ValueOf("test string"), nil
67+
case reflect.Slice:
68+
sliceLen := 1
69+
v := reflect.MakeSlice(t, sliceLen, sliceLen)
70+
for i := 0; i < v.Len(); i++ {
71+
val, err := f.getFakedValue(v.Index(i).Interface())
72+
if err != nil {
73+
return reflect.Value{}, err
74+
}
75+
val = val.Convert(v.Index(i).Type())
76+
v.Index(i).Set(val)
77+
}
78+
return v, nil
79+
case reflect.Array:
80+
v := reflect.New(t).Elem()
81+
for i := 0; i < v.Len(); i++ {
82+
val, err := f.getFakedValue(v.Index(i).Interface())
83+
if err != nil {
84+
return reflect.Value{}, err
85+
}
86+
val = val.Convert(v.Index(i).Type())
87+
v.Index(i).Set(val)
88+
}
89+
return v, nil
90+
case reflect.Int:
91+
return reflect.ValueOf(int(123)), nil
92+
case reflect.Int8:
93+
return reflect.ValueOf(int8(123)), nil
94+
case reflect.Int16:
95+
return reflect.ValueOf(int16(123)), nil
96+
case reflect.Int32:
97+
return reflect.ValueOf(int32(123)), nil
98+
case reflect.Int64:
99+
return reflect.ValueOf(int64(123)), nil
100+
case reflect.Float32:
101+
return reflect.ValueOf(float32(123)), nil
102+
case reflect.Float64:
103+
return reflect.ValueOf(float64(1.123)), nil
104+
case reflect.Bool:
105+
return reflect.ValueOf(true), nil
106+
107+
case reflect.Uint:
108+
return reflect.ValueOf(uint(123)), nil
109+
110+
case reflect.Uint8:
111+
return reflect.ValueOf(uint8(123)), nil
112+
113+
case reflect.Uint16:
114+
return reflect.ValueOf(uint16(123)), nil
115+
116+
case reflect.Uint32:
117+
return reflect.ValueOf(uint32(123)), nil
118+
119+
case reflect.Uint64:
120+
return reflect.ValueOf(uint64(123)), nil
121+
122+
case reflect.Map:
123+
v := reflect.MakeMap(t)
124+
for i := 0; i < 1; i++ {
125+
keyInstance := reflect.New(t.Key()).Elem().Interface()
126+
key, err := f.getFakedValue(keyInstance)
127+
if err != nil {
128+
return reflect.Value{}, err
129+
}
130+
131+
valueInstance := reflect.New(t.Elem()).Elem().Interface()
132+
val, err := f.getFakedValue(valueInstance)
133+
if err != nil {
134+
return reflect.Value{}, err
135+
}
136+
val = val.Convert(v.Type().Elem())
137+
v.SetMapIndex(key, val)
138+
}
139+
return v, nil
140+
default:
141+
err := fmt.Errorf("no support for kind %+v", t)
142+
return reflect.Value{}, err
143+
}
144+
}
145+
146+
func WithMaxDepth(depth int) FakerOption {
147+
return func(f *faker) {
148+
f.max_depth = depth
149+
}
150+
}
151+
152+
func FakeObject(obj interface{}, opts ...FakerOption) error {
153+
reflectType := reflect.TypeOf(obj)
154+
155+
if reflectType.Kind() != reflect.Ptr {
156+
return fmt.Errorf("object is not a pointer")
157+
}
158+
159+
if reflect.ValueOf(obj).IsNil() {
160+
return fmt.Errorf("object is nil %s", reflectType.Elem().String())
161+
}
162+
f := &faker{
163+
max_depth: 12,
164+
}
165+
for _, o := range opts {
166+
o(f)
167+
}
168+
169+
rval := reflect.ValueOf(obj)
170+
finalValue, err := f.getFakedValue(obj)
171+
if err != nil {
172+
return err
173+
}
174+
175+
rval.Elem().Set(finalValue.Elem().Convert(reflectType.Elem()))
176+
return nil
177+
}

faker/faker_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package faker
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
type testFakerStruct struct {
9+
A int
10+
B string
11+
C *string
12+
}
13+
14+
func TestFaker(t *testing.T) {
15+
a := testFakerStruct{}
16+
if err := FakeObject(&a); err != nil {
17+
t.Fatal(err)
18+
}
19+
fmt.Println(a)
20+
}

plugins/source.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func (p *SourcePlugin) Sync(ctx context.Context, spec specs.Source, res chan<- *
149149
for _, table := range p.tables {
150150
table := table
151151
if funk.ContainsString(spec.SkipTables, table.Name) || !funk.ContainsString(tableNames, table.Name) {
152-
p.logger.Info().Str("table", table.Name).Msg("skipping table")
152+
p.logger.Debug().Str("table", table.Name).Msg("skipping table")
153153
continue
154154
}
155155
clients := []schema.ClientMeta{c}

plugins/source_testing.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func init() {
2626
// type
2727

2828
func TestSourcePluginSync(t *testing.T, plugin *SourcePlugin, spec specs.Source) {
29-
t.Parallel()
29+
// t.Parallel()
3030
t.Helper()
3131
// No need for configuration or db connection, get it out of the way first
3232
// testTableIdentifiersForProvider(t, resource.Provider)
@@ -46,13 +46,12 @@ func TestSourcePluginSync(t *testing.T, plugin *SourcePlugin, spec specs.Source)
4646
totalResources++
4747
validateResource(t, resource)
4848
}
49-
if totalResources == 0 {
50-
t.Fatal("no resources fetched")
51-
}
52-
5349
if fetchErr != nil {
5450
t.Fatal(fetchErr)
5551
}
52+
if totalResources == 0 {
53+
t.Fatal("no resources fetched")
54+
}
5655
}
5756

5857
func validateResource(t *testing.T, resource *schema.Resource) {

0 commit comments

Comments
 (0)