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

View File

@@ -41,10 +41,13 @@ func GetBreaker(name string) Breaker {
} }
lock.Lock() 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 return b
} }
@@ -55,20 +58,5 @@ func NoBreakFor(name string) {
} }
func do(name string, execute func(b Breaker) error) error { func do(name string, execute func(b Breaker) error) error {
lock.RLock() return execute(GetBreaker(name))
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)
} }

View File

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

View File

@@ -27,7 +27,6 @@ func getGoogleBreaker() *googleBreaker {
return &googleBreaker{ return &googleBreaker{
stat: st, stat: st,
k: 5, k: 5,
state: StateClosed,
proba: mathx.NewProba(), 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.Nil(t, err)
assert.Equal(t, string(src), string(decryptedSrc)) 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 stringType
) )
// Set is not thread-safe, for concurrent use, make sure to use it with synchronization.
type Set struct { type Set struct {
data map[interface{}]lang.PlaceholderType data map[interface{}]lang.PlaceholderType
tp int tp int
@@ -182,10 +183,7 @@ func (s *Set) add(i interface{}) {
} }
func (s *Set) setType(i interface{}) { func (s *Set) setType(i interface{}) {
if s.tp != untyped { // s.tp can only be untyped here
return
}
switch i.(type) { switch i.(type) {
case int: case int:
s.tp = intType s.tp = intType

View File

@@ -5,8 +5,13 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/logx"
) )
func init() {
logx.Disable()
}
func BenchmarkRawSet(b *testing.B) { func BenchmarkRawSet(b *testing.B) {
m := make(map[interface{}]struct{}) m := make(map[interface{}]struct{})
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
@@ -147,3 +152,51 @@ func TestCount(t *testing.T) {
// then // then
assert.Equal(t, set.Count(), 3) 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"` A string `json:"a"`
B int `json:"b"` B int `json:"b"`
} }
err = LoadConfig(tmpfile, &val) MustLoad(tmpfile, &val)
assert.Nil(t, err)
assert.Equal(t, "foo", val.A) assert.Equal(t, "foo", val.A)
assert.Equal(t, 1, val.B) assert.Equal(t, 1, val.B)
}) })

View File

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

View File

@@ -41,3 +41,8 @@ func TestSetInt(t *testing.T) {
props.SetInt(key, value) props.SetInt(key, value)
assert.Equal(t, value, props.GetInt(key)) 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) { func TestStructedLogWithDuration(t *testing.T) {
const message = "hello there" const message = "hello there"
writer := new(mockWriter) writer := new(mockWriter)
@@ -135,6 +175,15 @@ func TestMustNil(t *testing.T) {
Must(nil) 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) { func BenchmarkCopyByteSliceAppend(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
var buf []byte var buf []byte
@@ -232,7 +281,7 @@ func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteClo
t.Error(err) t.Error(err)
} }
assert.Equal(t, level, entry.Level) 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) { func testSetLevelTwiceWithMode(t *testing.T, mode string) {

View File

@@ -192,14 +192,16 @@ func (l *RotateLogger) init() error {
} }
func (l *RotateLogger) maybeCompressFile(file string) { func (l *RotateLogger) maybeCompressFile(file string) {
if l.compress { if !l.compress {
defer func() { return
if r := recover(); r != nil {
ErrorStack(r)
}
}()
compressLogFile(file)
} }
defer func() {
if r := recover(); r != nil {
ErrorStack(r)
}
}()
compressLogFile(file)
} }
func (l *RotateLogger) maybeDeleteOutdatedFiles() { 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) writer := new(mockWriter)
infoLog = writer infoLog = writer
prevLevel := logLevel prevLevel := atomic.LoadUint32(&logLevel)
logLevel = InfoLevel SetLevel(InfoLevel)
f() f()
logLevel = prevLevel SetLevel(prevLevel)
return writer.builder.String() 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" "time"
"github.com/alicebob/miniredis" "github.com/alicebob/miniredis"
red "github.com/go-redis/redis"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestRedis_Exists(t *testing.T) { func TestRedis_Exists(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Exists("a")
assert.NotNil(t, err)
ok, err := client.Exists("a") ok, err := client.Exists("a")
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, ok) assert.False(t, ok)
@@ -24,7 +27,9 @@ func TestRedis_Exists(t *testing.T) {
func TestRedis_Eval(t *testing.T) { func TestRedis_Eval(t *testing.T) {
runOnRedis(t, func(client *Redis) { 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) assert.Equal(t, Nil, err)
err = client.Set("key1", "value1") err = client.Set("key1", "value1")
assert.Nil(t, err) assert.Nil(t, err)
@@ -40,6 +45,8 @@ func TestRedis_Hgetall(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb")) assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hgetall("a")
assert.NotNil(t, err)
vals, err := client.Hgetall("a") vals, err := client.Hgetall("a")
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, map[string]string{ assert.EqualValues(t, map[string]string{
@@ -51,8 +58,11 @@ func TestRedis_Hgetall(t *testing.T) {
func TestRedis_Hvals(t *testing.T) { func TestRedis_Hvals(t *testing.T) {
runOnRedis(t, func(client *Redis) { 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", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb")) assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hvals("a")
assert.NotNil(t, err)
vals, err := client.Hvals("a") vals, err := client.Hvals("a")
assert.Nil(t, err) assert.Nil(t, err)
assert.ElementsMatch(t, []string{"aaa", "bbb"}, vals) assert.ElementsMatch(t, []string{"aaa", "bbb"}, vals)
@@ -63,6 +73,8 @@ func TestRedis_Hsetnx(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb")) 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") ok, err := client.Hsetnx("a", "bb", "ccc")
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, ok) assert.False(t, ok)
@@ -79,6 +91,8 @@ func TestRedis_HdelHlen(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb")) assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hlen("a")
assert.NotNil(t, err)
num, err := client.Hlen("a") num, err := client.Hlen("a")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
@@ -93,6 +107,8 @@ func TestRedis_HdelHlen(t *testing.T) {
func TestRedis_HIncrBy(t *testing.T) { func TestRedis_HIncrBy(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Hincrby("key", "field", 2)
assert.NotNil(t, err)
val, err := client.Hincrby("key", "field", 2) val, err := client.Hincrby("key", "field", 2)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, val) assert.Equal(t, 2, val)
@@ -106,6 +122,8 @@ func TestRedis_Hkeys(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb")) assert.Nil(t, client.Hset("a", "bb", "bbb"))
_, err := NewRedis(client.Addr, "").Hkeys("a")
assert.NotNil(t, err)
vals, err := client.Hkeys("a") vals, err := client.Hkeys("a")
assert.Nil(t, err) assert.Nil(t, err)
assert.ElementsMatch(t, []string{"aa", "bb"}, vals) assert.ElementsMatch(t, []string{"aa", "bb"}, vals)
@@ -116,6 +134,8 @@ func TestRedis_Hmget(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.Nil(t, client.Hset("a", "aa", "aaa")) assert.Nil(t, client.Hset("a", "aa", "aaa"))
assert.Nil(t, client.Hset("a", "bb", "bbb")) 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") vals, err := client.Hmget("a", "aa", "bb")
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"aaa", "bbb"}, vals) assert.EqualValues(t, []string{"aaa", "bbb"}, vals)
@@ -127,6 +147,7 @@ func TestRedis_Hmget(t *testing.T) {
func TestRedis_Hmset(t *testing.T) { func TestRedis_Hmset(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.NotNil(t, NewRedis(client.Addr, "").Hmset("a", nil))
assert.Nil(t, client.Hmset("a", map[string]string{ assert.Nil(t, client.Hmset("a", map[string]string{
"aa": "aaa", "aa": "aaa",
"bb": "bbb", "bb": "bbb",
@@ -139,6 +160,8 @@ func TestRedis_Hmset(t *testing.T) {
func TestRedis_Incr(t *testing.T) { func TestRedis_Incr(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Incr("a")
assert.NotNil(t, err)
val, err := client.Incr("a") val, err := client.Incr("a")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(1), val) assert.Equal(t, int64(1), val)
@@ -150,6 +173,8 @@ func TestRedis_Incr(t *testing.T) {
func TestRedis_IncrBy(t *testing.T) { func TestRedis_IncrBy(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Incrby("a", 2)
assert.NotNil(t, err)
val, err := client.Incrby("a", 2) val, err := client.Incrby("a", 2)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(2), val) assert.Equal(t, int64(2), val)
@@ -165,26 +190,49 @@ func TestRedis_Keys(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
err = client.Set("key2", "value2") err = client.Set("key2", "value2")
assert.Nil(t, err) assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Keys("*")
assert.NotNil(t, err)
keys, err := client.Keys("*") keys, err := client.Keys("*")
assert.Nil(t, err) assert.Nil(t, err)
assert.ElementsMatch(t, []string{"key1", "key2"}, keys) 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) { func TestRedis_List(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Lpush("key", "value1", "value2")
assert.NotNil(t, err)
val, err := client.Lpush("key", "value1", "value2") val, err := client.Lpush("key", "value1", "value2")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, val) assert.Equal(t, 2, val)
_, err = NewRedis(client.Addr, "").Rpush("key", "value3", "value4")
assert.NotNil(t, err)
val, err = client.Rpush("key", "value3", "value4") val, err = client.Rpush("key", "value3", "value4")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 4, val) assert.Equal(t, 4, val)
_, err = NewRedis(client.Addr, "").Llen("key")
assert.NotNil(t, err)
val, err = client.Llen("key") val, err = client.Llen("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 4, val) assert.Equal(t, 4, val)
vals, err := client.Lrange("key", 0, 10) vals, err := client.Lrange("key", 0, 10)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"value2", "value1", "value3", "value4"}, vals) assert.EqualValues(t, []string{"value2", "value1", "value3", "value4"}, vals)
_, err = NewRedis(client.Addr, "").Lpop("key")
assert.NotNil(t, err)
v, err := client.Lpop("key") v, err := client.Lpop("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "value2", v) assert.Equal(t, "value2", v)
@@ -194,9 +242,13 @@ func TestRedis_List(t *testing.T) {
val, err = client.Rpush("key", "value3", "value3") val, err = client.Rpush("key", "value3", "value3")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 7, val) assert.Equal(t, 7, val)
_, err = NewRedis(client.Addr, "").Lrem("key", 2, "value1")
assert.NotNil(t, err)
n, err := client.Lrem("key", 2, "value1") n, err := client.Lrem("key", 2, "value1")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, n) assert.Equal(t, 2, n)
_, err = NewRedis(client.Addr, "").Lrange("key", 0, 10)
assert.NotNil(t, err)
vals, err = client.Lrange("key", 0, 10) vals, err = client.Lrange("key", 0, 10)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"value2", "value3", "value4", "value3", "value3"}, vals) assert.EqualValues(t, []string{"value2", "value3", "value4", "value3", "value3"}, vals)
@@ -215,6 +267,8 @@ func TestRedis_Mget(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
err = client.Set("key2", "value2") err = client.Set("key2", "value2")
assert.Nil(t, err) 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") vals, err := client.Mget("key1", "key0", "key2", "key3")
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"value1", "", "value2", ""}, vals) assert.EqualValues(t, []string{"value1", "", "value2", ""}, vals)
@@ -223,7 +277,9 @@ func TestRedis_Mget(t *testing.T) {
func TestRedis_SetBit(t *testing.T) { func TestRedis_SetBit(t *testing.T) {
runOnRedis(t, func(client *Redis) { 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) assert.Nil(t, err)
}) })
} }
@@ -232,6 +288,8 @@ func TestRedis_GetBit(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
err := client.SetBit("key", 2, 1) err := client.SetBit("key", 2, 1)
assert.Nil(t, err) assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").GetBit("key", 2)
assert.NotNil(t, err)
val, err := client.GetBit("key", 2) val, err := client.GetBit("key", 2)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, val) assert.Equal(t, 1, val)
@@ -240,6 +298,8 @@ func TestRedis_GetBit(t *testing.T) {
func TestRedis_Persist(t *testing.T) { func TestRedis_Persist(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
_, err := NewRedis(client.Addr, "").Persist("key")
assert.NotNil(t, err)
ok, err := client.Persist("key") ok, err := client.Persist("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, ok) assert.False(t, ok)
@@ -248,11 +308,15 @@ func TestRedis_Persist(t *testing.T) {
ok, err = client.Persist("key") ok, err = client.Persist("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, ok) assert.False(t, ok)
err = NewRedis(client.Addr, "").Expire("key", 5)
assert.NotNil(t, err)
err = client.Expire("key", 5) err = client.Expire("key", 5)
assert.Nil(t, err) assert.Nil(t, err)
ok, err = client.Persist("key") ok, err = client.Persist("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) 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) err = client.Expireat("key", time.Now().Unix()+5)
assert.Nil(t, err) assert.Nil(t, err)
ok, err = client.Persist("key") ok, err = client.Persist("key")
@@ -274,6 +338,8 @@ func TestRedis_Scan(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
err = client.Set("key2", "value2") err = client.Set("key2", "value2")
assert.Nil(t, err) assert.Nil(t, err)
_, _, err = NewRedis(client.Addr, "").Scan(0, "*", 100)
assert.NotNil(t, err)
keys, _, err := client.Scan(0, "*", 100) keys, _, err := client.Scan(0, "*", 100)
assert.Nil(t, err) assert.Nil(t, err)
assert.ElementsMatch(t, []string{"key1", "key2"}, keys) assert.ElementsMatch(t, []string{"key1", "key2"}, keys)
@@ -294,6 +360,8 @@ func TestRedis_Sscan(t *testing.T) {
var cursor uint64 = 0 var cursor uint64 = 0
sum := 0 sum := 0
for { for {
_, _, err := NewRedis(client.Addr, "").Sscan(key, cursor, "", 100)
assert.NotNil(t, err)
keys, next, err := client.Sscan(key, cursor, "", 100) keys, next, err := client.Sscan(key, cursor, "", 100)
assert.Nil(t, err) assert.Nil(t, err)
sum += len(keys) sum += len(keys)
@@ -304,6 +372,8 @@ func TestRedis_Sscan(t *testing.T) {
} }
assert.Equal(t, sum, 1550) assert.Equal(t, sum, 1550)
_, err = NewRedis(client.Addr, "").Del(key)
assert.NotNil(t, err)
_, err = client.Del(key) _, err = client.Del(key)
assert.Nil(t, err) assert.Nil(t, err)
}) })
@@ -311,46 +381,72 @@ func TestRedis_Sscan(t *testing.T) {
func TestRedis_Set(t *testing.T) { func TestRedis_Set(t *testing.T) {
runOnRedis(t, func(client *Redis) { 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) num, err := client.Sadd("key", 1, 2, 3, 4)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 4, num) assert.Equal(t, 4, num)
_, err = NewRedis(client.Addr, "").Scard("key")
assert.NotNil(t, err)
val, err := client.Scard("key") val, err := client.Scard("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(4), val) assert.Equal(t, int64(4), val)
_, err = NewRedis(client.Addr, "").Sismember("key", 2)
assert.NotNil(t, err)
ok, err := client.Sismember("key", 2) ok, err := client.Sismember("key", 2)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Srem("key", 3, 4)
assert.NotNil(t, err)
num, err = client.Srem("key", 3, 4) num, err = client.Srem("key", 3, 4)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
_, err = NewRedis(client.Addr, "").Smembers("key")
assert.NotNil(t, err)
vals, err := client.Smembers("key") vals, err := client.Smembers("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.ElementsMatch(t, []string{"1", "2"}, vals) assert.ElementsMatch(t, []string{"1", "2"}, vals)
_, err = NewRedis(client.Addr, "").Srandmember("key", 1)
assert.NotNil(t, err)
members, err := client.Srandmember("key", 1) members, err := client.Srandmember("key", 1)
assert.Nil(t, err) assert.Nil(t, err)
assert.Len(t, members, 1) assert.Len(t, members, 1)
assert.Contains(t, []string{"1", "2"}, members[0]) assert.Contains(t, []string{"1", "2"}, members[0])
_, err = NewRedis(client.Addr, "").Spop("key")
assert.NotNil(t, err)
member, err := client.Spop("key") member, err := client.Spop("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.Contains(t, []string{"1", "2"}, member) assert.Contains(t, []string{"1", "2"}, member)
_, err = NewRedis(client.Addr, "").Smembers("key")
assert.NotNil(t, err)
vals, err = client.Smembers("key") vals, err = client.Smembers("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.NotContains(t, vals, member) 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) num, err = client.Sadd("key1", 1, 2, 3, 4)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 4, num) assert.Equal(t, 4, num)
num, err = client.Sadd("key2", 2, 3, 4, 5) num, err = client.Sadd("key2", 2, 3, 4, 5)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 4, num) assert.Equal(t, 4, num)
_, err = NewRedis(client.Addr, "").Sunion("key1", "key2")
assert.NotNil(t, err)
vals, err = client.Sunion("key1", "key2") vals, err = client.Sunion("key1", "key2")
assert.Nil(t, err) assert.Nil(t, err)
assert.ElementsMatch(t, []string{"1", "2", "3", "4", "5"}, vals) 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") num, err = client.Sunionstore("key3", "key1", "key2")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 5, num) assert.Equal(t, 5, num)
_, err = NewRedis(client.Addr, "").Sdiff("key1", "key2")
assert.NotNil(t, err)
vals, err = client.Sdiff("key1", "key2") vals, err = client.Sdiff("key1", "key2")
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"1"}, vals) 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") num, err = client.Sdiffstore("key4", "key1", "key2")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@@ -359,8 +455,12 @@ func TestRedis_Set(t *testing.T) {
func TestRedis_SetGetDel(t *testing.T) { func TestRedis_SetGetDel(t *testing.T) {
runOnRedis(t, func(client *Redis) { 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) assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Get("hello")
assert.NotNil(t, err)
val, err := client.Get("hello") val, err := client.Get("hello")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "world", val) assert.Equal(t, "world", val)
@@ -372,8 +472,12 @@ func TestRedis_SetGetDel(t *testing.T) {
func TestRedis_SetExNx(t *testing.T) { func TestRedis_SetExNx(t *testing.T) {
runOnRedis(t, func(client *Redis) { 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) assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Setnx("hello", "newworld")
assert.NotNil(t, err)
ok, err := client.Setnx("hello", "newworld") ok, err := client.Setnx("hello", "newworld")
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, ok) assert.False(t, ok)
@@ -389,6 +493,8 @@ func TestRedis_SetExNx(t *testing.T) {
ttl, err := client.Ttl("hello") ttl, err := client.Ttl("hello")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ttl > 0) assert.True(t, ttl > 0)
_, err = NewRedis(client.Addr, "").SetnxEx("newhello", "newworld", 5)
assert.NotNil(t, err)
ok, err = client.SetnxEx("newhello", "newworld", 5) ok, err = client.SetnxEx("newhello", "newworld", 5)
assert.Nil(t, err) assert.Nil(t, err)
assert.False(t, ok) assert.False(t, ok)
@@ -408,12 +514,18 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
err := client.Hset("key", "field", "value") err := client.Hset("key", "field", "value")
assert.Nil(t, err) assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Hget("key", "field")
assert.NotNil(t, err)
val, err := client.Hget("key", "field") val, err := client.Hget("key", "field")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "value", val) assert.Equal(t, "value", val)
_, err = NewRedis(client.Addr, "").Hexists("key", "field")
assert.NotNil(t, err)
ok, err := client.Hexists("key", "field") ok, err := client.Hexists("key", "field")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Hdel("key", "field")
assert.NotNil(t, err)
ret, err := client.Hdel("key", "field") ret, err := client.Hdel("key", "field")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ret) assert.True(t, ret)
@@ -434,23 +546,50 @@ func TestRedis_SortedSet(t *testing.T) {
val, err := client.Zscore("key", "value1") val, err := client.Zscore("key", "value1")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(2), val) 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") val, err = client.Zincrby("key", 3, "value1")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(5), val) assert.Equal(t, int64(5), val)
_, err = NewRedis(client.Addr, "").Zscore("key", "value1")
assert.NotNil(t, err)
val, err = client.Zscore("key", "value1") val, err = client.Zscore("key", "value1")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(5), val) 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.Nil(t, err)
assert.True(t, ok) assert.Equal(t, int64(2), val)
ok, err = client.Zadd("key", 7, "value3") 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.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") rank, err := client.Zrank("key", "value2")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(1), rank) assert.Equal(t, int64(1), rank)
_, err = NewRedis(client.Addr, "").Zrank("key", "value4")
assert.NotNil(t, err)
_, err = client.Zrank("key", "value4") _, err = client.Zrank("key", "value4")
assert.Equal(t, Nil, err) assert.Equal(t, Nil, err)
_, err = NewRedis(client.Addr, "").Zrem("key", "value2", "value3")
assert.NotNil(t, err)
num, err := client.Zrem("key", "value2", "value3") num, err := client.Zrem("key", "value2", "value3")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
@@ -463,31 +602,47 @@ func TestRedis_SortedSet(t *testing.T) {
ok, err = client.Zadd("key", 8, "value4") ok, err = client.Zadd("key", 8, "value4")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Zremrangebyscore("key", 6, 7)
assert.NotNil(t, err)
num, err = client.Zremrangebyscore("key", 6, 7) num, err = client.Zremrangebyscore("key", 6, 7)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
ok, err = client.Zadd("key", 6, "value2") ok, err = client.Zadd("key", 6, "value2")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Zadd("key", 7, "value3")
assert.NotNil(t, err)
ok, err = client.Zadd("key", 7, "value3") ok, err = client.Zadd("key", 7, "value3")
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, ok) assert.True(t, ok)
_, err = NewRedis(client.Addr, "").Zcount("key", 6, 7)
assert.NotNil(t, err)
num, err = client.Zcount("key", 6, 7) num, err = client.Zcount("key", 6, 7)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
_, err = NewRedis(client.Addr, "").Zremrangebyrank("key", 1, 2)
assert.NotNil(t, err)
num, err = client.Zremrangebyrank("key", 1, 2) num, err = client.Zremrangebyrank("key", 1, 2)
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
_, err = NewRedis(client.Addr, "").Zcard("key")
assert.NotNil(t, err)
card, err := client.Zcard("key") card, err := client.Zcard("key")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, card) assert.Equal(t, 2, card)
_, err = NewRedis(client.Addr, "").Zrange("key", 0, -1)
assert.NotNil(t, err)
vals, err := client.Zrange("key", 0, -1) vals, err := client.Zrange("key", 0, -1)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"value1", "value4"}, vals) 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) vals, err = client.Zrevrange("key", 0, -1)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []string{"value4", "value1"}, vals) 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.Nil(t, err)
assert.EqualValues(t, []Pair{ assert.EqualValues(t, []Pair{
{ {
@@ -499,6 +654,8 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 8, Score: 8,
}, },
}, pairs) }, pairs)
_, err = NewRedis(client.Addr, "").ZrangebyscoreWithScores("key", 5, 8)
assert.NotNil(t, err)
pairs, err = client.ZrangebyscoreWithScores("key", 5, 8) pairs, err = client.ZrangebyscoreWithScores("key", 5, 8)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []Pair{ assert.EqualValues(t, []Pair{
@@ -511,6 +668,9 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 8, Score: 8,
}, },
}, pairs) }, pairs)
_, err = NewRedis(client.Addr, "").ZrangebyscoreWithScoresAndLimit(
"key", 5, 8, 1, 1)
assert.NotNil(t, err)
pairs, err = client.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1) pairs, err = client.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []Pair{ assert.EqualValues(t, []Pair{
@@ -519,6 +679,11 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 8, Score: 8,
}, },
}, pairs) }, 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) pairs, err = client.ZrevrangebyscoreWithScores("key", 5, 8)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []Pair{ assert.EqualValues(t, []Pair{
@@ -531,6 +696,9 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 5, Score: 5,
}, },
}, pairs) }, pairs)
_, err = NewRedis(client.Addr, "").ZrevrangebyscoreWithScoresAndLimit(
"key", 5, 8, 1, 1)
assert.NotNil(t, err)
pairs, err = client.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1) pairs, err = client.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1)
assert.Nil(t, err) assert.Nil(t, err)
assert.EqualValues(t, []Pair{ assert.EqualValues(t, []Pair{
@@ -539,11 +707,17 @@ func TestRedis_SortedSet(t *testing.T) {
Score: 5, Score: 5,
}, },
}, pairs) }, 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) { func TestRedis_Pipelined(t *testing.T) {
runOnRedis(t, func(client *Redis) { runOnRedis(t, func(client *Redis) {
assert.NotNil(t, NewRedis(client.Addr, "").Pipelined(func(pipeliner Pipeliner) error {
return nil
}))
err := client.Pipelined( err := client.Pipelined(
func(pipe Pipeliner) error { func(pipe Pipeliner) error {
pipe.Incr("pipelined_counter") pipe.Incr("pipelined_counter")
@@ -553,6 +727,8 @@ func TestRedis_Pipelined(t *testing.T) {
}, },
) )
assert.Nil(t, err) assert.Nil(t, err)
_, err = NewRedis(client.Addr, "").Ttl("pipelined_counter")
assert.NotNil(t, err)
ttl, err := client.Ttl("pipelined_counter") ttl, err := client.Ttl("pipelined_counter")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 3600, ttl) 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)) { func runOnRedis(t *testing.T, fn func(client *Redis)) {
s, err := miniredis.Run() s, err := miniredis.Run()
assert.Nil(t, err) assert.Nil(t, err)
@@ -576,8 +822,18 @@ func runOnRedis(t *testing.T, fn func(client *Redis)) {
t.Error(err) t.Error(err)
} }
client.Close() if client != nil {
client.Close()
}
}() }()
fn(NewRedis(s.Addr(), NodeType)) 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 { indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
var primaryKey interface{} var primaryKey interface{}
var found bool 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) { if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) {
primaryKey, err = indexQuery(cc.db, v) primaryKey, err = indexQuery(cc.db, v)
if err != nil { 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 { func (cc CachedConn) Transact(fn func(sqlx.Session) error) error {
return cc.db.Transact(fn) 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) 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) { func TestCachedConn_QueryRowIndex_HasWrongCache(t *testing.T) {
caches := map[string]string{ caches := map[string]string{
"index": "primary", "index": "primary",
@@ -148,6 +245,8 @@ func TestCachedConn_QueryRowIndex_HasWrongCache(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
s.FlushAll()
defer s.Close()
r := redis.NewRedis(s.Addr(), redis.NodeType) r := redis.NewRedis(s.Addr(), redis.NodeType)
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10), c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10),
@@ -446,6 +545,45 @@ func TestCachedConnTransact(t *testing.T) {
assert.True(t, conn.transactValue) 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() { func resetStats() {
atomic.StoreUint64(&stats.Total, 0) atomic.StoreUint64(&stats.Total, 0)
atomic.StoreUint64(&stats.Hit, 0) atomic.StoreUint64(&stats.Hit, 0)
@@ -454,6 +592,7 @@ func resetStats() {
} }
type dummySqlConn struct { type dummySqlConn struct {
queryRow func(interface{}, string, ...interface{}) error
} }
func (d dummySqlConn) Exec(query string, args ...interface{}) (sql.Result, 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 { func (d dummySqlConn) QueryRow(v interface{}, query string, args ...interface{}) error {
if d.queryRow != nil {
return d.queryRow(v, query, args...)
}
return nil 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) { func TestUnmarshalRowInt(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) { runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
rs := sqlmock.NewRows([]string{"value"}).FromCSVString("2") 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) { func TestUnmarshalRowsBool(t *testing.T) {
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) { runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
var expect = []bool{true, false} 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) { func (g *sharedGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
g.lock.Lock() c, done := g.createCall(key, fn)
if c, ok := g.calls[key]; ok { if done {
g.lock.Unlock()
c.wg.Wait()
return c.val, c.err return c.val, c.err
} }
c := g.makeCall(key, fn) g.makeCall(c, key, fn)
return c.val, c.err return c.val, c.err
} }
func (g *sharedGroup) DoEx(key string, fn func() (interface{}, error)) (val interface{}, fresh bool, err error) { 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() g.lock.Lock()
if c, ok := g.calls[key]; ok { if c, ok := g.calls[key]; ok {
g.lock.Unlock() g.lock.Unlock()
c.wg.Wait() c.wg.Wait()
return c.val, false, c.err return c, true
} }
c := g.makeCall(key, fn) c = new(call)
return c.val, true, c.err
}
func (g *sharedGroup) makeCall(key string, fn func() (interface{}, error)) *call {
c := new(call)
c.wg.Add(1) c.wg.Add(1)
g.calls[key] = c g.calls[key] = c
g.lock.Unlock() g.lock.Unlock()
return c, false
}
func (g *sharedGroup) makeCall(c *call, key string, fn func() (interface{}, error)) {
defer func() { defer func() {
// delete key first, done later. can't reverse the order, because if reverse, // 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() // 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() 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 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`` * 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 ## 6. Write code for add rpc service
- under directory `bookstore` create dir `rpc`
* under directory `rpc/add` create `add.proto` file * under directory `rpc/add` create `add.proto` file
```shell ```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 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` * 在`bookstore`目录下执行`go mod init bookstore`初始化`go.mod`
@@ -191,6 +191,8 @@
## 6. 编写add rpc服务 ## 6. 编写add rpc服务
- 在 `bookstore` 下创建 `rpc` 目录
* 在`rpc/add`目录下编写`add.proto`文件 * 在`rpc/add`目录下编写`add.proto`文件
可以通过命令生成proto文件模板 可以通过命令生成proto文件模板
@@ -546,7 +548,7 @@
} }
``` ```
至此代码修改完成,凡手动修改的代码我加了标注 至此代码修改完成,凡手动修改的代码我加了标注
## 11. 完整调用演示 ## 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 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` * 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 ## 6. Write code for transform rpc service
- under directory `shorturl` create dir `rpc`
* under directory `rpc/transform` create `transform.proto` file * under directory `rpc/transform` create `transform.proto` file
```shell ```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 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` * 在`shorturl`目录下执行`go mod init shorturl`初始化`go.mod`
@@ -195,6 +195,8 @@
## 6. 编写transform rpc服务 ## 6. 编写transform rpc服务
- 在 `shorturl` 目录下创建 `rpc` 目录
* 在`rpc/transform`目录下编写`transform.proto`文件 * 在`rpc/transform`目录下编写`transform.proto`文件
可以通过命令生成proto文件模板 可以通过命令生成proto文件模板
@@ -480,7 +482,7 @@
} }
``` ```
至此代码修改完成,凡手动修改的代码我加了标注 至此代码修改完成,凡手动修改的代码我加了标注
## 10. 完整调用演示 ## 10. 完整调用演示

View File

@@ -27,6 +27,13 @@ func TestParseForm(t *testing.T) {
assert.Equal(t, 3.4, v.Percent) 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) { func TestParseFormOutOfRange(t *testing.T) {
var v struct { var v struct {
Age int `form:"age,range=[10:20)"` Age int `form:"age,range=[10:20)"`