Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Commit

Permalink
fix: exluded characters in random string generator (#111)
Browse files Browse the repository at this point in the history
* exluded characters in random string generator

* randomString returning error

* add panic in case of string generation failture

* fix panic for Username

Co-authored-by: o.musin <o.musin@tinkoff.ru>
  • Loading branch information
Oleg and o.musin committed Jun 17, 2020
1 parent 3f13091 commit 02fc1b2
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 37 deletions.
53 changes: 38 additions & 15 deletions faker.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ var (
generateUniqueValues = false
// Unique values are kept in memory so the generator retries if the value already exists
uniqueValues = map[string][]interface{}{}
lang = LangENG
// Lang is selected language for random string generator
lang = LangENG
// How much tries for generating random string
maxGenerateStringRetries = 1000000
)

type numberBoundary struct {
Expand All @@ -41,18 +44,19 @@ type numberBoundary struct {
}

type langRuneBoundary struct {
start rune
end rune
start rune
end rune
exclude []rune
}

// Language rune boundaries here
var (
// LangENG is for english language
LangENG = langRuneBoundary{65, 122}
LangENG = langRuneBoundary{65, 122, []rune{91, 92, 93, 94, 95, 96}}
// LangCHI is for chinese language
LangCHI = langRuneBoundary{19968, 40869}
LangCHI = langRuneBoundary{19968, 40869, nil}
// LangRUS is for russian language
LangRUS = langRuneBoundary{1025, 1105}
LangRUS = langRuneBoundary{1025, 1105, nil}
)

// Supported tags
Expand Down Expand Up @@ -246,9 +250,9 @@ var (

// Compiled regexp
var (
findLangReg *regexp.Regexp
findLenReg *regexp.Regexp
findSliceLenReg *regexp.Regexp
findLangReg *regexp.Regexp
findLenReg *regexp.Regexp
findSliceLenReg *regexp.Regexp
)

func init() {
Expand Down Expand Up @@ -479,8 +483,8 @@ func getValue(a interface{}) (reflect.Value, error) {
}

case reflect.String:
res := randomString(randomStringLen, &lang)
return reflect.ValueOf(res), nil
res, err := randomString(randomStringLen, &lang)
return reflect.ValueOf(res), err
case reflect.Slice:
len := randomSliceAndMapSize()
if shouldSetNil && len == 0 {
Expand Down Expand Up @@ -848,8 +852,8 @@ func extractStringFromTag(tag string) (interface{}, error) {
toRet := args[rand.Intn(len(args))]
return strings.TrimSpace(toRet), nil
}
res := randomString(strlen, strlng)
return res, nil
res, err := randomString(strlen, strlng)
return res, err
}

func extractLangFromTag(tag string) (*langRuneBoundary, error) {
Expand Down Expand Up @@ -1046,15 +1050,34 @@ func extractNumberFromText(text string) (int, error) {
return strconv.Atoi(texts[1])
}

func randomString(n int, lang *langRuneBoundary) string {
func randomString(n int, lang *langRuneBoundary) (string, error) {
b := make([]rune, 0)
set := make(map[rune]struct{})
if lang.exclude != nil {
for _, s := range lang.exclude {
set[s] = struct{}{}
}
}

counter := 0
for i := 0; i < n; {
randRune := rune(rand.Intn(int(lang.end-lang.start)) + int(lang.start))
for slice.ContainsRune(set, randRune) {
if counter++; counter >= maxGenerateStringRetries {
return "", errors.New("Max number of string generation retries exhausted")
}
randRune = rune(rand.Intn(int(lang.end-lang.start)) + int(lang.start))
_, ok := set[randRune]
if !ok {
break
}
}
b = append(b, randRune)
i++
}

return string(b)
k := string(b)
return k, nil
}

// randomIntegerWithBoundary returns a random integer between input start and end boundary. [start, end)
Expand Down
6 changes: 3 additions & 3 deletions faker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const (
)

var (
langCorrectTagsMap = map[langRuneBoundary]string{LangENG: "lang=eng", LangCHI: "lang=chi", LangRUS: "lang=rus"}
langCorrectTagsMap = map[string]langRuneBoundary{"lang=eng": LangENG, "lang=chi": LangCHI, "lang=rus": LangRUS}
langUncorrectTags = [3]string{"lang=", "lang", "lng=eng"}

lenCorrectTags = [3]string{"len=4", "len=5", "len=10"}
Expand Down Expand Up @@ -597,10 +597,10 @@ func TestExtractingLangFromTag(t *testing.T) {
var err error
var lng *langRuneBoundary
for k, v := range langCorrectTagsMap {
if lng, err = extractLangFromTag(v); err != nil {
if lng, err = extractLangFromTag(k); err != nil {
t.Error(err.Error())
}
if k != *lng {
if !reflect.DeepEqual(v, *lng) {
t.Errorf("Got %v lang rune range, but expected %v", lng, k)
}
}
Expand Down
78 changes: 59 additions & 19 deletions internet.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,32 @@ type Networker interface {
// Internet struct
type Internet struct{}

func (internet Internet) email() string {
return randomString(7, &LangENG) + "@" + randomString(5, &LangENG) + "." + randomElementFromSliceString(tld)
func (internet Internet) email() (string, error) {
var err error
var emailName, emailDomain string
if emailName, err = randomString(7, &LangENG); err != nil {
return "", err
}
if emailDomain, err = randomString(7, &LangENG); err != nil {
return "", err
}
return (emailName + "@" + emailDomain + "." + randomElementFromSliceString(tld)), nil
}

// Email generates random email id
func (internet Internet) Email(v reflect.Value) (interface{}, error) {
return internet.email(), nil
return internet.email()
}

// Email get email randomly in string
func Email() string {
return singleFakeData(EmailTag, func() interface{} {
i := Internet{}
return i.email()
r, err := i.email()
if err != nil {
panic(err.Error())
}
return r
}).(string)
}

Expand All @@ -94,59 +106,83 @@ func MacAddress() string {
}).(string)
}

func (internet Internet) domainName() string {
return randomString(7, &LangENG) + "." + randomElementFromSliceString(tld)
func (internet Internet) domainName() (string, error) {
domainPart, err := randomString(7, &LangENG)
if err != nil {
return "", err
}
return (domainPart + "." + randomElementFromSliceString(tld)), nil
}

// DomainName generates random domain name
func (internet Internet) DomainName(v reflect.Value) (interface{}, error) {
return internet.domainName(), nil
return internet.domainName()
}

// DomainName get email domain name in string
func DomainName() string {
return singleFakeData(DomainNameTag, func() interface{} {
i := Internet{}
return i.domainName()
d, err := i.domainName()
if err != nil {
panic(err.Error())
}
return d
}).(string)
}

func (internet Internet) url() string {
func (internet Internet) url() (string, error) {
format := randomElementFromSliceString(urlFormats)
countVerbs := strings.Count(format, "%s")
d, err := internet.domainName()
if err != nil {
return "", nil
}
if countVerbs == 1 {
return fmt.Sprintf(format, internet.domainName())
return fmt.Sprintf(format, d), nil
}
u, err := internet.username()
if err != nil {
return "", err
}
return fmt.Sprintf(format, internet.domainName(), internet.username())
return fmt.Sprintf(format, d, u), nil
}

// URL generates random URL standardized in urlFormats const
func (internet Internet) URL(v reflect.Value) (interface{}, error) {
return internet.url(), nil
return internet.url()
}

// URL get Url randomly in string
func URL() string {
return singleFakeData(URLTag, func() interface{} {
i := Internet{}
return i.url()
u, err := i.url()
if err != nil {
panic(err.Error())
}
return u
}).(string)
}

func (internet Internet) username() string {
func (internet Internet) username() (string, error) {
return randomString(7, &LangENG)
}

// UserName generates random username
func (internet Internet) UserName(v reflect.Value) (interface{}, error) {
return internet.username(), nil
return internet.username()
}

// Username get username randomly in string
func Username() string {
return singleFakeData(UserNameTag, func() interface{} {
i := Internet{}
return i.username()
u, err := i.username()
if err != nil {
panic(err.Error())
}
return u
}).(string)
}

Expand Down Expand Up @@ -194,19 +230,23 @@ func IPv6() string {
}).(string)
}

func (internet Internet) password() string {
func (internet Internet) password() (string, error) {
return randomString(50, &LangENG)
}

// Password returns a hashed password
func (internet Internet) Password(v reflect.Value) (interface{}, error) {
return internet.password(), nil
return internet.password()
}

// Password get password randomly in string
func Password() string {
return singleFakeData(PASSWORD, func() interface{} {
i := Internet{}
return i.password()
p, err := i.password()
if err != nil {
panic(err.Error())
}
return p
}).(string)
}
6 changes: 6 additions & 0 deletions support/slice/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ func Contains(slice []string, item string) bool {
return ok
}

// ContainsRune Check item in map rune type
func ContainsRune(set map[rune]struct{}, item rune) bool {
_, ok := set[item]
return ok
}

// ContainsValue check if value exists in slice, no matter its type
func ContainsValue(slice []interface{}, value interface{}) bool {
for _, v := range slice {
Expand Down

0 comments on commit 02fc1b2

Please sign in to comment.