Compare commits

..

25 Commits

Author SHA1 Message Date
kevin
6749c5b94a add more tests 2020-10-04 17:52:54 +08:00
刘青
e66cca3710 breaker: remover useless code (#114) 2020-10-04 16:25:26 +08:00
kevin
f90c0aa98e update wechat qrcode 2020-10-04 10:14:08 +08:00
kevin
f00b5416a3 update codecov settings 2020-10-03 23:09:29 +08:00
kevin
f49694d6b6 fix data race 2020-10-02 22:41:25 +08:00
kevin
d809bf2dca add more tests 2020-10-02 22:37:15 +08:00
kevin
44ae5463bc add more tests 2020-10-02 09:00:25 +08:00
kevin
40dbd722d7 add more tests 2020-10-01 23:29:49 +08:00
kevin
709574133b add more tests 2020-10-01 23:22:53 +08:00
kevin
cb1c593108 remove markdown linter 2020-10-01 21:11:19 +08:00
kevin
6ecf575c00 add more tests 2020-10-01 20:58:12 +08:00
kevin
b8fcdd5460 add more tests 2020-10-01 17:50:53 +08:00
kevin
ce42281568 add more tests 2020-10-01 17:27:21 +08:00
kevin
40230d79e7 fix data race 2020-10-01 16:58:07 +08:00
kevin
ba7851795b add more tests 2020-10-01 16:49:39 +08:00
kevin
096fe3bc47 add more tests 2020-10-01 11:57:06 +08:00
kevin
e37858295a add more tests 2020-10-01 11:49:17 +08:00
kevin
5a4afb1518 add more tests 2020-10-01 10:29:03 +08:00
kevin
63f1f39c40 fix int64 primary key problem 2020-09-30 22:25:47 +08:00
kevin
481895d1e4 add more tests 2020-09-30 17:47:56 +08:00
shenbaise9527
9e9ce3bf48 GetBreaker need double-check (#112) 2020-09-30 16:50:02 +08:00
kevin
0ce654968d add more tests 2020-09-30 15:36:13 +08:00
Percy Gauguin
2703493541 update: fix wrong word (#110) 2020-09-30 15:08:47 +08:00
janetyu
d4240cd4b0 perfect the bookstore and shorturl doc (#109)
* perfect the bookstore and shorturl doc

* 避免歧义
2020-09-30 14:22:37 +08:00
kevin
a22bcc84a3 better lock practice in sharedcalls 2020-09-30 12:31:35 +08:00
33 changed files with 1130 additions and 91 deletions

View File

@@ -1,4 +1,4 @@
ignore:
- "doc"
- "example"
- "tools"
- "tools"

View File

@@ -1,6 +0,0 @@
{
"MD010": false,
"MD013": false,
"MD033": false,
"MD034": false
}

View File

@@ -13,11 +13,6 @@ import (
"github.com/tal-tech/go-zero/core/timex"
)
const (
StateClosed State = iota
StateOpen
)
const (
numHistoryReasons = 5
timeFormat = "15:04:05"
@@ -27,7 +22,6 @@ const (
var ErrServiceUnavailable = errors.New("circuit breaker is open")
type (
State = int32
Acceptable func(err error) bool
Breaker interface {

View File

@@ -41,10 +41,13 @@ func GetBreaker(name string) Breaker {
}
lock.Lock()
defer lock.Unlock()
b, ok = breakers[name]
if !ok {
b = NewBreaker(WithName(name))
breakers[name] = b
}
lock.Unlock()
b = NewBreaker()
breakers[name] = b
return b
}
@@ -55,20 +58,5 @@ func NoBreakFor(name string) {
}
func do(name string, execute func(b Breaker) error) error {
lock.RLock()
b, ok := breakers[name]
lock.RUnlock()
if ok {
return execute(b)
}
lock.Lock()
b, ok = breakers[name]
if !ok {
b = NewBreaker(WithName(name))
breakers[name] = b
}
lock.Unlock()
return execute(b)
return execute(GetBreaker(name))
}

View File

@@ -2,7 +2,6 @@ package breaker
import (
"math"
"sync/atomic"
"time"
"github.com/tal-tech/go-zero/core/collection"
@@ -21,7 +20,6 @@ const (
// see Client-Side Throttling section in https://landing.google.com/sre/sre-book/chapters/handling-overload/
type googleBreaker struct {
k float64
state int32
stat *collection.RollingWindow
proba *mathx.Proba
}
@@ -32,7 +30,6 @@ func newGoogleBreaker() *googleBreaker {
return &googleBreaker{
stat: st,
k: k,
state: StateClosed,
proba: mathx.NewProba(),
}
}
@@ -43,15 +40,9 @@ func (b *googleBreaker) accept() error {
// https://landing.google.com/sre/sre-book/chapters/handling-overload/#eq2101
dropRatio := math.Max(0, (float64(total-protection)-weightedAccepts)/float64(total+1))
if dropRatio <= 0 {
if atomic.LoadInt32(&b.state) == StateOpen {
atomic.CompareAndSwapInt32(&b.state, StateOpen, StateClosed)
}
return nil
}
if atomic.LoadInt32(&b.state) == StateClosed {
atomic.CompareAndSwapInt32(&b.state, StateClosed, StateOpen)
}
if b.proba.TrueOnProba(dropRatio) {
return ErrServiceUnavailable
}

View File

@@ -27,7 +27,6 @@ func getGoogleBreaker() *googleBreaker {
return &googleBreaker{
stat: st,
k: 5,
state: StateClosed,
proba: mathx.NewProba(),
}
}

View File

@@ -0,0 +1,86 @@
package cmdline
import (
"fmt"
"os"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/lang"
)
func TestEnterToContinue(t *testing.T) {
r, w, err := os.Pipe()
assert.Nil(t, err)
ow := os.Stdout
os.Stdout = w
or := os.Stdin
os.Stdin = r
defer func() {
os.Stdin = or
os.Stdout = ow
}()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println()
}()
go func() {
defer wg.Done()
EnterToContinue()
}()
wait := make(chan lang.PlaceholderType)
go func() {
wg.Wait()
close(wait)
}()
select {
case <-time.After(time.Second):
t.Error("timeout")
case <-wait:
}
}
func TestReadLine(t *testing.T) {
r, w, err := os.Pipe()
assert.Nil(t, err)
ow := os.Stdout
os.Stdout = w
or := os.Stdin
os.Stdin = r
defer func() {
os.Stdin = or
os.Stdout = ow
}()
const message = "hello"
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println(message)
}()
go func() {
defer wg.Done()
input := ReadLine("")
assert.Equal(t, message, input)
}()
wait := make(chan lang.PlaceholderType)
go func() {
wg.Wait()
close(wait)
}()
select {
case <-time.After(time.Second):
t.Error("timeout")
case <-wait:
}
}

View File

@@ -71,3 +71,12 @@ func TestDiffieHellmanMiddleManAttack(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, string(src), string(decryptedSrc))
}
func TestKeyBytes(t *testing.T) {
var empty DhKey
assert.Equal(t, 0, len(empty.Bytes()))
key, err := GenerateKey()
assert.Nil(t, err)
assert.True(t, len(key.Bytes()) > 0)
}

19
core/codec/hmac_test.go Normal file
View File

@@ -0,0 +1,19 @@
package codec
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHmac(t *testing.T) {
ret := Hmac([]byte("foo"), "bar")
assert.Equal(t, "f9320baf0249169e73850cd6156ded0106e2bb6ad8cab01b7bbbebe6d1065317",
fmt.Sprintf("%x", ret))
}
func TestHmacBase64(t *testing.T) {
ret := HmacBase64([]byte("foo"), "bar")
assert.Equal(t, "+TILrwJJFp5zhQzWFW3tAQbiu2rYyrAbe7vr5tEGUxc=", ret)
}

59
core/codec/rsa_test.go Normal file
View File

@@ -0,0 +1,59 @@
package codec
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/fs"
)
const (
priKey = `-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQC4TJk3onpqb2RYE3wwt23J9SHLFstHGSkUYFLe+nl1dEKHbD+/
Zt95L757J3xGTrwoTc7KCTxbrgn+stn0w52BNjj/kIE2ko4lbh/v8Fl14AyVR9ms
fKtKOnhe5FCT72mdtApr+qvzcC3q9hfXwkyQU32pv7q5UimZ205iKSBmgQIDAQAB
AoGAM5mWqGIAXj5z3MkP01/4CDxuyrrGDVD5FHBno3CDgyQa4Gmpa4B0/ywj671B
aTnwKmSmiiCN2qleuQYASixes2zY5fgTzt+7KNkl9JHsy7i606eH2eCKzsUa/s6u
WD8V3w/hGCQ9zYI18ihwyXlGHIgcRz/eeRh+nWcWVJzGOPUCQQD5nr6It/1yHb1p
C6l4fC4xXF19l4KxJjGu1xv/sOpSx0pOqBDEX3Mh//FU954392rUWDXV1/I65BPt
TLphdsu3AkEAvQJ2Qay/lffFj9FaUrvXuftJZ/Ypn0FpaSiUh3Ak3obBT6UvSZS0
bcYdCJCNHDtBOsWHnIN1x+BcWAPrdU7PhwJBAIQ0dUlH2S3VXnoCOTGc44I1Hzbj
Rc65IdsuBqA3fQN2lX5vOOIog3vgaFrOArg1jBkG1wx5IMvb/EnUN2pjVqUCQCza
KLXtCInOAlPemlCHwumfeAvznmzsWNdbieOZ+SXVVIpR6KbNYwOpv7oIk3Pfm9sW
hNffWlPUKhW42Gc+DIECQQDmk20YgBXwXWRM5DRPbhisIV088N5Z58K9DtFWkZsd
OBDT3dFcgZONtlmR1MqZO0pTh30lA4qovYj3Bx7A8i36
-----END RSA PRIVATE KEY-----`
pubKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4TJk3onpqb2RYE3wwt23J9SHL
FstHGSkUYFLe+nl1dEKHbD+/Zt95L757J3xGTrwoTc7KCTxbrgn+stn0w52BNjj/
kIE2ko4lbh/v8Fl14AyVR9msfKtKOnhe5FCT72mdtApr+qvzcC3q9hfXwkyQU32p
v7q5UimZ205iKSBmgQIDAQAB
-----END PUBLIC KEY-----`
testBody = `this is the content`
encryptedBody = `49e7bc15640e5d927fd3f129b749536d0755baf03a0f35fc914ff1b7b8ce659e5fe3a598442eb908c5995e28bacd3d76e4420bb05b6bfc177040f66c6976f680f7123505d626ab96a9db1151f45c93bc0262db9087b9fb6801715f76f902e644a20029262858f05b0d10540842204346ac1d6d8f29cc5d47dab79af75d922ef2`
)
func TestCryption(t *testing.T) {
enc, err := NewRsaEncrypter([]byte(pubKey))
assert.Nil(t, err)
ret, err := enc.Encrypt([]byte(testBody))
assert.Nil(t, err)
file, err := fs.TempFilenameWithText(priKey)
assert.Nil(t, err)
dec, err := NewRsaDecrypter(file)
assert.Nil(t, err)
actual, err := dec.Decrypt(ret)
assert.Nil(t, err)
assert.Equal(t, testBody, string(actual))
actual, err = dec.DecryptBase64(base64.StdEncoding.EncodeToString(ret))
assert.Nil(t, err)
assert.Equal(t, testBody, string(actual))
}
func TestBadPubKey(t *testing.T) {
_, err := NewRsaEncrypter([]byte("foo"))
assert.Equal(t, ErrPublicKey, err)
}

View File

@@ -15,6 +15,7 @@ const (
stringType
)
// Set is not thread-safe, for concurrent use, make sure to use it with synchronization.
type Set struct {
data map[interface{}]lang.PlaceholderType
tp int
@@ -182,10 +183,7 @@ func (s *Set) add(i interface{}) {
}
func (s *Set) setType(i interface{}) {
if s.tp != untyped {
return
}
// s.tp can only be untyped here
switch i.(type) {
case int:
s.tp = intType

View File

@@ -5,8 +5,13 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
)
func init() {
logx.Disable()
}
func BenchmarkRawSet(b *testing.B) {
m := make(map[interface{}]struct{})
for i := 0; i < b.N; i++ {
@@ -147,3 +152,51 @@ func TestCount(t *testing.T) {
// then
assert.Equal(t, set.Count(), 3)
}
func TestKeysIntMismatch(t *testing.T) {
set := NewSet()
set.add(int64(1))
set.add(2)
vals := set.KeysInt()
assert.EqualValues(t, []int{2}, vals)
}
func TestKeysInt64Mismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add(int64(2))
vals := set.KeysInt64()
assert.EqualValues(t, []int64{2}, vals)
}
func TestKeysUintMismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add(uint(2))
vals := set.KeysUint()
assert.EqualValues(t, []uint{2}, vals)
}
func TestKeysUint64Mismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add(uint64(2))
vals := set.KeysUint64()
assert.EqualValues(t, []uint64{2}, vals)
}
func TestKeysStrMismatch(t *testing.T) {
set := NewSet()
set.add(1)
set.add("2")
vals := set.KeysStr()
assert.EqualValues(t, []string{"2"}, vals)
}
func TestSetType(t *testing.T) {
set := NewUnmanagedSet()
set.add(1)
set.add("2")
vals := set.Keys()
assert.ElementsMatch(t, []interface{}{1, "2"}, vals)
}

View File

@@ -32,8 +32,7 @@ func TestConfigJson(t *testing.T) {
A string `json:"a"`
B int `json:"b"`
}
err = LoadConfig(tmpfile, &val)
assert.Nil(t, err)
MustLoad(tmpfile, &val)
assert.Equal(t, "foo", val.A)
assert.Equal(t, 1, val.B)
})

View File

@@ -30,12 +30,12 @@ type mapBasedProperties struct {
lock sync.RWMutex
}
// Loads the properties into a properties configuration instance. May return the
// configuration itself along with an error that indicates if there was a problem loading the configuration.
// Loads the properties into a properties configuration instance.
// Returns an error that indicates if there was a problem loading the configuration.
func LoadProperties(filename string) (Properties, error) {
lines, err := iox.ReadTextLines(filename, iox.WithoutBlank(), iox.OmitWithPrefix("#"))
if err != nil {
return nil, nil
return nil, err
}
raw := make(map[string]string)

View File

@@ -41,3 +41,8 @@ func TestSetInt(t *testing.T) {
props.SetInt(key, value)
assert.Equal(t, value, props.GetInt(key))
}
func TestLoadBadFile(t *testing.T) {
_, err := LoadProperties("nosuchfile")
assert.NotNil(t, err)
}

View File

@@ -85,6 +85,46 @@ func TestStructedLogSlow(t *testing.T) {
})
}
func TestStructedLogSlowf(t *testing.T) {
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
slowLog = writer
}, func(v ...interface{}) {
Slowf(fmt.Sprint(v...))
})
}
func TestStructedLogStat(t *testing.T) {
doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
statLog = writer
}, func(v ...interface{}) {
Stat(v...)
})
}
func TestStructedLogStatf(t *testing.T) {
doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
statLog = writer
}, func(v ...interface{}) {
Statf(fmt.Sprint(v...))
})
}
func TestStructedLogSevere(t *testing.T) {
doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
severeLog = writer
}, func(v ...interface{}) {
Severe(v...)
})
}
func TestStructedLogSeveref(t *testing.T) {
doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
severeLog = writer
}, func(v ...interface{}) {
Severef(fmt.Sprint(v...))
})
}
func TestStructedLogWithDuration(t *testing.T) {
const message = "hello there"
writer := new(mockWriter)
@@ -135,6 +175,15 @@ func TestMustNil(t *testing.T) {
Must(nil)
}
func TestDisable(t *testing.T) {
Disable()
WithKeepDays(1)
WithGzip()
assert.Nil(t, Close())
writeConsole = false
assert.Nil(t, Close())
}
func BenchmarkCopyByteSliceAppend(b *testing.B) {
for i := 0; i < b.N; i++ {
var buf []byte
@@ -232,7 +281,7 @@ func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteClo
t.Error(err)
}
assert.Equal(t, level, entry.Level)
assert.Equal(t, message, entry.Content)
assert.True(t, strings.Contains(entry.Content, message))
}
func testSetLevelTwiceWithMode(t *testing.T, mode string) {

View File

@@ -192,14 +192,16 @@ func (l *RotateLogger) init() error {
}
func (l *RotateLogger) maybeCompressFile(file string) {
if l.compress {
defer func() {
if r := recover(); r != nil {
ErrorStack(r)
}
}()
compressLogFile(file)
if !l.compress {
return
}
defer func() {
if r := recover(); r != nil {
ErrorStack(r)
}
}()
compressLogFile(file)
}
func (l *RotateLogger) maybeDeleteOutdatedFiles() {

View File

@@ -0,0 +1,119 @@
package logx
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/fs"
)
func TestDailyRotateRuleMarkRotated(t *testing.T) {
var rule DailyRotateRule
rule.MarkRotated()
assert.Equal(t, getNowDate(), rule.rotatedTime)
}
func TestDailyRotateRuleOutdatedFiles(t *testing.T) {
var rule DailyRotateRule
assert.Empty(t, rule.OutdatedFiles())
rule.days = 1
assert.Empty(t, rule.OutdatedFiles())
rule.gzip = true
assert.Empty(t, rule.OutdatedFiles())
}
func TestDailyRotateRuleShallRotate(t *testing.T) {
var rule DailyRotateRule
rule.rotatedTime = time.Now().Add(time.Hour * 24).Format(dateFormat)
assert.True(t, rule.ShallRotate())
}
func TestRotateLoggerClose(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
assert.Nil(t, logger.Close())
}
func TestRotateLoggerGetBackupFilename(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
assert.True(t, len(logger.getBackupFilename()) > 0)
logger.backup = ""
assert.True(t, len(logger.getBackupFilename()) > 0)
}
func TestRotateLoggerMayCompressFile(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
if len(filename) > 0 {
defer os.Remove(filename)
}
logger, err := NewLogger(filename, new(DailyRotateRule), false)
assert.Nil(t, err)
logger.maybeCompressFile(filename)
_, err = os.Stat(filename)
assert.Nil(t, err)
}
func TestRotateLoggerMayCompressFileTrue(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
logger, err := NewLogger(filename, new(DailyRotateRule), true)
assert.Nil(t, err)
if len(filename) > 0 {
defer func() {
os.Remove(filename)
os.Remove(filepath.Base(logger.getBackupFilename()) + ".gz")
}()
}
logger.maybeCompressFile(filename)
_, err = os.Stat(filename)
assert.NotNil(t, err)
}
func TestRotateLoggerRotate(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
logger, err := NewLogger(filename, new(DailyRotateRule), true)
assert.Nil(t, err)
if len(filename) > 0 {
defer func() {
os.Remove(filename)
os.Remove(logger.getBackupFilename())
os.Remove(filepath.Base(logger.getBackupFilename()) + ".gz")
}()
}
err = logger.rotate()
assert.Nil(t, err)
}
func TestRotateLoggerWrite(t *testing.T) {
filename, err := fs.TempFilenameWithText("foo")
assert.Nil(t, err)
rule := new(DailyRotateRule)
logger, err := NewLogger(filename, rule, true)
assert.Nil(t, err)
if len(filename) > 0 {
defer func() {
os.Remove(filename)
os.Remove(logger.getBackupFilename())
os.Remove(filepath.Base(logger.getBackupFilename()) + ".gz")
}()
}
logger.write([]byte(`foo`))
rule.rotatedTime = time.Now().Add(-time.Hour * 24).Format(dateFormat)
logger.write([]byte(`bar`))
}

View File

@@ -33,10 +33,10 @@ func captureOutput(f func()) string {
writer := new(mockWriter)
infoLog = writer
prevLevel := logLevel
logLevel = InfoLevel
prevLevel := atomic.LoadUint32(&logLevel)
SetLevel(InfoLevel)
f()
logLevel = prevLevel
SetLevel(prevLevel)
return writer.builder.String()
}

56
core/stores/cache/cleaner_test.go vendored Normal file
View File

@@ -0,0 +1,56 @@
package cache
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestNextDelay(t *testing.T) {
tests := []struct {
name string
input time.Duration
output time.Duration
ok bool
}{
{
name: "second",
input: time.Second,
output: time.Second * 5,
ok: true,
},
{
name: "5 seconds",
input: time.Second * 5,
output: time.Minute,
ok: true,
},
{
name: "minute",
input: time.Minute,
output: time.Minute * 5,
ok: true,
},
{
name: "5 minutes",
input: time.Minute * 5,
output: time.Hour,
ok: true,
},
{
name: "hour",
input: time.Hour,
output: 0,
ok: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
next, ok := nextDelay(test.input)
assert.Equal(t, test.ok, ok)
assert.Equal(t, test.output, next)
})
}
}

26
core/stores/cache/util_test.go vendored Normal file
View File

@@ -0,0 +1,26 @@
package cache
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFormatKeys(t *testing.T) {
assert.Equal(t, "a,b", formatKeys([]string{"a", "b"}))
}
func TestTotalWeights(t *testing.T) {
val := TotalWeights([]NodeConf{
{
Weight: -1,
},
{
Weight: 0,
},
{
Weight: 1,
},
})
assert.Equal(t, 1, val)
}

View File

@@ -0,0 +1,110 @@
package redis
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/stringx"
)
func TestRedisConf(t *testing.T) {
tests := []struct {
name string
RedisConf
ok bool
}{
{
name: "missing host",
RedisConf: RedisConf{
Host: "",
Type: NodeType,
Pass: "",
},
ok: false,
},
{
name: "missing type",
RedisConf: RedisConf{
Host: "localhost:6379",
Type: "",
Pass: "",
},
ok: false,
},
{
name: "ok",
RedisConf: RedisConf{
Host: "localhost:6379",
Type: NodeType,
Pass: "",
},
ok: true,
},
}
for _, test := range tests {
t.Run(stringx.RandId(), func(t *testing.T) {
if test.ok {
assert.Nil(t, test.RedisConf.Validate())
assert.NotNil(t, test.RedisConf.NewRedis())
} else {
assert.NotNil(t, test.RedisConf.Validate())
}
})
}
}
func TestRedisKeyConf(t *testing.T) {
tests := []struct {
name string
RedisKeyConf
ok bool
}{
{
name: "missing host",
RedisKeyConf: RedisKeyConf{
RedisConf: RedisConf{
Host: "",
Type: NodeType,
Pass: "",
},
Key: "foo",
},
ok: false,
},
{
name: "missing key",
RedisKeyConf: RedisKeyConf{
RedisConf: RedisConf{
Host: "localhost:6379",
Type: NodeType,
Pass: "",
},
Key: "",
},
ok: false,
},
{
name: "ok",
RedisKeyConf: RedisKeyConf{
RedisConf: RedisConf{
Host: "localhost:6379",
Type: NodeType,
Pass: "",
},
Key: "foo",
},
ok: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if test.ok {
assert.Nil(t, test.RedisKeyConf.Validate())
} else {
assert.NotNil(t, test.RedisKeyConf.Validate())
}
})
}
}

View File

@@ -7,11 +7,14 @@ import (
"time"
"github.com/alicebob/miniredis"
red "github.com/go-redis/redis"
"github.com/stretchr/testify/assert"
)
func TestRedis_Exists(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Exists("a")
assert.NotNil(t, err)
ok, err := client.Exists("a")
assert.Nil(t, err)
assert.False(t, ok)
@@ -24,7 +27,9 @@ func TestRedis_Exists(t *testing.T) {
func TestRedis_Eval(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := client.Eval(`redis.call("EXISTS", KEYS[1])`, []string{"notexist"})
_, err := NewRedis(client.Addr, "").Eval(`redis.call("EXISTS", KEYS[1])`, []string{"notexist"})
assert.NotNil(t, err)
_, err = client.Eval(`redis.call("EXISTS", KEYS[1])`, []string{"notexist"})
assert.Equal(t, Nil, err)
err = client.Set("key1", "value1")
assert.Nil(t, err)
@@ -40,6 +45,8 @@ func TestRedis_Hgetall(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hgetall("a")
assert.NotNil(t, err)
vals, err := client.Hgetall("a")
assert.Nil(t, err)
assert.EqualValues(t, map[string]string{
@@ -51,8 +58,11 @@ func TestRedis_Hgetall(t *testing.T) {
func TestRedis_Hvals(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.NotNil(t, NewRedis(client.Addr, "").Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hvals("a")
assert.NotNil(t, err)
vals, err := client.Hvals("a")
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"aaa", "bbb"}, vals)
@@ -63,6 +73,8 @@ func TestRedis_Hsetnx(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hsetnx("a", "bb", "ccc")
assert.NotNil(t, err)
ok, err := client.Hsetnx("a", "bb", "ccc")
assert.Nil(t, err)
assert.False(t, ok)
@@ -79,6 +91,8 @@ func TestRedis_HdelHlen(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hlen("a")
assert.NotNil(t, err)
num, err := client.Hlen("a")
assert.Nil(t, err)
assert.Equal(t, 2, num)
@@ -93,6 +107,8 @@ func TestRedis_HdelHlen(t *testing.T) {
func TestRedis_HIncrBy(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Hincrby("key", "field", 2)
assert.NotNil(t, err)
val, err := client.Hincrby("key", "field", 2)
assert.Nil(t, err)
assert.Equal(t, 2, val)
@@ -106,6 +122,8 @@ func TestRedis_Hkeys(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hkeys("a")
assert.NotNil(t, err)
vals, err := client.Hkeys("a")
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"aa", "bb"}, vals)
@@ -116,6 +134,8 @@ func TestRedis_Hmget(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hmget("a", "aa", "bb")
assert.NotNil(t, err)
vals, err := client.Hmget("a", "aa", "bb")
assert.Nil(t, err)
assert.EqualValues(t, []string{"aaa", "bbb"}, vals)
@@ -127,6 +147,7 @@ func TestRedis_Hmget(t *testing.T) {
func TestRedis_Hmset(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.NotNil(t, NewRedis(client.Addr, "").Hmset("a", nil))
assert.Nil(t, client.Hmset("a", map[string]string{
"aa": "aaa",
"bb": "bbb",
@@ -139,6 +160,8 @@ func TestRedis_Hmset(t *testing.T) {
func TestRedis_Incr(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Incr("a")
assert.NotNil(t, err)
val, err := client.Incr("a")
assert.Nil(t, err)
assert.Equal(t, int64(1), val)
@@ -150,6 +173,8 @@ func TestRedis_Incr(t *testing.T) {
func TestRedis_IncrBy(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Incrby("a", 2)
assert.NotNil(t, err)
val, err := client.Incrby("a", 2)
assert.Nil(t, err)
assert.Equal(t, int64(2), val)
@@ -165,26 +190,49 @@ func TestRedis_Keys(t *testing.T) {
assert.Nil(t, err)
err = client.Set("key2", "value2")
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Keys("*")
assert.NotNil(t, err)
keys, err := client.Keys("*")
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"key1", "key2"}, keys)
})
}
func TestRedis_HyperLogLog(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
r := NewRedis(client.Addr, "")
_, err := r.Pfadd("key1")
assert.NotNil(t, err)
_, err = r.Pfcount("*")
assert.NotNil(t, err)
err = r.Pfmerge("*")
assert.NotNil(t, err)
})
}
func TestRedis_List(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Lpush("key", "value1", "value2")
assert.NotNil(t, err)
val, err := client.Lpush("key", "value1", "value2")
assert.Nil(t, err)
assert.Equal(t, 2, val)
_, err = NewRedis(client.Addr, "").Rpush("key", "value3", "value4")
assert.NotNil(t, err)
val, err = client.Rpush("key", "value3", "value4")
assert.Nil(t, err)
assert.Equal(t, 4, val)
_, err = NewRedis(client.Addr, "").Llen("key")
assert.NotNil(t, err)
val, err = client.Llen("key")
assert.Nil(t, err)
assert.Equal(t, 4, val)
vals, err := client.Lrange("key", 0, 10)
assert.Nil(t, err)
assert.EqualValues(t, []string{"value2", "value1", "value3", "value4"}, vals)
_, err = NewRedis(client.Addr, "").Lpop("key")
assert.NotNil(t, err)
v, err := client.Lpop("key")
assert.Nil(t, err)
assert.Equal(t, "value2", v)
@@ -194,9 +242,13 @@ func TestRedis_List(t *testing.T) {
val, err = client.Rpush("key", "value3", "value3")
assert.Nil(t, err)
assert.Equal(t, 7, val)
_, err = NewRedis(client.Addr, "").Lrem("key", 2, "value1")
assert.NotNil(t, err)
n, err := client.Lrem("key", 2, "value1")
assert.Nil(t, err)
assert.Equal(t, 2, n)
_, err = NewRedis(client.Addr, "").Lrange("key", 0, 10)
assert.NotNil(t, err)
vals, err = client.Lrange("key", 0, 10)
assert.Nil(t, err)
assert.EqualValues(t, []string{"value2", "value3", "value4", "value3", "value3"}, vals)
@@ -215,6 +267,8 @@ func TestRedis_Mget(t *testing.T) {
assert.Nil(t, err)
err = client.Set("key2", "value2")
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Mget("key1", "key0", "key2", "key3")
assert.NotNil(t, err)
vals, err := client.Mget("key1", "key0", "key2", "key3")
assert.Nil(t, err)
assert.EqualValues(t, []string{"value1", "", "value2", ""}, vals)
@@ -223,7 +277,9 @@ func TestRedis_Mget(t *testing.T) {
func TestRedis_SetBit(t *testing.T) {
runOnRedis(t, func(client *Redis) {
err := client.SetBit("key", 1, 1)
err := NewRedis(client.Addr, "").SetBit("key", 1, 1)
assert.NotNil(t, err)
err = client.SetBit("key", 1, 1)
assert.Nil(t, err)
})
}
@@ -232,6 +288,8 @@ func TestRedis_GetBit(t *testing.T) {
runOnRedis(t, func(client *Redis) {
err := client.SetBit("key", 2, 1)
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").GetBit("key", 2)
assert.NotNil(t, err)
val, err := client.GetBit("key", 2)
assert.Nil(t, err)
assert.Equal(t, 1, val)
@@ -240,6 +298,8 @@ func TestRedis_GetBit(t *testing.T) {
func TestRedis_Persist(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Persist("key")
assert.NotNil(t, err)
ok, err := client.Persist("key")
assert.Nil(t, err)
assert.False(t, ok)
@@ -248,11 +308,15 @@ func TestRedis_Persist(t *testing.T) {
ok, err = client.Persist("key")
assert.Nil(t, err)
assert.False(t, ok)
err = NewRedis(client.Addr, "").Expire("key", 5)
assert.NotNil(t, err)
err = client.Expire("key", 5)
assert.Nil(t, err)
ok, err = client.Persist("key")
assert.Nil(t, err)
assert.True(t, ok)
err = NewRedis(client.Addr, "").Expireat("key", time.Now().Unix()+5)
assert.NotNil(t, err)
err = client.Expireat("key", time.Now().Unix()+5)
assert.Nil(t, err)
ok, err = client.Persist("key")
@@ -274,6 +338,8 @@ func TestRedis_Scan(t *testing.T) {
assert.Nil(t, err)
err = client.Set("key2", "value2")
assert.Nil(t, err)
_, _, err = NewRedis(client.Addr, "").Scan(0, "*", 100)
assert.NotNil(t, err)
keys, _, err := client.Scan(0, "*", 100)
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"key1", "key2"}, keys)
@@ -294,6 +360,8 @@ func TestRedis_Sscan(t *testing.T) {
var cursor uint64 = 0
sum := 0
for {
_, _, err := NewRedis(client.Addr, "").Sscan(key, cursor, "", 100)
assert.NotNil(t, err)
keys, next, err := client.Sscan(key, cursor, "", 100)
assert.Nil(t, err)
sum += len(keys)
@@ -304,6 +372,8 @@ func TestRedis_Sscan(t *testing.T) {
}
assert.Equal(t, sum, 1550)
_, err = NewRedis(client.Addr, "").Del(key)
assert.NotNil(t, err)
_, err = client.Del(key)
assert.Nil(t, err)
})
@@ -311,46 +381,72 @@ func TestRedis_Sscan(t *testing.T) {
func TestRedis_Set(t *testing.T) {
runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Sadd("key", 1, 2, 3, 4)
assert.NotNil(t, err)
num, err := client.Sadd("key", 1, 2, 3, 4)
assert.Nil(t, err)
assert.Equal(t, 4, num)
_, err = NewRedis(client.Addr, "").Scard("key")
assert.NotNil(t, err)
val, err := client.Scard("key")
assert.Nil(t, err)
assert.Equal(t, int64(4), val)
_, err = NewRedis(client.Addr, "").Sismember("key", 2)
assert.NotNil(t, err)
ok, err := client.Sismember("key", 2)
assert.Nil(t, err)
assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Srem("key", 3, 4)
assert.NotNil(t, err)
num, err = client.Srem("key", 3, 4)
assert.Nil(t, err)
assert.Equal(t, 2, num)
_, err = NewRedis(client.Addr, "").Smembers("key")
assert.NotNil(t, err)
vals, err := client.Smembers("key")
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"1", "2"}, vals)
_, err = NewRedis(client.Addr, "").Srandmember("key", 1)
assert.NotNil(t, err)
members, err := client.Srandmember("key", 1)
assert.Nil(t, err)
assert.Len(t, members, 1)
assert.Contains(t, []string{"1", "2"}, members[0])
_, err = NewRedis(client.Addr, "").Spop("key")
assert.NotNil(t, err)
member, err := client.Spop("key")
assert.Nil(t, err)
assert.Contains(t, []string{"1", "2"}, member)
_, err = NewRedis(client.Addr, "").Smembers("key")
assert.NotNil(t, err)
vals, err = client.Smembers("key")
assert.Nil(t, err)
assert.NotContains(t, vals, member)
_, err = NewRedis(client.Addr, "").Sadd("key1", 1, 2, 3, 4)
assert.NotNil(t, err)
num, err = client.Sadd("key1", 1, 2, 3, 4)
assert.Nil(t, err)
assert.Equal(t, 4, num)
num, err = client.Sadd("key2", 2, 3, 4, 5)
assert.Nil(t, err)
assert.Equal(t, 4, num)
_, err = NewRedis(client.Addr, "").Sunion("key1", "key2")
assert.NotNil(t, err)
vals, err = client.Sunion("key1", "key2")
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"1", "2", "3", "4", "5"}, vals)
_, err = NewRedis(client.Addr, "").Sunionstore("key3", "key1", "key2")
assert.NotNil(t, err)
num, err = client.Sunionstore("key3", "key1", "key2")
assert.Nil(t, err)
assert.Equal(t, 5, num)
_, err = NewRedis(client.Addr, "").Sdiff("key1", "key2")
assert.NotNil(t, err)
vals, err = client.Sdiff("key1", "key2")
assert.Nil(t, err)
assert.EqualValues(t, []string{"1"}, vals)
_, err = NewRedis(client.Addr, "").Sdiffstore("key4", "key1", "key2")
assert.NotNil(t, err)
num, err = client.Sdiffstore("key4", "key1", "key2")
assert.Nil(t, err)
assert.Equal(t, 1, num)
@@ -359,8 +455,12 @@ func TestRedis_Set(t *testing.T) {
func TestRedis_SetGetDel(t *testing.T) {
runOnRedis(t, func(client *Redis) {
err := client.Set("hello", "world")
err := NewRedis(client.Addr, "").Set("hello", "world")
assert.NotNil(t, err)
err = client.Set("hello", "world")
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Get("hello")
assert.NotNil(t, err)
val, err := client.Get("hello")
assert.Nil(t, err)
assert.Equal(t, "world", val)
@@ -372,8 +472,12 @@ func TestRedis_SetGetDel(t *testing.T) {
func TestRedis_SetExNx(t *testing.T) {
runOnRedis(t, func(client *Redis) {
err := client.Setex("hello", "world", 5)
err := NewRedis(client.Addr, "").Setex("hello", "world", 5)
assert.NotNil(t, err)
err = client.Setex("hello", "world", 5)
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Setnx("hello", "newworld")
assert.NotNil(t, err)
ok, err := client.Setnx("hello", "newworld")
assert.Nil(t, err)
assert.False(t, ok)
@@ -389,6 +493,8 @@ func TestRedis_SetExNx(t *testing.T) {
ttl, err := client.Ttl("hello")
assert.Nil(t, err)
assert.True(t, ttl > 0)
_, err = NewRedis(client.Addr, "").SetnxEx("newhello", "newworld", 5)
assert.NotNil(t, err)
ok, err = client.SetnxEx("newhello", "newworld", 5)
assert.Nil(t, err)
assert.False(t, ok)
@@ -408,12 +514,18 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
runOnRedis(t, func(client *Redis) {
err := client.Hset("key", "field", "value")
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Hget("key", "field")
assert.NotNil(t, err)
val, err := client.Hget("key", "field")
assert.Nil(t, err)
assert.Equal(t, "value", val)
_, err = NewRedis(client.Addr, "").Hexists("key", "field")
assert.NotNil(t, err)
ok, err := client.Hexists("key", "field")
assert.Nil(t, err)
assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Hdel("key", "field")
assert.NotNil(t, err)
ret, err := client.Hdel("key", "field")
assert.Nil(t, err)
assert.True(t, ret)
@@ -434,23 +546,50 @@ func TestRedis_SortedSet(t *testing.T) {
val, err := client.Zscore("key", "value1")
assert.Nil(t, err)
assert.Equal(t, int64(2), val)
_, err = NewRedis(client.Addr, "").Zincrby("key", 3, "value1")
assert.NotNil(t, err)
val, err = client.Zincrby("key", 3, "value1")
assert.Nil(t, err)
assert.Equal(t, int64(5), val)
_, err = NewRedis(client.Addr, "").Zscore("key", "value1")
assert.NotNil(t, err)
val, err = client.Zscore("key", "value1")
assert.Nil(t, err)
assert.Equal(t, int64(5), val)
ok, err = client.Zadd("key", 6, "value2")
val, err = NewRedis(client.Addr, "").Zadds("key")
assert.NotNil(t, err)
val, err = client.Zadds("key", Pair{
Key: "value2",
Score: 6,
}, Pair{
Key: "value3",
Score: 7,
})
assert.Nil(t, err)
assert.True(t, ok)
ok, err = client.Zadd("key", 7, "value3")
assert.Equal(t, int64(2), val)
pairs, err := NewRedis(client.Addr, "").ZRevRangeWithScores("key", 1, 3)
assert.NotNil(t, err)
pairs, err = client.ZRevRangeWithScores("key", 1, 3)
assert.Nil(t, err)
assert.True(t, ok)
assert.EqualValues(t, []Pair{
{
Key: "value2",
Score: 6,
},
{
Key: "value1",
Score: 5,
},
}, pairs)
rank, err := client.Zrank("key", "value2")
assert.Nil(t, err)
assert.Equal(t, int64(1), rank)
_, err = NewRedis(client.Addr, "").Zrank("key", "value4")
assert.NotNil(t, err)
_, err = client.Zrank("key", "value4")
assert.Equal(t, Nil, err)
_, err = NewRedis(client.Addr, "").Zrem("key", "value2", "value3")
assert.NotNil(t, err)
num, err := client.Zrem("key", "value2", "value3")
assert.Nil(t, err)
assert.Equal(t, 2, num)
@@ -463,31 +602,47 @@ func TestRedis_SortedSet(t *testing.T) {
ok, err = client.Zadd("key", 8, "value4")
assert.Nil(t, err)
assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Zremrangebyscore("key", 6, 7)
assert.NotNil(t, err)
num, err = client.Zremrangebyscore("key", 6, 7)
assert.Nil(t, err)
assert.Equal(t, 2, num)
ok, err = client.Zadd("key", 6, "value2")
assert.Nil(t, err)
assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Zadd("key", 7, "value3")
assert.NotNil(t, err)
ok, err = client.Zadd("key", 7, "value3")
assert.Nil(t, err)
assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Zcount("key", 6, 7)
assert.NotNil(t, err)
num, err = client.Zcount("key", 6, 7)
assert.Nil(t, err)
assert.Equal(t, 2, num)
_, err = NewRedis(client.Addr, "").Zremrangebyrank("key", 1, 2)
assert.NotNil(t, err)
num, err = client.Zremrangebyrank("key", 1, 2)
assert.Nil(t, err)
assert.Equal(t, 2, num)
_, err = NewRedis(client.Addr, "").Zcard("key")
assert.NotNil(t, err)
card, err := client.Zcard("key")
assert.Nil(t, err)
assert.Equal(t, 2, card)
_, err = NewRedis(client.Addr, "").Zrange("key", 0, -1)
assert.NotNil(t, err)
vals, err := client.Zrange("key", 0, -1)
assert.Nil(t, err)
assert.EqualValues(t, []string{"value1", "value4"}, vals)
_, err = NewRedis(client.Addr, "").Zrevrange("key", 0, -1)
assert.NotNil(t, err)
vals, err = client.Zrevrange("key", 0, -1)
assert.Nil(t, err)
assert.EqualValues(t, []string{"value4", "value1"}, vals)
pairs, err := client.ZrangeWithScores("key", 0, -1)
_, err = NewRedis(client.Addr, "").ZrangeWithScores("key", 0, -1)
assert.NotNil(t, err)
pairs, err = client.ZrangeWithScores("key", 0, -1)
assert.Nil(t, err)
assert.EqualValues(t, []Pair{
{
@@ -499,6 +654,8 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 8,
},
}, pairs)
_, err = NewRedis(client.Addr, "").ZrangebyscoreWithScores("key", 5, 8)
assert.NotNil(t, err)
pairs, err = client.ZrangebyscoreWithScores("key", 5, 8)
assert.Nil(t, err)
assert.EqualValues(t, []Pair{
@@ -511,6 +668,9 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 8,
},
}, pairs)
_, err = NewRedis(client.Addr, "").ZrangebyscoreWithScoresAndLimit(
"key", 5, 8, 1, 1)
assert.NotNil(t, err)
pairs, err = client.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1)
assert.Nil(t, err)
assert.EqualValues(t, []Pair{
@@ -519,6 +679,11 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 8,
},
}, pairs)
pairs, err = client.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 0)
assert.Nil(t, err)
assert.Equal(t, 0, len(pairs))
_, err = NewRedis(client.Addr, "").ZrevrangebyscoreWithScores("key", 5, 8)
assert.NotNil(t, err)
pairs, err = client.ZrevrangebyscoreWithScores("key", 5, 8)
assert.Nil(t, err)
assert.EqualValues(t, []Pair{
@@ -531,6 +696,9 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 5,
},
}, pairs)
_, err = NewRedis(client.Addr, "").ZrevrangebyscoreWithScoresAndLimit(
"key", 5, 8, 1, 1)
assert.NotNil(t, err)
pairs, err = client.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1)
assert.Nil(t, err)
assert.EqualValues(t, []Pair{
@@ -539,11 +707,17 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 5,
},
}, pairs)
pairs, err = client.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 0)
assert.Nil(t, err)
assert.Equal(t, 0, len(pairs))
})
}
func TestRedis_Pipelined(t *testing.T) {
runOnRedis(t, func(client *Redis) {
assert.NotNil(t, NewRedis(client.Addr, "").Pipelined(func(pipeliner Pipeliner) error {
return nil
}))
err := client.Pipelined(
func(pipe Pipeliner) error {
pipe.Incr("pipelined_counter")
@@ -553,6 +727,8 @@ func TestRedis_Pipelined(t *testing.T) {
},
)
assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Ttl("pipelined_counter")
assert.NotNil(t, err)
ttl, err := client.Ttl("pipelined_counter")
assert.Nil(t, err)
assert.Equal(t, 3600, ttl)
@@ -565,6 +741,76 @@ func TestRedis_Pipelined(t *testing.T) {
})
}
func TestRedisString(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
_, err := getRedis(NewRedis(client.Addr, ClusterType))
assert.Nil(t, err)
assert.Equal(t, client.Addr, client.String())
assert.NotNil(t, NewRedis(client.Addr, "").Ping())
})
}
func TestRedisScriptLoad(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
_, err := NewRedis(client.Addr, "").scriptLoad("foo")
assert.NotNil(t, err)
_, err = client.scriptLoad("foo")
assert.NotNil(t, err)
})
}
func TestRedisToPairs(t *testing.T) {
pairs := toPairs([]red.Z{
{
Member: 1,
Score: 1,
},
{
Member: 2,
Score: 2,
},
})
assert.EqualValues(t, []Pair{
{
Key: "1",
Score: 1,
},
{
Key: "2",
Score: 2,
},
}, pairs)
}
func TestRedisToStrings(t *testing.T) {
vals := toStrings([]interface{}{1, 2})
assert.EqualValues(t, []string{"1", "2"}, vals)
}
func TestRedisBlpop(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
var node mockedNode
_, err := client.Blpop(nil, "foo")
assert.NotNil(t, err)
_, err = client.Blpop(node, "foo")
assert.NotNil(t, err)
})
}
func TestRedisBlpopEx(t *testing.T) {
runOnRedis(t, func(client *Redis) {
client.Ping()
var node mockedNode
_, _, err := client.BlpopEx(nil, "foo")
assert.NotNil(t, err)
_, _, err = client.BlpopEx(node, "foo")
assert.NotNil(t, err)
})
}
func runOnRedis(t *testing.T, fn func(client *Redis)) {
s, err := miniredis.Run()
assert.Nil(t, err)
@@ -576,8 +822,18 @@ func runOnRedis(t *testing.T, fn func(client *Redis)) {
t.Error(err)
}
client.Close()
if client != nil {
client.Close()
}
}()
fn(NewRedis(s.Addr(), NodeType))
}
type mockedNode struct {
RedisNode
}
func (n mockedNode) BLPop(timeout time.Duration, keys ...string) *red.StringSliceCmd {
return red.NewStringSliceCmd("foo", "bar")
}

View File

@@ -82,6 +82,11 @@ func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary
indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
var primaryKey interface{}
var found bool
// if don't use convert numeric primary key into int64,
// then it will be represented as scientific notion, like 2e6
// which will make the cache doesn't match with the previous insert one
keyer = floatKeyer(keyer)
if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) {
primaryKey, err = indexQuery(cc.db, v)
if err != nil {
@@ -119,3 +124,16 @@ func (cc CachedConn) SetCache(key string, v interface{}) error {
func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
return cc.db.Transact(fn)
}
func floatKeyer(fn func(interface{}) string) func(interface{}) string {
return func(primary interface{}) string {
switch v := primary.(type) {
case float32:
return fn(int64(v))
case float64:
return fn(int64(v))
default:
return fn(primary)
}
}
}

View File

@@ -135,6 +135,103 @@ func TestCachedConn_QueryRowIndex_HasCache(t *testing.T) {
assert.Equal(t, `"xin"`, val)
}
func TestCachedConn_QueryRowIndex_HasCache_IntPrimary(t *testing.T) {
const (
primaryInt8 int8 = 100
primaryInt16 int16 = 10000
primaryInt32 int32 = 10000000
primaryInt64 int64 = 10000000
primaryUint8 uint8 = 100
primaryUint16 uint16 = 10000
primaryUint32 uint32 = 10000000
primaryUint64 uint64 = 10000000
)
tests := []struct {
name string
primary interface{}
primaryCache string
}{
{
name: "int8 primary",
primary: primaryInt8,
primaryCache: fmt.Sprint(primaryInt8),
},
{
name: "int16 primary",
primary: primaryInt16,
primaryCache: fmt.Sprint(primaryInt16),
},
{
name: "int32 primary",
primary: primaryInt32,
primaryCache: fmt.Sprint(primaryInt32),
},
{
name: "int64 primary",
primary: primaryInt64,
primaryCache: fmt.Sprint(primaryInt64),
},
{
name: "uint8 primary",
primary: primaryUint8,
primaryCache: fmt.Sprint(primaryUint8),
},
{
name: "uint16 primary",
primary: primaryUint16,
primaryCache: fmt.Sprint(primaryUint16),
},
{
name: "uint32 primary",
primary: primaryUint32,
primaryCache: fmt.Sprint(primaryUint32),
},
{
name: "uint64 primary",
primary: primaryUint64,
primaryCache: fmt.Sprint(primaryUint64),
},
}
s, err := miniredis.Run()
if err != nil {
t.Error(err)
}
defer s.Close()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
resetStats()
s.FlushAll()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10),
cache.WithNotFoundExpiry(time.Second))
var str string
r.Set("index", test.primaryCache)
err = c.QueryRowIndex(&str, "index", func(s interface{}) string {
return fmt.Sprintf("%v/1234", s)
}, func(conn sqlx.SqlConn, v interface{}) (interface{}, error) {
assert.Fail(t, "should not go here")
return test.primary, nil
}, func(conn sqlx.SqlConn, v, primary interface{}) error {
*v.(*string) = "xin"
assert.Equal(t, primary, primary)
return nil
})
assert.Nil(t, err)
assert.Equal(t, "xin", str)
val, err := r.Get("index")
assert.Nil(t, err)
assert.Equal(t, test.primaryCache, val)
val, err = r.Get(test.primaryCache + "/1234")
assert.Nil(t, err)
assert.Equal(t, `"xin"`, val)
})
}
}
func TestCachedConn_QueryRowIndex_HasWrongCache(t *testing.T) {
caches := map[string]string{
"index": "primary",
@@ -148,6 +245,8 @@ func TestCachedConn_QueryRowIndex_HasWrongCache(t *testing.T) {
if err != nil {
t.Error(err)
}
s.FlushAll()
defer s.Close()
r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10),
@@ -446,6 +545,45 @@ func TestCachedConnTransact(t *testing.T) {
assert.True(t, conn.transactValue)
}
func TestQueryRowNoCache(t *testing.T) {
s, err := miniredis.Run()
if err != nil {
t.Error(err)
}
const (
key = "user"
value = "any"
)
var user string
var ran bool
r := redis.NewRedis(s.Addr(), redis.NodeType)
conn := dummySqlConn{queryRow: func(v interface{}, q string, args ...interface{}) error {
user = value
ran = true
return nil
}}
c := NewNodeConn(&conn, r, cache.WithExpiry(time.Second*30))
err = c.QueryRowNoCache(&user, key)
assert.Nil(t, err)
assert.Equal(t, value, user)
assert.True(t, ran)
}
func TestFloatKeyer(t *testing.T) {
primaries := []interface{}{
float32(1),
float64(1),
}
for _, primary := range primaries {
val := floatKeyer(func(i interface{}) string {
return fmt.Sprint(i)
})(primary)
assert.Equal(t, "1", val)
}
}
func resetStats() {
atomic.StoreUint64(&stats.Total, 0)
atomic.StoreUint64(&stats.Hit, 0)
@@ -454,6 +592,7 @@ func resetStats() {
}
type dummySqlConn struct {
queryRow func(interface{}, string, ...interface{}) error
}
func (d dummySqlConn) Exec(query string, args ...interface{}) (sql.Result, error) {
@@ -465,6 +604,9 @@ func (d dummySqlConn) Prepare(query string) (sqlx.StmtSession, error) {
}
func (d dummySqlConn) QueryRow(v interface{}, query string, args ...interface{}) error {
if d.queryRow != nil {
return d.queryRow(v, query, args...)
}
return nil
}

View File

@@ -22,6 +22,18 @@ func TestUnmarshalRowBool(t *testing.T) {
})
}
func TestUnmarshalRowBoolNotSettable(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("1")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
var value bool
assert.NotNil(t, query(db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select value from users where user=?", "anyone"))
})
}
func TestUnmarshalRowInt(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2")
@@ -228,6 +240,40 @@ func TestUnmarshalRowStructWithTags(t *testing.T) {
})
}
func TestUnmarshalRowStructWithTagsWrongColumns(t *testing.T) {
var value = new(struct {
Age *int `db:"age"`
Name string `db:"name"`
})
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name"}).FromCSVString("liao")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.NotNil(t, query(db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select name, age from users where user=?", "anyone"))
})
}
func TestUnmarshalRowStructWithTagsPtr(t *testing.T) {
var value = new(struct {
Age *int `db:"age"`
Name string `db:"name"`
})
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"name", "age"}).FromCSVString("liao,5")
mock.ExpectQuery("select (.+) from users where user=?").WithArgs("anyone").WillReturnRows(rs)
assert.Nil(t, query(db, func(rows *sql.Rows) error {
return unmarshalRow(value, rows, true)
}, "select name, age from users where user=?", "anyone"))
assert.Equal(t, "liao", value.Name)
assert.Equal(t, 5, *value.Age)
})
}
func TestUnmarshalRowsBool(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var expect = []bool{true, false}

View File

@@ -33,35 +33,42 @@ func NewSharedCalls() SharedCalls {
}
func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.lock.Lock()
if c, ok := g.calls[key]; ok {
g.lock.Unlock()
c.wg.Wait()
c, done := g.createCall(key, fn)
if done {
return c.val, c.err
}
c := g.makeCall(key, fn)
g.makeCall(c, key, fn)
return c.val, c.err
}
func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) {
c, done := g.createCall(key, fn)
if done {
return c.val, false, c.err
}
g.makeCall(c, key, fn)
return c.val, true, c.err
}
func (g *sharedGroup) createCall(key string, fn func() (interface{}, error)) (c *call, done bool) {
g.lock.Lock()
if c, ok := g.calls[key]; ok {
g.lock.Unlock()
c.wg.Wait()
return c.val, false, c.err
return c, true
}
c := g.makeCall(key, fn)
return c.val, true, c.err
}
func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call {
c := new(call)
c = new(call)
c.wg.Add(1)
g.calls[key] = c
g.lock.Unlock()
return c, false
}
func (g *sharedGroup) makeCall(c *call, key string, fn func() (interface{}, error)) {
defer func() {
// delete key first, done later. can't reverse the order, because if reverse,
// another Do call might wg.Wait() without get notified with wg.Done()
@@ -72,5 +79,4 @@ func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call
}()
c.val, c.err = fn()
return c
}

View File

@@ -69,7 +69,7 @@ And now, lets walk through the complete flow of quickly create a microservice
GO111MODULE=on go get -u github.com/tal-tech/go-zero/tools/goctl
```
* create the working dir bookstore
* create the working dir `bookstore` and `bookstore/api`
* in `bookstore` dir, execute `go mod init bookstore` to initialize `go.mod``
@@ -191,6 +191,8 @@ And now, lets walk through the complete flow of quickly create a microservice
## 6. Write code for add rpc service
- under directory `bookstore` create dir `rpc`
* under directory `rpc/add` create `add.proto` file
```shell

View File

@@ -69,7 +69,7 @@
GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl
```
* 创建工作目录`bookstore`
* 创建工作目录 `bookstore` 和 `bookstore/api`
* 在`bookstore`目录下执行`go mod init bookstore`初始化`go.mod`
@@ -191,6 +191,8 @@
## 6. 编写add rpc服务
- 在 `bookstore` 下创建 `rpc` 目录
* 在`rpc/add`目录下编写`add.proto`文件
可以通过命令生成proto文件模板
@@ -546,7 +548,7 @@
}
```
至此代码修改完成,凡手动修改的代码我加了标注
至此代码修改完成,凡手动修改的代码我加了标注
## 11. 完整调用演示

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

@@ -72,7 +72,7 @@ And now, lets walk through the complete flow of quickly create a microservice
GO111MODULE=on go get -u github.com/tal-tech/go-zero/tools/goctl
```
* create the working dir `shorturl`
* create the working dir `shorturl` and `shorturl/api`
* in `shorturl` dir, execute `go mod init shorturl` to initialize `go.mod`
@@ -195,6 +195,8 @@ And now, lets walk through the complete flow of quickly create a microservice
## 6. Write code for transform rpc service
- under directory `shorturl` create dir `rpc`
* under directory `rpc/transform` create `transform.proto` file
```shell

View File

@@ -72,7 +72,7 @@
GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl
```
* 创建工作目录`shorturl`
* 创建工作目录 `shorturl` 和 `shorturl/api`
* 在`shorturl`目录下执行`go mod init shorturl`初始化`go.mod`
@@ -195,6 +195,8 @@
## 6. 编写transform rpc服务
- 在 `shorturl` 目录下创建 `rpc` 目录
* 在`rpc/transform`目录下编写`transform.proto`文件
可以通过命令生成proto文件模板
@@ -480,7 +482,7 @@
}
```
至此代码修改完成,凡手动修改的代码我加了标注
至此代码修改完成,凡手动修改的代码我加了标注
## 10. 完整调用演示

View File

@@ -27,6 +27,13 @@ func TestParseForm(t *testing.T) {
assert.Equal(t, 3.4, v.Percent)
}
func TestParseHeader(t *testing.T) {
m := ParseHeader("key=value;")
assert.EqualValues(t, map[string]string{
"key": "value",
}, m)
}
func TestParseFormOutOfRange(t *testing.T) {
var v struct {
Age int `form:"age,range=[10:20)"`