From 5e07ae6db0bb8e9e5a2515cfa3ff8d18aaf2aaba Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 01:59:01 -0500 Subject: [PATCH 01/17] move custom tag check to inside userDefinedArray (following similar pattern to the other userDefined functions); rearrange and add tests; --- faker.go | 24 +++++++++--------------- faker_test.go | 47 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/faker.go b/faker.go index 1da4bc9..197162a 100644 --- a/faker.go +++ b/faker.go @@ -622,21 +622,6 @@ func setDataWithTag(v reflect.Value, tag string) error { reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: return userDefinedNumber(v, tag) case reflect.Slice, reflect.Array: - /** - * check for added Provider tag first before - * defaulting to userDefinedArray() - * this way the user at least has the - * option of a custom tag working - */ - _, tagExists := mapperTag[tag] - if tagExists { - res, err := mapperTag[tag](v) - if err != nil { - return err - } - v.Set(reflect.ValueOf(res)) - return nil - } return userDefinedArray(v, tag) case reflect.Map: return userDefinedMap(v, tag) @@ -706,6 +691,15 @@ func getValueWithTag(t reflect.Type, tag string) (interface{}, error) { } func userDefinedArray(v reflect.Value, tag string) error { + _, tagExists := mapperTag[tag] + if tagExists { + res, err := mapperTag[tag](v) + if err != nil { + return err + } + v.Set(reflect.ValueOf(res)) + return nil + } len := randomSliceAndMapSize() if shouldSetNil && len == 0 { v.Set(reflect.Zero(v.Type())) diff --git a/faker_test.go b/faker_test.go index 91ec250..f6612e8 100644 --- a/faker_test.go +++ b/faker_test.go @@ -908,11 +908,6 @@ type School struct { Location string } -type CustomTypeOverSlice []byte -type CustomThatUsesSlice struct { - UUID CustomTypeOverSlice `faker:"custom-type-over-slice"` -} - func TestExtend(t *testing.T) { // This test is to ensure that faker can be extended new providers @@ -966,9 +961,11 @@ func TestExtend(t *testing.T) { } }) - /** - * Before updates, this test would fail - */ + type CustomTypeOverSlice []byte + type CustomThatUsesSlice struct { + UUID CustomTypeOverSlice `faker:"custom-type-over-slice"` + } + t.Run("test-with-custom-slice-type", func(t *testing.T) { a := CustomThatUsesSlice{} err := AddProvider("custom-type-over-slice", func(v reflect.Value) (interface{}, error) { @@ -990,6 +987,40 @@ func TestExtend(t *testing.T) { } }) + type MyInt int + type Sample struct { + Value []MyInt `faker:"myint"` + } + + t.Run("test with type alias for int", func(t *testing.T) { + a := Sample{} + sliceLen := 10 + err := AddProvider("myint", func(v reflect.Value) (interface{}, error) { + s1 := rand.NewSource(time.Now().UnixNano()) + r1 := rand.New(s1) + r := make([]MyInt, sliceLen) + for i := range r { + r[i] = MyInt(r1.Intn(100)) + } + return r, nil + }) + + if err != nil { + t.Error("Expected Not Error, But Got: ", err) + } + + err = FakeData(&a) + + if err != nil { + t.Error("Expected Not Error, But Got: ", err) + } + + if len(a.Value) != sliceLen { + t.Errorf("Expected a slice of length %v but got %v", sliceLen, len(a.Value)) + } + + }) + } func TestTagAlreadyExists(t *testing.T) { From 8b03abbf539e0e42699ac31ad6f291640199d7a1 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 02:01:08 -0500 Subject: [PATCH 02/17] go fmt fixes; add support for 'oneof' tag for strings; add one basic test; --- example_custom_faker_test.go | 10 +++++----- faker.go | 8 +++++++- faker_test.go | 32 +++++++++++++++++++++++++++++--- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/example_custom_faker_test.go b/example_custom_faker_test.go index 160a1ca..dda4a9a 100644 --- a/example_custom_faker_test.go +++ b/example_custom_faker_test.go @@ -18,9 +18,9 @@ type CustomUUID []byte // Sample ... type Sample struct { - ID int64 `faker:"customIdFaker"` - Gondoruwo Gondoruwo `faker:"gondoruwo"` - Danger string `faker:"danger"` + ID int64 `faker:"customIdFaker"` + Gondoruwo Gondoruwo `faker:"gondoruwo"` + Danger string `faker:"danger"` UUID CustomUUID `faker:"customUUID"` } @@ -42,8 +42,8 @@ func CustomGenerator() { }) _ = faker.AddProvider("customUUID", func(v reflect.Value) (interface{}, error) { - s := []byte { - 0,8,7,2,3, + s := []byte{ + 0, 8, 7, 2, 3, } return s, nil }) diff --git a/faker.go b/faker.go index 197162a..1f25cf9 100644 --- a/faker.go +++ b/faker.go @@ -112,6 +112,7 @@ const ( BoundaryEnd = "boundary_end" Equals = "=" comma = "," + ONEOF = "oneof" ) var defaultTag = map[string]string{ @@ -767,7 +768,7 @@ func extractStringFromTag(tag string) (interface{}, error) { var err error strlen := randomStringLen strlng := &lang - if !strings.Contains(tag, Length) && !strings.Contains(tag, Language) { + if !strings.Contains(tag, Length) && !strings.Contains(tag, Language) && !strings.Contains(tag, ONEOF) { return nil, fmt.Errorf(ErrTagNotSupported, tag) } if strings.Contains(tag, Length) { @@ -783,6 +784,11 @@ func extractStringFromTag(tag string) (interface{}, error) { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) } } + if strings.Contains(tag, ONEOF) { + items := strings.Split(tag, ":") + choose := items[1:] + return choose[rand.Intn(len(choose))], nil + } res := randomString(strlen, strlng) return res, nil } diff --git a/faker_test.go b/faker_test.go index f6612e8..0606c69 100644 --- a/faker_test.go +++ b/faker_test.go @@ -969,7 +969,7 @@ func TestExtend(t *testing.T) { t.Run("test-with-custom-slice-type", func(t *testing.T) { a := CustomThatUsesSlice{} err := AddProvider("custom-type-over-slice", func(v reflect.Value) (interface{}, error) { - return []byte{0,1,2,3,4}, nil + return []byte{0, 1, 2, 3, 4}, nil }) if err != nil { @@ -982,14 +982,14 @@ func TestExtend(t *testing.T) { t.Error("Expected Not Error, But Got: ", err) } - if reflect.DeepEqual(a.UUID, []byte{0,1,2,3,4}) { + if reflect.DeepEqual(a.UUID, []byte{0, 1, 2, 3, 4}) { t.Error("UUID should equal test value") } }) type MyInt int type Sample struct { - Value []MyInt `faker:"myint"` + Value []MyInt `faker:"myint"` } t.Run("test with type alias for int", func(t *testing.T) { @@ -1251,6 +1251,32 @@ func TestUniqueFailure(t *testing.T) { } } +func TestOneOfTag(t *testing.T) { + + type CustomOne struct { + PaymentType string `faker:"oneof:credit card:paypal"` + } + + t.Run("creates only one of the desired values", func(t *testing.T) { + a := CustomOne{} + err := FakeData(&a) + if err != nil { + t.Errorf("expected no error, but got %v", err) + } + one := a.PaymentType == "credit card" + two := a.PaymentType == "paypal" + + if !one && !two { + t.Errorf( + "expected either %v or %v but got %v", + "credit card", + "paypal", + a.PaymentType, + ) + } + }) +} + // getStringLen for language independent string length func utfLen(value string) int { var r int From 86c668cec49527a6daae8c74f5074b7a06bd9f0a Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 02:44:20 -0500 Subject: [PATCH 03/17] add error check and error return for malformed tag; add extra tests to validate these scenarios --- faker.go | 6 +++++- faker_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/faker.go b/faker.go index 1f25cf9..06f466c 100644 --- a/faker.go +++ b/faker.go @@ -787,7 +787,11 @@ func extractStringFromTag(tag string) (interface{}, error) { if strings.Contains(tag, ONEOF) { items := strings.Split(tag, ":") choose := items[1:] - return choose[rand.Intn(len(choose))], nil + toRet := choose[rand.Intn(len(choose))] + if len(toRet) <= 1 { + return nil, fmt.Errorf(ErrWrongFormattedTag, tag) + } + return toRet, nil } res := randomString(strlen, strlng) return res, nil diff --git a/faker_test.go b/faker_test.go index 0606c69..d752846 100644 --- a/faker_test.go +++ b/faker_test.go @@ -1275,6 +1275,50 @@ func TestOneOfTag(t *testing.T) { ) } }) + + type CustomMulti struct { + PaymentType string `faker:"oneof:cc:check:paypal:bank account"` + } + t.Run("creates only one of the desired values from many", func(t *testing.T) { + a := CustomMulti{} + err := FakeData(&a) + if err != nil { + t.Errorf("expected no error, but got %v", err) + } + one := a.PaymentType == "cc" + two := a.PaymentType == "paypal" + three := a.PaymentType == "check" + four := a.PaymentType == "bank account" + + if !one && !two && !three && !four { + t.Errorf( + "expected either %v or %v or %v or %v but got %v", + "cc", + "paypal", + "check", + "bank account", + a.PaymentType, + ) + } + }) + + type CustomOneofWrong struct { + PaymentType string `faker:"oneof:"` + } + + t.Run("errors when tag is not used correctly", func(t *testing.T) { + a := CustomOneofWrong{} + err := FakeData(&a) + if err == nil { + t.Errorf("expected error, but got no error") + } + actual := err.Error() + expected := fmt.Sprintf(ErrWrongFormattedTag, "oneof:") + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + } // getStringLen for language independent string length From 61e4b06ec5ec6b56fc9967f1af08b4a87a3d1387 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 04:46:29 -0500 Subject: [PATCH 04/17] added support for a tag called oneof for strings and ints --- faker.go | 35 ++++++++++++++++--- faker_test.go | 94 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 6 deletions(-) diff --git a/faker.go b/faker.go index 06f466c..f7e708c 100644 --- a/faker.go +++ b/faker.go @@ -112,6 +112,7 @@ const ( BoundaryEnd = "boundary_end" Equals = "=" comma = "," + colon = ":" ONEOF = "oneof" ) @@ -231,6 +232,7 @@ var ( ErrWrongFormattedTag = "Tag \"%s\" is not written properly" ErrUnknownType = "Unknown Type" ErrNotSupportedTypeForTag = "Type is not supported by tag." + ErrUnsupportedTagArguments = "Tag arguments are not compatible with field type." ) // Compiled regexp @@ -785,11 +787,11 @@ func extractStringFromTag(tag string) (interface{}, error) { } } if strings.Contains(tag, ONEOF) { - items := strings.Split(tag, ":") + items := strings.Split(tag, colon) choose := items[1:] toRet := choose[rand.Intn(len(choose))] - if len(toRet) <= 1 { - return nil, fmt.Errorf(ErrWrongFormattedTag, tag) + if len(toRet) <= 1 || strings.Contains(tag, comma) { + return nil, fmt.Errorf(ErrUnsupportedTagArguments) } return toRet, nil } @@ -816,9 +818,34 @@ func extractLangFromTag(tag string) (*langRuneBoundary, error) { } func extractNumberFromTag(tag string, t reflect.Type) (interface{}, error) { - if !strings.Contains(tag, BoundaryStart) || !strings.Contains(tag, BoundaryEnd) { + hasOneOf := strings.Contains(tag, ONEOF) + hasBoundaryStart := strings.Contains(tag, BoundaryStart) + hasBoundaryEnd := strings.Contains(tag, BoundaryEnd) + usingOneOfTag := hasOneOf && (!hasBoundaryStart && !hasBoundaryEnd) + usingBoundariesTags := !hasOneOf && (hasBoundaryStart && hasBoundaryEnd) + if !usingOneOfTag && !usingBoundariesTags { return nil, fmt.Errorf(ErrTagNotSupported, tag) } + + // handling oneof tag + if usingOneOfTag { + values := strings.Split(tag, colon)[1:] + if len(values) < 1 || strings.Contains(tag, comma) { + return nil, fmt.Errorf(ErrUnsupportedTagArguments) + } + var numberValues []int + for _, i := range values { + j, err := strconv.Atoi(i) + if err != nil { + return nil, fmt.Errorf(ErrUnsupportedTagArguments) + } + numberValues = append(numberValues, j) + } + toRet := numberValues[rand.Intn(len(numberValues))] + return toRet, nil + } + + // handling boundary tags valuesStr := strings.SplitN(tag, comma, -1) if len(valuesStr) != 2 { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) diff --git a/faker_test.go b/faker_test.go index d752846..72cddff 100644 --- a/faker_test.go +++ b/faker_test.go @@ -1306,14 +1306,104 @@ func TestOneOfTag(t *testing.T) { PaymentType string `faker:"oneof:"` } - t.Run("errors when tag is not used correctly", func(t *testing.T) { + t.Run("errors when tag is not used correctly string no args", func(t *testing.T) { a := CustomOneofWrong{} err := FakeData(&a) if err == nil { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := fmt.Sprintf(ErrWrongFormattedTag, "oneof:") + expected := fmt.Sprintf(ErrUnsupportedTagArguments) + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + + type CustomOneofWrong2 struct { + PaymentType string `faker:"oneof:cc:check,bank"` + } + + t.Run("errors when tag is not used correctly string bad args", func(t *testing.T) { + a := CustomOneofWrong2{} + err := FakeData(&a) + if err == nil { + t.Errorf("expected error, but got no error") + } + actual := err.Error() + expected := fmt.Sprintf(ErrUnsupportedTagArguments) + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + + type CustomOneofInt1 struct { + Age int `faker:"oneof:16:18:21"` + } + + t.Run("should pick one of the numbers", func(t *testing.T) { + a := CustomOneofInt1{} + err := FakeData(&a) + if err != nil { + t.Errorf("expected no error, but got %v", err) + } + one := a.Age == 16 + two := a.Age == 18 + three := a.Age == 21 + actual := a.Age + if !one && !two && !three { + t.Errorf( + "expected either %v, %v, or %v, but got %v", + 16, 18, 21, actual, + ) + } + }) + + type CustomOneofWrongInt struct { + Age int `faker:"oneof:"` + } + + t.Run("errors when tag is not used correctly no args int", func(t *testing.T) { + a := CustomOneofWrongInt{} + err := FakeData(&a) + if err == nil { + t.Errorf("expected error, but got no error") + } + actual := err.Error() + expected := fmt.Sprintf(ErrUnsupportedTagArguments) + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + + type CustomOneofWrongInt2 struct { + Age int `faker:"oneof:15:18,35"` + } + + t.Run("errors when tag is not used correctly bad args int", func(t *testing.T) { + a := CustomOneofWrongInt2{} + err := FakeData(&a) + if err == nil { + t.Errorf("expected error, but got no error") + } + actual := err.Error() + expected := fmt.Sprintf(ErrUnsupportedTagArguments) + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + + type CustomOneofWrongInt3 struct { + Age int `faker:"oneof:15:18:oops"` + } + + t.Run("errors when tag is not used correctly mixed args int", func(t *testing.T) { + a := CustomOneofWrongInt3{} + err := FakeData(&a) + if err == nil { + t.Fatal("expected error, but got no error") + } + actual := err.Error() + expected := fmt.Sprintf(ErrUnsupportedTagArguments) if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } From 48bc5e760c02e36e67bf16456695ee5b240cdf47 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 21:14:43 -0500 Subject: [PATCH 05/17] update the creation of the custom types (not using the underlying types) --- example_custom_faker_test.go | 2 +- faker_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example_custom_faker_test.go b/example_custom_faker_test.go index dda4a9a..77e94f2 100644 --- a/example_custom_faker_test.go +++ b/example_custom_faker_test.go @@ -42,7 +42,7 @@ func CustomGenerator() { }) _ = faker.AddProvider("customUUID", func(v reflect.Value) (interface{}, error) { - s := []byte{ + s := CustomUUID{ 0, 8, 7, 2, 3, } return s, nil diff --git a/faker_test.go b/faker_test.go index 72cddff..270d63e 100644 --- a/faker_test.go +++ b/faker_test.go @@ -969,7 +969,7 @@ func TestExtend(t *testing.T) { t.Run("test-with-custom-slice-type", func(t *testing.T) { a := CustomThatUsesSlice{} err := AddProvider("custom-type-over-slice", func(v reflect.Value) (interface{}, error) { - return []byte{0, 1, 2, 3, 4}, nil + return CustomTypeOverSlice{0, 1, 2, 3, 4}, nil }) if err != nil { From 6601d1cfc0035f517d0b73bf98c698967863cabd Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 21:16:00 -0500 Subject: [PATCH 06/17] update examples; add const for convenience --- example_with_tags_test.go | 4 ++++ faker.go | 1 + 2 files changed, 5 insertions(+) diff --git a/example_with_tags_test.go b/example_with_tags_test.go index 4dd08a0..a34bea2 100644 --- a/example_with_tags_test.go +++ b/example_with_tags_test.go @@ -50,6 +50,8 @@ type SomeStructWithTags struct { UUIDHypenated string `faker:"uuid_hyphenated"` UUID string `faker:"uuid_digit"` Skip string `faker:"-"` + PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` + AccountID int `faker:"oneof:15,27,61"` } func Example_withTags() { @@ -104,6 +106,8 @@ func Example_withTags() { AmountWithCurrency: XBB 49257.100000, UUIDHypenated: 8f8e4463-9560-4a38-9b0c-ef24481e4e27, UUID: 90ea6479fd0e4940af741f0a87596b73, + PaymentMethod: paypal, + AccountID: 61 Skip: } */ diff --git a/faker.go b/faker.go index f7e708c..061d797 100644 --- a/faker.go +++ b/faker.go @@ -113,6 +113,7 @@ const ( Equals = "=" comma = "," colon = ":" + period = "." ONEOF = "oneof" ) From e6329a24a5fe5cc2381db32e5693bb391c8fdb12 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 21:16:43 -0500 Subject: [PATCH 07/17] go fmt --- faker_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/faker_test.go b/faker_test.go index 270d63e..cba6073 100644 --- a/faker_test.go +++ b/faker_test.go @@ -150,7 +150,7 @@ func (s SomeStruct) String() string { SFloat64:%v SBool: %v Struct: %v - Time: %v + Time: %v Stime: %v Currency: %v Amount: %v @@ -159,7 +159,7 @@ func (s SomeStruct) String() string { HyphenatedID: %v MapStringString: %v - MapStringStruct: %v + MapStringStruct: %v MapStringStructPointer: %v }`, s.Inta, s.Int8, s.Int16, s.Int32, s.Int64, s.Float32, s.Float64, s.UInta, From 0267e711b1f1c75cc5ec8aff9018db17ce8ad9c8 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 21:25:15 -0500 Subject: [PATCH 08/17] update documentation examples; add TrimSpace to ensure space between colon separated values doesn't matter; --- example_with_tags_test.go | 4 ++-- faker.go | 4 ++-- faker_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example_with_tags_test.go b/example_with_tags_test.go index a34bea2..6c3e5c4 100644 --- a/example_with_tags_test.go +++ b/example_with_tags_test.go @@ -50,8 +50,8 @@ type SomeStructWithTags struct { UUIDHypenated string `faker:"uuid_hyphenated"` UUID string `faker:"uuid_digit"` Skip string `faker:"-"` - PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` - AccountID int `faker:"oneof:15,27,61"` + PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` // support for oneof tag + AccountID int `faker:"oneof:15,27,61"` // support for oneof tag } func Example_withTags() { diff --git a/faker.go b/faker.go index 061d797..c08739c 100644 --- a/faker.go +++ b/faker.go @@ -794,7 +794,7 @@ func extractStringFromTag(tag string) (interface{}, error) { if len(toRet) <= 1 || strings.Contains(tag, comma) { return nil, fmt.Errorf(ErrUnsupportedTagArguments) } - return toRet, nil + return strings.TrimSpace(toRet), nil } res := randomString(strlen, strlng) return res, nil @@ -836,7 +836,7 @@ func extractNumberFromTag(tag string, t reflect.Type) (interface{}, error) { } var numberValues []int for _, i := range values { - j, err := strconv.Atoi(i) + j, err := strconv.Atoi(strings.TrimSpace(i)) if err != nil { return nil, fmt.Errorf(ErrUnsupportedTagArguments) } diff --git a/faker_test.go b/faker_test.go index cba6073..3eb4b0f 100644 --- a/faker_test.go +++ b/faker_test.go @@ -1277,7 +1277,7 @@ func TestOneOfTag(t *testing.T) { }) type CustomMulti struct { - PaymentType string `faker:"oneof:cc:check:paypal:bank account"` + PaymentType string `faker:"oneof: cc: check: paypal: bank account"` } t.Run("creates only one of the desired values from many", func(t *testing.T) { a := CustomMulti{} @@ -1337,7 +1337,7 @@ func TestOneOfTag(t *testing.T) { }) type CustomOneofInt1 struct { - Age int `faker:"oneof:16:18:21"` + Age int `faker:"oneof: 16: 18: 21"` } t.Run("should pick one of the numbers", func(t *testing.T) { From 2c3d65f89dd5507f6d7f93fe31240183079c0b05 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 21:29:25 -0500 Subject: [PATCH 09/17] update readme with caveat about 'oneof' tag --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1fd9682..ea35660 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Faker will generate you a fake data based on your Struct. [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/bxcodec/faker/blob/master/LICENSE) [![GoDoc](https://godoc.org/github.com/bxcodec/faker?status.svg)](https://godoc.org/github.com/bxcodec/faker) [![Go.Dev](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white)](https://pkg.go.dev/github.com/bxcodec/faker/v3?tab=doc) - + ## Index * [Support](#support) @@ -38,8 +38,8 @@ go get -u github.com/bxcodec/faker/v3 # Example --- - - - Using Struct's tag: + + - Using Struct's tag: - [basic tags: example_with_tags_test.go](/example_with_tags_test.go) - [length and bounds: example_with_tags_lenbounds_test.go](/example_with_tags_lenbounds_test.go) - [language: example_with_tags_lang_test.go](/example_with_tags_lang_test.go) @@ -47,7 +47,7 @@ go get -u github.com/bxcodec/faker/v3 - Custom Struct's tag (define your own faker data): [example_custom_faker_test.go](/example_custom_faker_test.go) - Without struct's tag: [example_without_tag_test.go](/example_without_tag_test.go) - Single Fake Data Function: [example_single_fake_data_test.go](/example_single_fake_data_test.go) - + ## DEMO --- @@ -94,6 +94,7 @@ Unfortunately this library has some limitation * It does not support the `map[interface{}]interface{}`, `map[any_type]interface{}` & `map[interface{}]any_type` data types. Once again, we cannot generate values for an unknown data type. * Custom types are not fully supported. However some custom types are already supported: we are still investigating how to do this the correct way. For now, if you use `faker`, it's safer not to use any custom types in order to avoid panics. * Some extra custom types can be supported IF AND ONLY IF extended with [AddProvider()](https://github.com/bxcodec/faker/blob/9169c33ae9926e5b8f8732909790ee20b10b736a/faker.go#L320) please see [example](example_custom_faker_test.go#L46) +* The `oneof` tag currently only supports `string` & `int`. Further support is coming soon. See [example](example_with_tags_test.go#L53) for usage. ## Contribution From c35890a3881b3f83740b26ec62b42c3627e0e0db Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 21:57:22 -0500 Subject: [PATCH 10/17] linter fixes --- faker.go | 2 +- faker_test.go | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/faker.go b/faker.go index c08739c..08e647d 100644 --- a/faker.go +++ b/faker.go @@ -113,8 +113,8 @@ const ( Equals = "=" comma = "," colon = ":" - period = "." ONEOF = "oneof" + // period = "." ) var defaultTag = map[string]string{ diff --git a/faker_test.go b/faker_test.go index 3eb4b0f..45f896c 100644 --- a/faker_test.go +++ b/faker_test.go @@ -1313,7 +1313,7 @@ func TestOneOfTag(t *testing.T) { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := fmt.Sprintf(ErrUnsupportedTagArguments) + expected := ErrUnsupportedTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } @@ -1330,7 +1330,7 @@ func TestOneOfTag(t *testing.T) { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := fmt.Sprintf(ErrUnsupportedTagArguments) + expected := ErrUnsupportedTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } @@ -1369,7 +1369,7 @@ func TestOneOfTag(t *testing.T) { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := fmt.Sprintf(ErrUnsupportedTagArguments) + expected := ErrUnsupportedTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } @@ -1386,7 +1386,7 @@ func TestOneOfTag(t *testing.T) { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := fmt.Sprintf(ErrUnsupportedTagArguments) + expected := ErrUnsupportedTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } @@ -1403,7 +1403,7 @@ func TestOneOfTag(t *testing.T) { t.Fatal("expected error, but got no error") } actual := err.Error() - expected := fmt.Sprintf(ErrUnsupportedTagArguments) + expected := ErrUnsupportedTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } From 1a9fe1ffc2ff87c3455d5e6b01da737b6c9b0464 Mon Sep 17 00:00:00 2001 From: altair Date: Sat, 6 Jun 2020 22:04:28 -0500 Subject: [PATCH 11/17] clean up duplicate if conditions --- faker.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/faker.go b/faker.go index 08e647d..9937276 100644 --- a/faker.go +++ b/faker.go @@ -771,7 +771,8 @@ func extractStringFromTag(tag string) (interface{}, error) { var err error strlen := randomStringLen strlng := &lang - if !strings.Contains(tag, Length) && !strings.Contains(tag, Language) && !strings.Contains(tag, ONEOF) { + isOneOfTag := strings.Contains(tag, ONEOF) + if !strings.Contains(tag, Length) && !strings.Contains(tag, Language) && !isOneOfTag { return nil, fmt.Errorf(ErrTagNotSupported, tag) } if strings.Contains(tag, Length) { @@ -787,7 +788,7 @@ func extractStringFromTag(tag string) (interface{}, error) { return nil, fmt.Errorf(ErrWrongFormattedTag, tag) } } - if strings.Contains(tag, ONEOF) { + if isOneOfTag { items := strings.Split(tag, colon) choose := items[1:] toRet := choose[rand.Intn(len(choose))] From cca5c35820b9b648e73f06f64baf536b14307824 Mon Sep 17 00:00:00 2001 From: altair Date: Sun, 7 Jun 2020 09:58:16 -0500 Subject: [PATCH 12/17] finish fixing merge conflicts --- example_custom_faker_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/example_custom_faker_test.go b/example_custom_faker_test.go index b720fd4..77e94f2 100644 --- a/example_custom_faker_test.go +++ b/example_custom_faker_test.go @@ -42,11 +42,7 @@ func CustomGenerator() { }) _ = faker.AddProvider("customUUID", func(v reflect.Value) (interface{}, error) { -<<<<<<< HEAD s := CustomUUID{ -======= - s := []byte{ ->>>>>>> origin/master 0, 8, 7, 2, 3, } return s, nil From cfb800499c0f967068ea25e4f2a1a84be49f00be Mon Sep 17 00:00:00 2001 From: altair Date: Sun, 7 Jun 2020 11:51:12 -0500 Subject: [PATCH 13/17] update example to be correct. use ':' to separate arguments not ',' --- example_with_tags_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_with_tags_test.go b/example_with_tags_test.go index 6c3e5c4..f103eb6 100644 --- a/example_with_tags_test.go +++ b/example_with_tags_test.go @@ -51,7 +51,7 @@ type SomeStructWithTags struct { UUID string `faker:"uuid_digit"` Skip string `faker:"-"` PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` // support for oneof tag - AccountID int `faker:"oneof:15,27,61"` // support for oneof tag + AccountID int `faker:"oneof:15:27:61"` // support for oneof tag } func Example_withTags() { From 210f21ca52f5b5ebe67d8d37e2f73a8dd1e087d5 Mon Sep 17 00:00:00 2001 From: altair Date: Sun, 7 Jun 2020 11:52:59 -0500 Subject: [PATCH 14/17] update example comments for clarity --- example_with_tags_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example_with_tags_test.go b/example_with_tags_test.go index f103eb6..3dcea99 100644 --- a/example_with_tags_test.go +++ b/example_with_tags_test.go @@ -50,8 +50,8 @@ type SomeStructWithTags struct { UUIDHypenated string `faker:"uuid_hyphenated"` UUID string `faker:"uuid_digit"` Skip string `faker:"-"` - PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` // support for oneof tag - AccountID int `faker:"oneof:15:27:61"` // support for oneof tag + PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` // oneof will randomly pick one of the colon-separated values supplied in the tag + AccountID int `faker:"oneof:15:27:61"` // use colons to separate the values for now. Future support for other separator characters may be added } func Example_withTags() { From 0c1fccd9c3b00c571c31ad39a450b9aec459ea8f Mon Sep 17 00:00:00 2001 From: altair Date: Sun, 7 Jun 2020 14:03:16 -0500 Subject: [PATCH 15/17] ensure comma separated arguments to oneof tag; add several more tests and a new error; --- faker.go | 34 +++++++++++---- faker_test.go | 112 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/faker.go b/faker.go index 79d2428..95f628b 100644 --- a/faker.go +++ b/faker.go @@ -237,6 +237,8 @@ var ( ErrUnknownType = "Unknown Type" ErrNotSupportedTypeForTag = "Type is not supported by tag." ErrUnsupportedTagArguments = "Tag arguments are not compatible with field type." + ErrDuplicateSeparator = "Duplicate separator for tag arguments." + ErrNotEnoughTagArguments = "Not enough arguments for tag." ) // Compiled regexp @@ -804,11 +806,21 @@ func extractStringFromTag(tag string) (interface{}, error) { } if isOneOfTag { items := strings.Split(tag, colon) - choose := items[1:] - toRet := choose[rand.Intn(len(choose))] - if len(toRet) <= 1 || strings.Contains(tag, comma) { + if len(items) <= 1 { return nil, fmt.Errorf(ErrUnsupportedTagArguments) } + argsList := items[1:] + if len(argsList) != 1 { + return nil, fmt.Errorf(ErrUnsupportedTagArguments) + } + if strings.Contains(argsList[0], ",,") { + return nil, fmt.Errorf(ErrDuplicateSeparator) + } + args := strings.Split(argsList[0], comma) + if len(args) < 2 { + return nil, fmt.Errorf(ErrNotEnoughTagArguments) + } + toRet := args[rand.Intn(len(args))] return strings.TrimSpace(toRet), nil } res := randomString(strlen, strlng) @@ -845,13 +857,21 @@ func extractNumberFromTag(tag string, t reflect.Type) (interface{}, error) { // handling oneof tag if usingOneOfTag { - values := strings.Split(tag, colon)[1:] - if len(values) < 1 || strings.Contains(tag, comma) { + argsList := strings.Split(tag, colon)[1:] + if len(argsList) != 1 { return nil, fmt.Errorf(ErrUnsupportedTagArguments) } + if strings.Contains(argsList[0], ",,") { + return nil, fmt.Errorf(ErrDuplicateSeparator) + } + args := strings.Split(argsList[0], comma) + if len(args) < 2 { + return nil, fmt.Errorf(ErrNotEnoughTagArguments) + } var numberValues []int - for _, i := range values { - j, err := strconv.Atoi(strings.TrimSpace(i)) + for _, i := range args { + k := strings.TrimSpace(i) + j, err := strconv.Atoi(k) if err != nil { return nil, fmt.Errorf(ErrUnsupportedTagArguments) } diff --git a/faker_test.go b/faker_test.go index ae01f89..bfcc8f2 100644 --- a/faker_test.go +++ b/faker_test.go @@ -1256,12 +1256,12 @@ func TestUniqueFailure(t *testing.T) { func TestOneOfTag(t *testing.T) { - type CustomOne struct { - PaymentType string `faker:"oneof:credit card:paypal"` + type CustomOneString struct { + PaymentType string `faker:"oneof: credit card, paypal"` } - t.Run("creates only one of the desired values", func(t *testing.T) { - a := CustomOne{} + t.Run("creates one of the desired string values", func(t *testing.T) { + a := CustomOneString{} err := FakeData(&a) if err != nil { t.Errorf("expected no error, but got %v", err) @@ -1279,11 +1279,11 @@ func TestOneOfTag(t *testing.T) { } }) - type CustomMulti struct { - PaymentType string `faker:"oneof: cc: check: paypal: bank account"` + type CustomMultiString struct { + PaymentType string `faker:"oneof: cc, check, paypal, bank account"` } - t.Run("creates only one of the desired values from many", func(t *testing.T) { - a := CustomMulti{} + t.Run("creates only one of the desired string values from many", func(t *testing.T) { + a := CustomMultiString{} err := FakeData(&a) if err != nil { t.Errorf("expected no error, but got %v", err) @@ -1305,29 +1305,29 @@ func TestOneOfTag(t *testing.T) { } }) - type CustomOneofWrong struct { + type CustomOneofWrongString struct { PaymentType string `faker:"oneof:"` } t.Run("errors when tag is not used correctly string no args", func(t *testing.T) { - a := CustomOneofWrong{} + a := CustomOneofWrongString{} err := FakeData(&a) if err == nil { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := ErrUnsupportedTagArguments + expected := ErrNotEnoughTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } }) - type CustomOneofWrong2 struct { - PaymentType string `faker:"oneof:cc:check,bank"` + type CustomOneofWrongString2 struct { + PaymentType string `faker:"oneof: cc: check, bank"` } - t.Run("errors when tag is not used correctly string bad args", func(t *testing.T) { - a := CustomOneofWrong2{} + t.Run("errors when tag is not used correctly string invalid argument separator", func(t *testing.T) { + a := CustomOneofWrongString2{} err := FakeData(&a) if err == nil { t.Errorf("expected error, but got no error") @@ -1339,11 +1339,45 @@ func TestOneOfTag(t *testing.T) { } }) + type CustomWrongString3 struct { + PaymentType string `faker:"oneof: credit card"` + } + + t.Run("errors when tag is not used correctly string only one argument", func(t *testing.T) { + a := CustomWrongString3{} + err := FakeData(&a) + if err == nil { + t.Errorf("expected error, but got no error") + } + actual := err.Error() + expected := ErrNotEnoughTagArguments + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + + type CustomWrongString4 struct { + PaymentType string `faker:"oneof: ,,,cc, credit card,,"` + } + + t.Run("errors when tag is not used correctly string duplicate separator", func(t *testing.T) { + a := CustomWrongString4{} + err := FakeData(&a) + if err == nil { + t.Errorf("expected error, but got no error") + } + actual := err.Error() + expected := ErrDuplicateSeparator + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + type CustomOneofInt1 struct { - Age int `faker:"oneof: 16: 18: 21"` + Age int `faker:"oneof: 16, 18, 21"` } - t.Run("should pick one of the numbers", func(t *testing.T) { + t.Run("should pick one of the number args", func(t *testing.T) { a := CustomOneofInt1{} err := FakeData(&a) if err != nil { @@ -1372,17 +1406,17 @@ func TestOneOfTag(t *testing.T) { t.Errorf("expected error, but got no error") } actual := err.Error() - expected := ErrUnsupportedTagArguments + expected := ErrNotEnoughTagArguments if actual != expected { t.Errorf("expected %v, but got %v", expected, actual) } }) type CustomOneofWrongInt2 struct { - Age int `faker:"oneof:15:18,35"` + Age int `faker:"oneof: 15: 18, 35"` } - t.Run("errors when tag is not used correctly bad args int", func(t *testing.T) { + t.Run("errors when tag is not used correctly int invalid argument separator", func(t *testing.T) { a := CustomOneofWrongInt2{} err := FakeData(&a) if err == nil { @@ -1396,10 +1430,10 @@ func TestOneOfTag(t *testing.T) { }) type CustomOneofWrongInt3 struct { - Age int `faker:"oneof:15:18:oops"` + Age int `faker:"oneof: 15, 18, oops"` } - t.Run("errors when tag is not used correctly mixed args int", func(t *testing.T) { + t.Run("errors when tag is not used correctly int invalid argument type", func(t *testing.T) { a := CustomOneofWrongInt3{} err := FakeData(&a) if err == nil { @@ -1412,6 +1446,40 @@ func TestOneOfTag(t *testing.T) { } }) + type CustomWrongInt4 struct { + Age int `faker:"oneof: 15"` + } + + t.Run("errors when tag is not used correctly int only one argument", func(t *testing.T) { + a := CustomWrongInt4{} + err := FakeData(&a) + if err == nil { + t.Fatal("expected error, but got no error") + } + actual := err.Error() + expected := ErrNotEnoughTagArguments + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + + type CustomWrongInt5 struct { + Age int `faker:"oneof: 15,,16,17"` + } + + t.Run("errors when tag is not used correctly int only one argument", func(t *testing.T) { + a := CustomWrongInt5{} + err := FakeData(&a) + if err == nil { + t.Fatal("expected error, but got no error") + } + actual := err.Error() + expected := ErrDuplicateSeparator + if actual != expected { + t.Errorf("expected %v, but got %v", expected, actual) + } + }) + } // getStringLen for language independent string length From fbde531fe736183a953b5d499f349f7e2cd7396e Mon Sep 17 00:00:00 2001 From: altair Date: Sun, 7 Jun 2020 14:04:46 -0500 Subject: [PATCH 16/17] update documentation examples --- example_with_tags_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example_with_tags_test.go b/example_with_tags_test.go index 3dcea99..e392390 100644 --- a/example_with_tags_test.go +++ b/example_with_tags_test.go @@ -50,8 +50,8 @@ type SomeStructWithTags struct { UUIDHypenated string `faker:"uuid_hyphenated"` UUID string `faker:"uuid_digit"` Skip string `faker:"-"` - PaymentMethod string `faker:"oneof:cc:paypal:check:money order"` // oneof will randomly pick one of the colon-separated values supplied in the tag - AccountID int `faker:"oneof:15:27:61"` // use colons to separate the values for now. Future support for other separator characters may be added + PaymentMethod string `faker:"oneof: cc, paypal, check, money order"` // oneof will randomly pick one of the comma-separated values supplied in the tag + AccountID int `faker:"oneof: 15, 27, 61"` // use commas to separate the values for now. Future support for other separator characters may be added } func Example_withTags() { From 4c44fc2118de75192ddf24130e8021cf6a1d540e Mon Sep 17 00:00:00 2001 From: altair Date: Sun, 7 Jun 2020 14:25:32 -0500 Subject: [PATCH 17/17] remove useless if statement that is never true --- faker.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/faker.go b/faker.go index 95f628b..0e00bcf 100644 --- a/faker.go +++ b/faker.go @@ -806,9 +806,6 @@ func extractStringFromTag(tag string) (interface{}, error) { } if isOneOfTag { items := strings.Split(tag, colon) - if len(items) <= 1 { - return nil, fmt.Errorf(ErrUnsupportedTagArguments) - } argsList := items[1:] if len(argsList) != 1 { return nil, fmt.Errorf(ErrUnsupportedTagArguments)