Skip to content

Commit

Permalink
update rand code
Browse files Browse the repository at this point in the history
  • Loading branch information
dxyinme committed Jan 12, 2024
1 parent 2a001a7 commit f5039fe
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 36 deletions.
50 changes: 39 additions & 11 deletions randx/rand_code.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ package randx
import (
"errors"
"math/rand"

"github.com/ecodeclub/ekit/tuple/pair"
)

var (
ErrTypeNotSupported = errors.New("ekit:不支持的类型")
ErrTypeNotSupported = errors.New("ekit:不支持的类型")
ErrLengthLessThanZero = errors.New("ekit:长度必须大于0")
// deprecated
ERRTYPENOTSUPPORTTED = ErrTypeNotSupported
)
Expand All @@ -36,8 +39,10 @@ const (
// 大写字母
TYPE_UPPER TYPE = 1 << 2
TYPE_CAPITAL TYPE = TYPE_UPPER
// 特殊符号
TYPE_SPECIAL TYPE = 1 << 3
// 混合类型
TYPE_MIXED = (TYPE_DIGIT | TYPE_UPPER | TYPE_LOWER)
TYPE_MIXED = (TYPE_DIGIT | TYPE_UPPER | TYPE_LOWER | TYPE_SPECIAL)

// 数字字符组
CHARSET_DIGIT = "0123456789"
Expand All @@ -47,29 +52,52 @@ const (
// 大写字母字符组
CHARSET_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
CHARSET_CAPITAL = CHARSET_UPPER
// 特殊字符数组
CHARSET_SPECIAL = " ~!@#$%^&*()_+-=[]{};'\\:\"|,./<>?"
)

var (
// 只限于randx包内部使用
typeCharSetPair = []pair.Pair[TYPE, string]{
pair.NewPair(TYPE_DIGIT, CHARSET_DIGIT),
pair.NewPair(TYPE_LOWER, CHARSET_LOWER),
pair.NewPair(TYPE_UPPER, CHARSET_UPPER),
pair.NewPair(TYPE_SPECIAL, CHARSET_SPECIAL),
}
)

// RandCode 根据传入的长度和类型生成随机字符串,这个方法目前可以生成数字、字母、数字+字母的随机字符串
// RandCode 根据传入的长度和类型生成随机字符串
func RandCode(length int, typ TYPE) (string, error) {
if typ > TYPE_MIXED {
return "", ErrTypeNotSupported
}
charset := ""
appendIfHas := func(typBase TYPE, baseCharset string) {
if (typ & typBase) == typBase {
charset += baseCharset
for _, p := range typeCharSetPair {
if (typ & p.Key) == p.Key {
charset += p.Value
}
}
appendIfHas(TYPE_DIGIT, CHARSET_DIGIT)
appendIfHas(TYPE_UPPER, CHARSET_UPPER)
appendIfHas(TYPE_LOWER, CHARSET_LOWER)
return RandStrByCharset(length, charset)
}

// 根据传入的长度和字符集生成随机字符串
func RandStrByCharset(length int, charset string) (string, error) {
if length < 0 {
return "", ErrLengthLessThanZero
}
charsetSize := len(charset)
if charsetSize == 0 {
return "", ErrTypeNotSupported
}
bits := 1
return generate(charset, length, getFirstMask(charsetSize)), nil
}

func getFirstMask(charsetSize int) int {
bits := 0
for charsetSize > ((1 << bits) - 1) {
bits++
}
return generate(charset, length, bits), nil
return bits
}

// generate 根据传入的随机源和长度生成随机字符串,一次随机,多次使用
Expand Down
164 changes: 139 additions & 25 deletions randx/rand_code_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
package randx_test

import (
"errors"
"regexp"
"strings"
"testing"

"github.com/ecodeclub/ekit/randx"
"github.com/stretchr/testify/assert"
)

func TestRandCode(t *testing.T) {
Expand All @@ -32,56 +33,169 @@ func TestRandCode(t *testing.T) {
}{
{
name: "数字验证码",
length: 8,
length: 100,
typ: randx.TYPE_DIGIT,
wantMatch: "^[0-9]+$",
wantErr: nil,
}, {
},
{
name: "小写字母验证码",
length: 8,
length: 100,
typ: randx.TYPE_LETTER,
wantMatch: "^[a-z]+$",
wantErr: nil,
}, {
},
{
name: "数字+小写字母验证码",
length: 100,
typ: randx.TYPE_DIGIT | randx.TYPE_LOWER,
wantMatch: "^[a-z0-9]+$",
wantErr: nil,
},
{
name: "数字+大写字母验证码",
length: 100,
typ: randx.TYPE_DIGIT | randx.TYPE_UPPER,
wantMatch: "^[A-Z0-9]+$",
wantErr: nil,
},
{
name: "大写字母验证码",
length: 8,
length: 100,
typ: randx.TYPE_CAPITAL,
wantMatch: "^[A-Z]+$",
wantErr: nil,
}, {
name: "混合验证码",
length: 8,
typ: randx.TYPE_MIXED,
},
{
name: "大小写字母验证码",
length: 100,
typ: randx.TYPE_UPPER | randx.TYPE_LOWER,
wantMatch: "^[a-zA-Z]+$",
wantErr: nil,
},
{
name: "数字+大小写字母验证码",
length: 100,
typ: randx.TYPE_DIGIT | randx.TYPE_UPPER | randx.TYPE_LOWER,
wantMatch: "^[0-9a-zA-Z]+$",
wantErr: nil,
}, {
name: "未定义类型",
length: 8,
},
{
name: "所有类型验证",
length: 100,
typ: randx.TYPE_MIXED,
wantMatch: "^[\\S\\s]*$",
wantErr: nil,
},
{
name: "未定义类型(超过范围)",
length: 100,
typ: randx.TYPE_MIXED + 1,
wantMatch: "",
wantErr: randx.ErrTypeNotSupported,
},
{
name: "未定义类型(0)",
length: 100,
typ: 0,
wantMatch: "",
wantErr: randx.ErrTypeNotSupported,
},
{
name: "长度小于0",
length: -1,
typ: 0,
wantMatch: "",
wantErr: randx.ErrLengthLessThanZero,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
code, err := randx.RandCode(tc.length, tc.typ)
if err != nil {
if !errors.Is(err, tc.wantErr) {
t.Errorf("unexpected error: %v", err)
}
assert.Equal(t, tc.wantErr, err)
} else {
assert.Lenf(
t,
code,
tc.length,
"expected length: %d but got length:%d",
tc.length, len(code))

matched, err := regexp.MatchString(tc.wantMatch, code)
assert.Nil(t, err)
assert.Truef(t, matched, "expected %s but got %s", tc.wantMatch, code)
}
})
}
}

func TestRandStrByCharset(t *testing.T) {
matchFunc := func(str, charset string) bool {
for _, c := range str {
if !strings.Contains(charset, string(c)) {
return false
}
}
return true
}
testCases := []struct {
name string
length int
charset string
wantErr error
}{
{
name: "长度小于0",
length: -1,
charset: "123",
wantErr: randx.ErrLengthLessThanZero,
},
{
name: "随机字符串测试",
length: 100,
charset: "2rg248ry227t@@",
wantErr: nil,
},
{
name: "随机字符串测试",
length: 100,
charset: "2rg248ry227t@&*($.!",
wantErr: nil,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
code, err := randx.RandStrByCharset(tc.length, tc.charset)
if err != nil {
assert.Equal(t, tc.wantErr, err)
} else {
//长度检验
if len(code) != tc.length {
t.Errorf("expected length: %d but got length:%d ", tc.length, len(code))
}
//模式检验
matched, _ := regexp.MatchString(tc.wantMatch, code)
if !matched {
t.Errorf("expected %s but got %s", tc.wantMatch, code)
}
assert.Lenf(
t,
code,
tc.length,
"expected length: %d but got length:%d",
tc.length, len(code))
assert.True(t, matchFunc(code, tc.charset))
}
})
}
}

// goos: linux
// goarch: amd64
// pkg: github.com/ecodeclub/ekit/randx
// cpu: 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
// BenchmarkRandCode_MIXED/length=1000000-8 1000000000 0.004584 ns/op 0 B/op 0 allocs/op
func BenchmarkRandCode_MIXED(b *testing.B) {
b.Run("length=1000000", func(b *testing.B) {
n := 1000000
b.StartTimer()
res, err := randx.RandCode(n, randx.TYPE_MIXED)
b.StopTimer()
assert.Nil(b, err)
assert.Len(b, res, n)
})
}

0 comments on commit f5039fe

Please sign in to comment.