mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-11 16:59:59 +08:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe855c52f1 | ||
|
|
3f8b080882 | ||
|
|
adc275872d | ||
|
|
be39133dba | ||
|
|
15a9ab1d18 | ||
|
|
7c354dcc38 | ||
|
|
3733b06f1b | ||
|
|
8115a0932e | ||
|
|
4df5eb760c | ||
|
|
4a639b853c | ||
|
|
1023425c1d | ||
|
|
360fbfd0fa | ||
|
|
09b7625f06 | ||
|
|
6db294b5cc | ||
|
|
305b6749fd | ||
|
|
10b855713d | ||
|
|
1cc0f071d9 | ||
|
|
02ce8f82c8 | ||
|
|
8a585afbf0 | ||
|
|
e356025cef | ||
|
|
14dee114dd | ||
|
|
637a94a189 | ||
|
|
173b347c90 | ||
|
|
6749c5b94a | ||
|
|
e66cca3710 | ||
|
|
f90c0aa98e | ||
|
|
f00b5416a3 | ||
|
|
f49694d6b6 | ||
|
|
d809bf2dca | ||
|
|
44ae5463bc | ||
|
|
40dbd722d7 | ||
|
|
709574133b | ||
|
|
cb1c593108 | ||
|
|
6ecf575c00 | ||
|
|
b8fcdd5460 | ||
|
|
ce42281568 | ||
|
|
40230d79e7 | ||
|
|
ba7851795b | ||
|
|
096fe3bc47 | ||
|
|
e37858295a | ||
|
|
5a4afb1518 | ||
|
|
63f1f39c40 | ||
|
|
481895d1e4 | ||
|
|
9e9ce3bf48 | ||
|
|
0ce654968d | ||
|
|
2703493541 | ||
|
|
d4240cd4b0 | ||
|
|
a22bcc84a3 |
@@ -1,4 +1,4 @@
|
||||
ignore:
|
||||
- "doc"
|
||||
- "example"
|
||||
- "tools"
|
||||
- "tools"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"MD010": false,
|
||||
"MD013": false,
|
||||
"MD033": false,
|
||||
"MD034": false
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ func getGoogleBreaker() *googleBreaker {
|
||||
return &googleBreaker{
|
||||
stat: st,
|
||||
k: 5,
|
||||
state: StateClosed,
|
||||
proba: mathx.NewProba(),
|
||||
}
|
||||
}
|
||||
|
||||
80
core/cmdline/input_test.go
Normal file
80
core/cmdline/input_test.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package cmdline
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/iox"
|
||||
"github.com/tal-tech/go-zero/core/lang"
|
||||
)
|
||||
|
||||
func TestEnterToContinue(t *testing.T) {
|
||||
restore, err := iox.RedirectInOut()
|
||||
assert.Nil(t, err)
|
||||
defer restore()
|
||||
|
||||
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:
|
||||
}
|
||||
}
|
||||
@@ -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
19
core/codec/hmac_test.go
Normal 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
59
core/codec/rsa_test.go
Normal 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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
//go:generate mockgen -package internal -destination listener_mock.go -source listener.go Listener
|
||||
package internal
|
||||
|
||||
type Listener interface {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: listener.go
|
||||
|
||||
// Package internal is a generated GoMock package.
|
||||
package internal
|
||||
|
||||
import (
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockListener is a mock of Listener interface
|
||||
type MockListener struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockListenerMockRecorder
|
||||
}
|
||||
|
||||
// MockListenerMockRecorder is the mock recorder for MockListener
|
||||
type MockListenerMockRecorder struct {
|
||||
mock *MockListener
|
||||
}
|
||||
|
||||
// NewMockListener creates a new mock instance
|
||||
func NewMockListener(ctrl *gomock.Controller) *MockListener {
|
||||
mock := &MockListener{ctrl: ctrl}
|
||||
mock.recorder = &MockListenerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockListener) EXPECT() *MockListenerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// OnUpdate mocks base method
|
||||
func (m *MockListener) OnUpdate(keys, values []string, newKey string) {
|
||||
m.ctrl.T.Helper()
|
||||
m.ctrl.Call(m, "OnUpdate", keys, values, newKey)
|
||||
}
|
||||
|
||||
// OnUpdate indicates an expected call of OnUpdate
|
||||
func (mr *MockListenerMockRecorder) OnUpdate(keys, values, newKey interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnUpdate", reflect.TypeOf((*MockListener)(nil).OnUpdate), keys, values, newKey)
|
||||
}
|
||||
@@ -111,6 +111,10 @@ func TestPublisher_keepAliveAsyncQuit(t *testing.T) {
|
||||
defer ctrl.Finish()
|
||||
const id clientv3.LeaseID = 1
|
||||
cli := internal.NewMockEtcdClient(ctrl)
|
||||
cli.EXPECT().ActiveConnection()
|
||||
cli.EXPECT().Close()
|
||||
defer cli.Close()
|
||||
cli.ActiveConnection()
|
||||
restore := setMockClient(cli)
|
||||
defer restore()
|
||||
cli.EXPECT().Ctx().AnyTimes()
|
||||
|
||||
@@ -84,6 +84,14 @@ func (p Stream) Buffer(n int) Stream {
|
||||
return Range(source)
|
||||
}
|
||||
|
||||
// Count counts the number of elements in the result.
|
||||
func (p Stream) Count() (count int) {
|
||||
for range p.source {
|
||||
count++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Distinct removes the duplicated items base on the given KeyFunc.
|
||||
func (p Stream) Distinct(fn KeyFunc) Stream {
|
||||
source := make(chan interface{})
|
||||
|
||||
@@ -49,6 +49,36 @@ func TestBufferNegative(t *testing.T) {
|
||||
assert.Equal(t, 10, result)
|
||||
}
|
||||
|
||||
func TestCount(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
elements []interface{}
|
||||
}{
|
||||
{
|
||||
name: "no elements with nil",
|
||||
},
|
||||
{
|
||||
name: "no elements",
|
||||
elements: []interface{}{},
|
||||
},
|
||||
{
|
||||
name: "1 element",
|
||||
elements: []interface{}{1},
|
||||
},
|
||||
{
|
||||
name: "multiple elements",
|
||||
elements: []interface{}{1, 2, 3},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
val := Just(test.elements...).Count()
|
||||
assert.Equal(t, len(test.elements), val)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDone(t *testing.T) {
|
||||
var count int32
|
||||
Just(1, 2, 3).Walk(func(item interface{}, pipe chan<- interface{}) {
|
||||
|
||||
23
core/iox/pipe.go
Normal file
23
core/iox/pipe.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package iox
|
||||
|
||||
import "os"
|
||||
|
||||
// RedirectInOut redirects stdin to r, stdout to w, and callers need to call restore afterwards.
|
||||
func RedirectInOut() (restore func(), err error) {
|
||||
var r, w *os.File
|
||||
r, w, err = os.Pipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ow := os.Stdout
|
||||
os.Stdout = w
|
||||
or := os.Stdin
|
||||
os.Stdin = r
|
||||
restore = func() {
|
||||
os.Stdin = or
|
||||
os.Stdout = ow
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
13
core/iox/pipe_test.go
Normal file
13
core/iox/pipe_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package iox
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRedirectInOut(t *testing.T) {
|
||||
restore, err := RedirectInOut()
|
||||
assert.Nil(t, err)
|
||||
defer restore()
|
||||
}
|
||||
@@ -135,6 +135,7 @@ func TestAdaptiveShedderShouldDrop(t *testing.T) {
|
||||
passCounter: passCounter,
|
||||
rtCounter: rtCounter,
|
||||
windows: buckets,
|
||||
dropTime: syncx.NewAtomicDuration(),
|
||||
droppedRecently: syncx.NewAtomicBool(),
|
||||
}
|
||||
// cpu >= 800, inflight < maxPass
|
||||
@@ -160,6 +161,40 @@ func TestAdaptiveShedderShouldDrop(t *testing.T) {
|
||||
}
|
||||
shedder.avgFlying = 80
|
||||
assert.False(t, shedder.shouldDrop())
|
||||
|
||||
// cpu >= 800, inflight < maxPass
|
||||
systemOverloadChecker = func(int64) bool {
|
||||
return true
|
||||
}
|
||||
shedder.avgFlying = 80
|
||||
shedder.flying = 80
|
||||
_, err := shedder.Allow()
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestAdaptiveShedderStillHot(t *testing.T) {
|
||||
logx.Disable()
|
||||
passCounter := newRollingWindow()
|
||||
rtCounter := newRollingWindow()
|
||||
for i := 0; i < 10; i++ {
|
||||
if i > 0 {
|
||||
time.Sleep(bucketDuration)
|
||||
}
|
||||
passCounter.Add(float64((i + 1) * 100))
|
||||
for j := i*10 + 1; j <= i*10+10; j++ {
|
||||
rtCounter.Add(float64(j))
|
||||
}
|
||||
}
|
||||
shedder := &adaptiveShedder{
|
||||
passCounter: passCounter,
|
||||
rtCounter: rtCounter,
|
||||
windows: buckets,
|
||||
dropTime: syncx.NewAtomicDuration(),
|
||||
droppedRecently: syncx.ForAtomicBool(true),
|
||||
}
|
||||
assert.False(t, shedder.stillHot())
|
||||
shedder.dropTime.Set(-coolOffDuration * 2)
|
||||
assert.False(t, shedder.stillHot())
|
||||
}
|
||||
|
||||
func BenchmarkAdaptiveShedder_Allow(b *testing.B) {
|
||||
|
||||
@@ -13,3 +13,8 @@ func TestGroup(t *testing.T) {
|
||||
assert.NotNil(t, limiter)
|
||||
})
|
||||
}
|
||||
|
||||
func TestShedderClose(t *testing.T) {
|
||||
var nop nopCloser
|
||||
assert.Nil(t, nop.Close())
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -21,10 +23,13 @@ var (
|
||||
)
|
||||
|
||||
type mockWriter struct {
|
||||
lock sync.Mutex
|
||||
builder strings.Builder
|
||||
}
|
||||
|
||||
func (mw *mockWriter) Write(data []byte) (int, error) {
|
||||
mw.lock.Lock()
|
||||
defer mw.lock.Unlock()
|
||||
return mw.builder.Write(data)
|
||||
}
|
||||
|
||||
@@ -32,12 +37,22 @@ func (mw *mockWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mw *mockWriter) Contains(text string) bool {
|
||||
mw.lock.Lock()
|
||||
defer mw.lock.Unlock()
|
||||
return strings.Contains(mw.builder.String(), text)
|
||||
}
|
||||
|
||||
func (mw *mockWriter) Reset() {
|
||||
mw.lock.Lock()
|
||||
defer mw.lock.Unlock()
|
||||
mw.builder.Reset()
|
||||
}
|
||||
|
||||
func (mw *mockWriter) Contains(text string) bool {
|
||||
return strings.Contains(mw.builder.String(), text)
|
||||
func (mw *mockWriter) String() string {
|
||||
mw.lock.Lock()
|
||||
defer mw.lock.Unlock()
|
||||
return mw.builder.String()
|
||||
}
|
||||
|
||||
func TestFileLineFileMode(t *testing.T) {
|
||||
@@ -85,6 +100,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 +190,66 @@ func TestMustNil(t *testing.T) {
|
||||
Must(nil)
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
MustSetup(LogConf{
|
||||
ServiceName: "any",
|
||||
Mode: "console",
|
||||
})
|
||||
MustSetup(LogConf{
|
||||
ServiceName: "any",
|
||||
Mode: "file",
|
||||
Path: os.TempDir(),
|
||||
})
|
||||
MustSetup(LogConf{
|
||||
ServiceName: "any",
|
||||
Mode: "volume",
|
||||
Path: os.TempDir(),
|
||||
})
|
||||
assert.NotNil(t, setupWithVolume(LogConf{}))
|
||||
assert.NotNil(t, setupWithFiles(LogConf{}))
|
||||
assert.Nil(t, setupWithFiles(LogConf{
|
||||
ServiceName: "any",
|
||||
Path: os.TempDir(),
|
||||
Compress: true,
|
||||
KeepDays: 1,
|
||||
}))
|
||||
setupLogLevel(LogConf{
|
||||
Level: levelInfo,
|
||||
})
|
||||
setupLogLevel(LogConf{
|
||||
Level: levelError,
|
||||
})
|
||||
setupLogLevel(LogConf{
|
||||
Level: levelSevere,
|
||||
})
|
||||
_, err := createOutput("")
|
||||
assert.NotNil(t, err)
|
||||
Disable()
|
||||
}
|
||||
|
||||
func TestDisable(t *testing.T) {
|
||||
Disable()
|
||||
WithKeepDays(1)
|
||||
WithGzip()
|
||||
assert.Nil(t, Close())
|
||||
writeConsole = false
|
||||
assert.Nil(t, Close())
|
||||
}
|
||||
|
||||
func TestWithGzip(t *testing.T) {
|
||||
fn := WithGzip()
|
||||
var opt logOptions
|
||||
fn(&opt)
|
||||
assert.True(t, opt.gzipEnabled)
|
||||
}
|
||||
|
||||
func TestWithKeepDays(t *testing.T) {
|
||||
fn := WithKeepDays(1)
|
||||
var opt logOptions
|
||||
fn(&opt)
|
||||
assert.Equal(t, 1, opt.keepDays)
|
||||
}
|
||||
|
||||
func BenchmarkCopyByteSliceAppend(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var buf []byte
|
||||
@@ -232,7 +347,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) {
|
||||
@@ -252,4 +367,10 @@ func testSetLevelTwiceWithMode(t *testing.T, mode string) {
|
||||
atomic.StoreUint32(&initialized, 1)
|
||||
Info(message)
|
||||
assert.Equal(t, 0, writer.builder.Len())
|
||||
Infof(message)
|
||||
assert.Equal(t, 0, writer.builder.Len())
|
||||
ErrorStack(message)
|
||||
assert.Equal(t, 0, writer.builder.Len())
|
||||
ErrorStackf(message)
|
||||
assert.Equal(t, 0, writer.builder.Len())
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
119
core/logx/rotatelogger_test.go
Normal file
119
core/logx/rotatelogger_test.go
Normal 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`))
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@ package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/trace/tracespec"
|
||||
@@ -17,13 +20,76 @@ const (
|
||||
var mock tracespec.Trace = new(mockTrace)
|
||||
|
||||
func TestTraceLog(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
var buf mockWriter
|
||||
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
|
||||
WithContext(ctx).(*traceLogger).write(&buf, levelInfo, testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
}
|
||||
|
||||
func TestTraceError(t *testing.T) {
|
||||
var buf mockWriter
|
||||
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
|
||||
l := WithContext(ctx).(*traceLogger)
|
||||
SetLevel(InfoLevel)
|
||||
atomic.StoreUint32(&initialized, 1)
|
||||
errorLog = newLogWriter(log.New(&buf, "", flags))
|
||||
l.WithDuration(time.Second).Error(testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
buf.Reset()
|
||||
l.WithDuration(time.Second).Errorf(testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
}
|
||||
|
||||
func TestTraceInfo(t *testing.T) {
|
||||
var buf mockWriter
|
||||
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
|
||||
l := WithContext(ctx).(*traceLogger)
|
||||
SetLevel(InfoLevel)
|
||||
atomic.StoreUint32(&initialized, 1)
|
||||
infoLog = newLogWriter(log.New(&buf, "", flags))
|
||||
l.WithDuration(time.Second).Info(testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
buf.Reset()
|
||||
l.WithDuration(time.Second).Infof(testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
}
|
||||
|
||||
func TestTraceSlow(t *testing.T) {
|
||||
var buf mockWriter
|
||||
ctx := context.WithValue(context.Background(), tracespec.TracingKey, mock)
|
||||
l := WithContext(ctx).(*traceLogger)
|
||||
SetLevel(InfoLevel)
|
||||
atomic.StoreUint32(&initialized, 1)
|
||||
slowLog = newLogWriter(log.New(&buf, "", flags))
|
||||
l.WithDuration(time.Second).Slow(testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
buf.Reset()
|
||||
l.WithDuration(time.Second).Slowf(testlog)
|
||||
assert.True(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.True(t, strings.Contains(buf.String(), mockSpanId))
|
||||
}
|
||||
|
||||
func TestTraceWithoutContext(t *testing.T) {
|
||||
var buf mockWriter
|
||||
l := WithContext(context.Background()).(*traceLogger)
|
||||
SetLevel(InfoLevel)
|
||||
atomic.StoreUint32(&initialized, 1)
|
||||
infoLog = newLogWriter(log.New(&buf, "", flags))
|
||||
l.WithDuration(time.Second).Info(testlog)
|
||||
assert.False(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.False(t, strings.Contains(buf.String(), mockSpanId))
|
||||
buf.Reset()
|
||||
l.WithDuration(time.Second).Infof(testlog)
|
||||
assert.False(t, strings.Contains(buf.String(), mockTraceId))
|
||||
assert.False(t, strings.Contains(buf.String(), mockSpanId))
|
||||
}
|
||||
|
||||
type mockTrace struct{}
|
||||
|
||||
func (t mockTrace) TraceId() string {
|
||||
|
||||
22
core/stat/alert_test.go
Normal file
22
core/stat/alert_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// +build linux
|
||||
|
||||
package stat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReport(t *testing.T) {
|
||||
var count int32
|
||||
SetReporter(func(s string) {
|
||||
atomic.AddInt32(&count, 1)
|
||||
})
|
||||
for i := 0; i < 10; i++ {
|
||||
Report(strconv.Itoa(i))
|
||||
}
|
||||
assert.Equal(t, int32(1), count)
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
package internal
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRefreshCpu(t *testing.T) {
|
||||
assert.True(t, RefreshCpu() >= 0)
|
||||
}
|
||||
|
||||
func BenchmarkRefreshCpu(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
37
core/stat/metrics_test.go
Normal file
37
core/stat/metrics_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package stat
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMetrics(t *testing.T) {
|
||||
counts := []int{1, 5, 10, 100, 1000, 1000}
|
||||
for _, count := range counts {
|
||||
m := NewMetrics("foo")
|
||||
m.SetName("bar")
|
||||
for i := 0; i < count; i++ {
|
||||
m.Add(Task{
|
||||
Duration: time.Millisecond * time.Duration(i),
|
||||
Description: strconv.Itoa(i),
|
||||
})
|
||||
}
|
||||
m.AddDrop()
|
||||
var writer mockedWriter
|
||||
SetReportWriter(&writer)
|
||||
m.executor.Flush()
|
||||
assert.Equal(t, "bar", writer.report.Name)
|
||||
}
|
||||
}
|
||||
|
||||
type mockedWriter struct {
|
||||
report *StatReport
|
||||
}
|
||||
|
||||
func (m *mockedWriter) Write(report *StatReport) error {
|
||||
m.report = report
|
||||
return nil
|
||||
}
|
||||
30
core/stat/remotewriter_test.go
Normal file
30
core/stat/remotewriter_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package stat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/h2non/gock.v1"
|
||||
)
|
||||
|
||||
func TestRemoteWriter(t *testing.T) {
|
||||
defer gock.Off()
|
||||
|
||||
gock.New("http://foo.com").Reply(200).BodyString("foo")
|
||||
writer := NewRemoteWriter("http://foo.com")
|
||||
err := writer.Write(&StatReport{
|
||||
Name: "bar",
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestRemoteWriterFail(t *testing.T) {
|
||||
defer gock.Off()
|
||||
|
||||
gock.New("http://foo.com").Reply(503).BodyString("foo")
|
||||
writer := NewRemoteWriter("http://foo.com")
|
||||
err := writer.Write(&StatReport{
|
||||
Name: "bar",
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
58
core/stores/cache/cache_test.go
vendored
58
core/stores/cache/cache_test.go
vendored
@@ -111,6 +111,45 @@ func TestCache_SetDel(t *testing.T) {
|
||||
assert.Nil(t, c.GetCache(fmt.Sprintf("key/%d", i), &v))
|
||||
assert.Equal(t, i, v)
|
||||
}
|
||||
assert.Nil(t, c.DelCache())
|
||||
for i := 0; i < total; i++ {
|
||||
assert.Nil(t, c.DelCache(fmt.Sprintf("key/%d", i)))
|
||||
}
|
||||
for i := 0; i < total; i++ {
|
||||
var v int
|
||||
assert.Equal(t, errPlaceholder, c.GetCache(fmt.Sprintf("key/%d", i), &v))
|
||||
assert.Equal(t, 0, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache_OneNode(t *testing.T) {
|
||||
const total = 1000
|
||||
r := miniredis.NewMiniRedis()
|
||||
assert.Nil(t, r.Start())
|
||||
defer r.Close()
|
||||
conf := ClusterConf{
|
||||
{
|
||||
RedisConf: redis.RedisConf{
|
||||
Host: r.Addr(),
|
||||
Type: redis.NodeType,
|
||||
},
|
||||
Weight: 100,
|
||||
},
|
||||
}
|
||||
c := NewCache(conf, syncx.NewSharedCalls(), NewCacheStat("mock"), errPlaceholder)
|
||||
for i := 0; i < total; i++ {
|
||||
if i%2 == 0 {
|
||||
assert.Nil(t, c.SetCache(fmt.Sprintf("key/%d", i), i))
|
||||
} else {
|
||||
assert.Nil(t, c.SetCacheWithExpire(fmt.Sprintf("key/%d", i), i, 0))
|
||||
}
|
||||
}
|
||||
for i := 0; i < total; i++ {
|
||||
var v int
|
||||
assert.Nil(t, c.GetCache(fmt.Sprintf("key/%d", i), &v))
|
||||
assert.Equal(t, i, v)
|
||||
}
|
||||
assert.Nil(t, c.DelCache())
|
||||
for i := 0; i < total; i++ {
|
||||
assert.Nil(t, c.DelCache(fmt.Sprintf("key/%d", i)))
|
||||
}
|
||||
@@ -188,6 +227,25 @@ func TestCache_Balance(t *testing.T) {
|
||||
assert.Equal(t, total/10, count)
|
||||
}
|
||||
|
||||
func TestCacheNoNode(t *testing.T) {
|
||||
dispatcher := hash.NewConsistentHash()
|
||||
c := cacheCluster{
|
||||
dispatcher: dispatcher,
|
||||
errNotFound: errPlaceholder,
|
||||
}
|
||||
assert.NotNil(t, c.DelCache("foo"))
|
||||
assert.NotNil(t, c.DelCache("foo", "bar", "any"))
|
||||
assert.NotNil(t, c.GetCache("foo", nil))
|
||||
assert.NotNil(t, c.SetCache("foo", nil))
|
||||
assert.NotNil(t, c.SetCacheWithExpire("foo", nil, time.Second))
|
||||
assert.NotNil(t, c.Take(nil, "foo", func(v interface{}) error {
|
||||
return nil
|
||||
}))
|
||||
assert.NotNil(t, c.TakeWithExpire(nil, "foo", func(v interface{}, duration time.Duration) error {
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
func calcEntropy(m map[int]int, total int) float64 {
|
||||
var entropy float64
|
||||
|
||||
|
||||
10
core/stores/cache/cachenode.go
vendored
10
core/stores/cache/cachenode.go
vendored
@@ -1,13 +1,13 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tal-tech/go-zero/core/jsonx"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/core/mathx"
|
||||
"github.com/tal-tech/go-zero/core/stat"
|
||||
@@ -79,7 +79,7 @@ func (c cacheNode) SetCache(key string, v interface{}) error {
|
||||
}
|
||||
|
||||
func (c cacheNode) SetCacheWithExpire(key string, v interface{}, expire time.Duration) error {
|
||||
data, err := json.Marshal(v)
|
||||
data, err := jsonx.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -168,7 +168,7 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(v)
|
||||
return jsonx.Marshal(v)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -181,11 +181,11 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
|
||||
c.stat.IncrementHit()
|
||||
}
|
||||
|
||||
return json.Unmarshal(val.([]byte), v)
|
||||
return jsonx.Unmarshal(val.([]byte), v)
|
||||
}
|
||||
|
||||
func (c cacheNode) processCache(key string, data string, v interface{}) error {
|
||||
err := json.Unmarshal([]byte(data), v)
|
||||
err := jsonx.Unmarshal([]byte(data), v)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
147
core/stores/cache/cachenode_test.go
vendored
147
core/stores/cache/cachenode_test.go
vendored
@@ -2,7 +2,9 @@ package cache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -13,8 +15,11 @@ import (
|
||||
"github.com/tal-tech/go-zero/core/mathx"
|
||||
"github.com/tal-tech/go-zero/core/stat"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis"
|
||||
"github.com/tal-tech/go-zero/core/syncx"
|
||||
)
|
||||
|
||||
var errTestNotFound = errors.New("not found")
|
||||
|
||||
func init() {
|
||||
logx.Disable()
|
||||
stat.SetReporter(nil)
|
||||
@@ -31,7 +36,7 @@ func TestCacheNode_DelCache(t *testing.T) {
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errors.New("any"),
|
||||
errNotFound: errTestNotFound,
|
||||
}
|
||||
assert.Nil(t, cn.DelCache())
|
||||
assert.Nil(t, cn.DelCache([]string{}...))
|
||||
@@ -54,7 +59,7 @@ func TestCacheNode_InvalidCache(t *testing.T) {
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errors.New("any"),
|
||||
errNotFound: errTestNotFound,
|
||||
}
|
||||
s.Set("any", "value")
|
||||
var str string
|
||||
@@ -63,3 +68,141 @@ func TestCacheNode_InvalidCache(t *testing.T) {
|
||||
_, err = s.Get("any")
|
||||
assert.Equal(t, miniredis.ErrKeyNotFound, err)
|
||||
}
|
||||
|
||||
func TestCacheNode_Take(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
defer s.Close()
|
||||
|
||||
cn := cacheNode{
|
||||
rds: redis.NewRedis(s.Addr(), redis.NodeType),
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
barrier: syncx.NewSharedCalls(),
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errTestNotFound,
|
||||
}
|
||||
var str string
|
||||
err = cn.Take(&str, "any", func(v interface{}) error {
|
||||
*v.(*string) = "value"
|
||||
return nil
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "value", str)
|
||||
assert.Nil(t, cn.GetCache("any", &str))
|
||||
val, err := s.Get("any")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `"value"`, val)
|
||||
}
|
||||
|
||||
func TestCacheNode_TakeNotFound(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
defer s.Close()
|
||||
|
||||
cn := cacheNode{
|
||||
rds: redis.NewRedis(s.Addr(), redis.NodeType),
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
barrier: syncx.NewSharedCalls(),
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errTestNotFound,
|
||||
}
|
||||
var str string
|
||||
err = cn.Take(&str, "any", func(v interface{}) error {
|
||||
return errTestNotFound
|
||||
})
|
||||
assert.Equal(t, errTestNotFound, err)
|
||||
assert.Equal(t, errTestNotFound, cn.GetCache("any", &str))
|
||||
val, err := s.Get("any")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `*`, val)
|
||||
|
||||
s.Set("any", "*")
|
||||
err = cn.Take(&str, "any", func(v interface{}) error {
|
||||
return nil
|
||||
})
|
||||
assert.Equal(t, errTestNotFound, err)
|
||||
assert.Equal(t, errTestNotFound, cn.GetCache("any", &str))
|
||||
|
||||
s.Del("any")
|
||||
var errDummy = errors.New("dummy")
|
||||
err = cn.Take(&str, "any", func(v interface{}) error {
|
||||
return errDummy
|
||||
})
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCacheNode_TakeWithExpire(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
defer s.Close()
|
||||
|
||||
cn := cacheNode{
|
||||
rds: redis.NewRedis(s.Addr(), redis.NodeType),
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
barrier: syncx.NewSharedCalls(),
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errors.New("any"),
|
||||
}
|
||||
var str string
|
||||
err = cn.TakeWithExpire(&str, "any", func(v interface{}, expire time.Duration) error {
|
||||
*v.(*string) = "value"
|
||||
return nil
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "value", str)
|
||||
assert.Nil(t, cn.GetCache("any", &str))
|
||||
val, err := s.Get("any")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, `"value"`, val)
|
||||
}
|
||||
|
||||
func TestCacheNode_String(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
defer s.Close()
|
||||
|
||||
cn := cacheNode{
|
||||
rds: redis.NewRedis(s.Addr(), redis.NodeType),
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
barrier: syncx.NewSharedCalls(),
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errors.New("any"),
|
||||
}
|
||||
assert.Equal(t, s.Addr(), cn.String())
|
||||
}
|
||||
|
||||
func TestCacheValueWithBigInt(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
cn := cacheNode{
|
||||
rds: redis.NewRedis(s.Addr(), redis.NodeType),
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
barrier: syncx.NewSharedCalls(),
|
||||
lock: new(sync.Mutex),
|
||||
unstableExpiry: mathx.NewUnstable(expiryDeviation),
|
||||
stat: NewCacheStat("any"),
|
||||
errNotFound: errors.New("any"),
|
||||
}
|
||||
|
||||
const (
|
||||
key = "key"
|
||||
value int64 = 323427211229009810
|
||||
)
|
||||
|
||||
assert.Nil(t, cn.SetCache(key, value))
|
||||
var val interface{}
|
||||
assert.Nil(t, cn.GetCache(key, &val))
|
||||
assert.Equal(t, strconv.FormatInt(value, 10), fmt.Sprintf("%v", val))
|
||||
}
|
||||
|
||||
56
core/stores/cache/cleaner_test.go
vendored
Normal file
56
core/stores/cache/cleaner_test.go
vendored
Normal 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
26
core/stores/cache/util_test.go
vendored
Normal 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)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/hash"
|
||||
"github.com/tal-tech/go-zero/core/stores/cache"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis"
|
||||
"github.com/tal-tech/go-zero/core/stringx"
|
||||
@@ -15,6 +16,10 @@ var s1, _ = miniredis.Run()
|
||||
var s2, _ = miniredis.Run()
|
||||
|
||||
func TestRedis_Exists(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Exists("foo")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
ok, err := client.Exists("a")
|
||||
assert.Nil(t, err)
|
||||
@@ -27,6 +32,10 @@ func TestRedis_Exists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Eval(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Eval(`redis.call("EXISTS", KEYS[1])`, "key1")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
_, err := client.Eval(`redis.call("EXISTS", KEYS[1])`, "notexist")
|
||||
assert.Equal(t, redis.Nil, err)
|
||||
@@ -41,6 +50,12 @@ func TestRedis_Eval(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Hgetall(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
err := store.Hset("a", "aa", "aaa")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Hgetall("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
@@ -54,6 +69,10 @@ func TestRedis_Hgetall(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Hvals(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Hvals("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
@@ -64,6 +83,10 @@ func TestRedis_Hvals(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Hsetnx(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Hsetnx("a", "dd", "ddd")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
@@ -80,6 +103,12 @@ func TestRedis_Hsetnx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_HdelHlen(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Hdel("a", "aa")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Hlen("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
@@ -96,6 +125,10 @@ func TestRedis_HdelHlen(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_HIncrBy(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Hincrby("key", "field", 3)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
val, err := client.Hincrby("key", "field", 2)
|
||||
assert.Nil(t, err)
|
||||
@@ -107,6 +140,10 @@ func TestRedis_HIncrBy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Hkeys(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Hkeys("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
@@ -117,6 +154,10 @@ func TestRedis_Hkeys(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Hmget(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Hmget("a", "aa", "bb")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
@@ -130,6 +171,12 @@ func TestRedis_Hmget(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Hmset(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
err := store.Hmset("a", map[string]string{
|
||||
"aa": "aaa",
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
assert.Nil(t, client.Hmset("a", map[string]string{
|
||||
"aa": "aaa",
|
||||
@@ -142,6 +189,10 @@ func TestRedis_Hmset(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Incr(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Incr("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
val, err := client.Incr("a")
|
||||
assert.Nil(t, err)
|
||||
@@ -153,6 +204,10 @@ func TestRedis_Incr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_IncrBy(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Incrby("a", 2)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
val, err := client.Incrby("a", 2)
|
||||
assert.Nil(t, err)
|
||||
@@ -164,6 +219,20 @@ func TestRedis_IncrBy(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_List(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Lpush("key", "value1", "value2")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Rpush("key", "value3", "value4")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Llen("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Lrange("key", 0, 10)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Lpop("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Lrem("key", 0, "val")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
val, err := client.Lpush("key", "value1", "value2")
|
||||
assert.Nil(t, err)
|
||||
@@ -202,6 +271,14 @@ func TestRedis_List(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Persist(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Persist("key")
|
||||
assert.NotNil(t, err)
|
||||
err = store.Expire("key", 5)
|
||||
assert.NotNil(t, err)
|
||||
err = store.Expireat("key", time.Now().Unix()+5)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
ok, err := client.Persist("key")
|
||||
assert.Nil(t, err)
|
||||
@@ -225,8 +302,16 @@ func TestRedis_Persist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Sscan(t *testing.T) {
|
||||
key := "list"
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Sadd(key, nil)
|
||||
assert.NotNil(t, err)
|
||||
_, _, err = store.Sscan(key, 0, "", 100)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Del(key)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
key := "list"
|
||||
var list []string
|
||||
for i := 0; i < 1550; i++ {
|
||||
list = append(list, stringx.Randn(i))
|
||||
@@ -254,6 +339,20 @@ func TestRedis_Sscan(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_Set(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Scard("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Sismember("key", 2)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Srem("key", 3, 4)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Smembers("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Srandmember("key", 1)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Spop("key")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
num, err := client.Sadd("key", 1, 2, 3, 4)
|
||||
assert.Nil(t, err)
|
||||
@@ -290,6 +389,14 @@ func TestRedis_Set(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_SetGetDel(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
err := store.Set("hello", "world")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Get("hello")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Del("hello")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
err := client.Set("hello", "world")
|
||||
assert.Nil(t, err)
|
||||
@@ -303,6 +410,16 @@ func TestRedis_SetGetDel(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_SetExNx(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
err := store.Setex("hello", "world", 5)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Setnx("newhello", "newworld")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Ttl("hello")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.SetnxEx("newhello", "newworld", 5)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
err := client.Setex("hello", "world", 5)
|
||||
assert.Nil(t, err)
|
||||
@@ -337,6 +454,16 @@ func TestRedis_SetExNx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_SetGetDelHashField(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
err := store.Hset("key", "field", "value")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Hget("key", "field")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Hexists("key", "field")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Hdel("key", "field")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
err := client.Hset("key", "field", "value")
|
||||
assert.Nil(t, err)
|
||||
@@ -356,6 +483,48 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRedis_SortedSet(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Zadd("key", 1, "value1")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zscore("key", "value1")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zcount("key", 6, 7)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zincrby("key", 3, "value1")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zrank("key", "value2")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zrem("key", "value2", "value3")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zremrangebyscore("key", 6, 7)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zremrangebyrank("key", 1, 2)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zcard("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zrange("key", 0, -1)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zrevrange("key", 0, -1)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.ZrangeWithScores("key", 0, -1)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.ZrangebyscoreWithScores("key", 5, 8)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.ZrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.ZrevrangebyscoreWithScores("key", 5, 8)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.ZrevrangebyscoreWithScoresAndLimit("key", 5, 8, 1, 1)
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Zadds("key", redis.Pair{
|
||||
Key: "value2",
|
||||
Score: 6,
|
||||
}, redis.Pair{
|
||||
Key: "value3",
|
||||
Score: 7,
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
ok, err := client.Zadd("key", 1, "value1")
|
||||
assert.Nil(t, err)
|
||||
@@ -471,6 +640,30 @@ func TestRedis_SortedSet(t *testing.T) {
|
||||
Score: 5,
|
||||
},
|
||||
}, pairs)
|
||||
val, err = client.Zadds("key", redis.Pair{
|
||||
Key: "value2",
|
||||
Score: 6,
|
||||
}, redis.Pair{
|
||||
Key: "value3",
|
||||
Score: 7,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(2), val)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedis_HyperLogLog(t *testing.T) {
|
||||
store := clusterStore{dispatcher: hash.NewConsistentHash()}
|
||||
_, err := store.Pfadd("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = store.Pfcount("key")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(cluster Store) {
|
||||
_, err := cluster.Pfadd("key")
|
||||
assert.NotNil(t, err)
|
||||
_, err = cluster.Pfcount("key")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/globalsign/mgo"
|
||||
"github.com/tal-tech/go-zero/core/breaker"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/core/stores/mongo/internal"
|
||||
"github.com/tal-tech/go-zero/core/timex"
|
||||
)
|
||||
|
||||
@@ -29,8 +30,9 @@ type (
|
||||
}
|
||||
|
||||
decoratedCollection struct {
|
||||
*mgo.Collection
|
||||
brk breaker.Breaker
|
||||
name string
|
||||
collection internal.MgoCollection
|
||||
brk breaker.Breaker
|
||||
}
|
||||
|
||||
keepablePromise struct {
|
||||
@@ -41,7 +43,8 @@ type (
|
||||
|
||||
func newCollection(collection *mgo.Collection) Collection {
|
||||
return &decoratedCollection{
|
||||
Collection: collection,
|
||||
name: collection.FullName,
|
||||
collection: collection,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
}
|
||||
@@ -54,7 +57,7 @@ func (c *decoratedCollection) Find(query interface{}) Query {
|
||||
|
||||
startTime := timex.Now()
|
||||
return promisedQuery{
|
||||
Query: c.Collection.Find(query),
|
||||
Query: c.collection.Find(query),
|
||||
promise: keepablePromise{
|
||||
promise: promise,
|
||||
log: func(err error) {
|
||||
@@ -73,7 +76,7 @@ func (c *decoratedCollection) FindId(id interface{}) Query {
|
||||
|
||||
startTime := timex.Now()
|
||||
return promisedQuery{
|
||||
Query: c.Collection.FindId(id),
|
||||
Query: c.collection.FindId(id),
|
||||
promise: keepablePromise{
|
||||
promise: promise,
|
||||
log: func(err error) {
|
||||
@@ -92,7 +95,7 @@ func (c *decoratedCollection) Insert(docs ...interface{}) (err error) {
|
||||
c.logDuration("insert", duration, err, docs...)
|
||||
}()
|
||||
|
||||
return c.Collection.Insert(docs...)
|
||||
return c.collection.Insert(docs...)
|
||||
}, acceptable)
|
||||
}
|
||||
|
||||
@@ -104,7 +107,7 @@ func (c *decoratedCollection) Pipe(pipeline interface{}) Pipe {
|
||||
|
||||
startTime := timex.Now()
|
||||
return promisedPipe{
|
||||
Pipe: c.Collection.Pipe(pipeline),
|
||||
Pipe: c.collection.Pipe(pipeline),
|
||||
promise: keepablePromise{
|
||||
promise: promise,
|
||||
log: func(err error) {
|
||||
@@ -123,7 +126,7 @@ func (c *decoratedCollection) Remove(selector interface{}) (err error) {
|
||||
c.logDuration("remove", duration, err, selector)
|
||||
}()
|
||||
|
||||
return c.Collection.Remove(selector)
|
||||
return c.collection.Remove(selector)
|
||||
}, acceptable)
|
||||
}
|
||||
|
||||
@@ -135,7 +138,7 @@ func (c *decoratedCollection) RemoveAll(selector interface{}) (info *mgo.ChangeI
|
||||
c.logDuration("removeAll", duration, err, selector)
|
||||
}()
|
||||
|
||||
info, err = c.Collection.RemoveAll(selector)
|
||||
info, err = c.collection.RemoveAll(selector)
|
||||
return err
|
||||
}, acceptable)
|
||||
|
||||
@@ -150,7 +153,7 @@ func (c *decoratedCollection) RemoveId(id interface{}) (err error) {
|
||||
c.logDuration("removeId", duration, err, id)
|
||||
}()
|
||||
|
||||
return c.Collection.RemoveId(id)
|
||||
return c.collection.RemoveId(id)
|
||||
}, acceptable)
|
||||
}
|
||||
|
||||
@@ -162,7 +165,7 @@ func (c *decoratedCollection) Update(selector, update interface{}) (err error) {
|
||||
c.logDuration("update", duration, err, selector, update)
|
||||
}()
|
||||
|
||||
return c.Collection.Update(selector, update)
|
||||
return c.collection.Update(selector, update)
|
||||
}, acceptable)
|
||||
}
|
||||
|
||||
@@ -174,7 +177,7 @@ func (c *decoratedCollection) UpdateId(id, update interface{}) (err error) {
|
||||
c.logDuration("updateId", duration, err, id, update)
|
||||
}()
|
||||
|
||||
return c.Collection.UpdateId(id, update)
|
||||
return c.collection.UpdateId(id, update)
|
||||
}, acceptable)
|
||||
}
|
||||
|
||||
@@ -186,7 +189,7 @@ func (c *decoratedCollection) Upsert(selector, update interface{}) (info *mgo.Ch
|
||||
c.logDuration("upsert", duration, err, selector, update)
|
||||
}()
|
||||
|
||||
info, err = c.Collection.Upsert(selector, update)
|
||||
info, err = c.collection.Upsert(selector, update)
|
||||
return err
|
||||
}, acceptable)
|
||||
|
||||
@@ -200,17 +203,17 @@ func (c *decoratedCollection) logDuration(method string, duration time.Duration,
|
||||
} else if err != nil {
|
||||
if duration > slowThreshold {
|
||||
logx.WithDuration(duration).Slowf("[MONGO] mongo(%s) - slowcall - %s - fail(%s) - %s",
|
||||
c.FullName, method, err.Error(), string(content))
|
||||
c.name, method, err.Error(), string(content))
|
||||
} else {
|
||||
logx.WithDuration(duration).Infof("mongo(%s) - %s - fail(%s) - %s",
|
||||
c.FullName, method, err.Error(), string(content))
|
||||
c.name, method, err.Error(), string(content))
|
||||
}
|
||||
} else {
|
||||
if duration > slowThreshold {
|
||||
logx.WithDuration(duration).Slowf("[MONGO] mongo(%s) - slowcall - %s - ok - %s",
|
||||
c.FullName, method, string(content))
|
||||
c.name, method, string(content))
|
||||
} else {
|
||||
logx.WithDuration(duration).Infof("mongo(%s) - %s - ok - %s", c.FullName, method, string(content))
|
||||
logx.WithDuration(duration).Infof("mongo(%s) - %s - ok - %s", c.name, method, string(content))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,20 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/globalsign/mgo"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/breaker"
|
||||
"github.com/tal-tech/go-zero/core/logx"
|
||||
"github.com/tal-tech/go-zero/core/stores/mongo/internal"
|
||||
"github.com/tal-tech/go-zero/core/stringx"
|
||||
)
|
||||
|
||||
var errDummy = errors.New("dummy")
|
||||
|
||||
func init() {
|
||||
logx.Disable()
|
||||
}
|
||||
|
||||
func TestKeepPromise_accept(t *testing.T) {
|
||||
p := new(mockPromise)
|
||||
kp := keepablePromise{
|
||||
@@ -56,6 +66,206 @@ func TestKeepPromise_keep(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewCollection(t *testing.T) {
|
||||
col := newCollection(&mgo.Collection{
|
||||
Database: nil,
|
||||
Name: "foo",
|
||||
FullName: "bar",
|
||||
})
|
||||
assert.Equal(t, "bar", col.(*decoratedCollection).name)
|
||||
}
|
||||
|
||||
func TestCollectionFind(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
var query mgo.Query
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().Find(gomock.Any()).Return(&query)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
actual := c.Find(nil)
|
||||
switch v := actual.(type) {
|
||||
case promisedQuery:
|
||||
assert.Equal(t, &query, v.Query)
|
||||
assert.Equal(t, errDummy, v.promise.keep(errDummy))
|
||||
default:
|
||||
t.Fail()
|
||||
}
|
||||
c.brk = new(dropBreaker)
|
||||
actual = c.Find(nil)
|
||||
assert.Equal(t, rejectedQuery{}, actual)
|
||||
}
|
||||
|
||||
func TestCollectionFindId(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
var query mgo.Query
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().FindId(gomock.Any()).Return(&query)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
actual := c.FindId(nil)
|
||||
switch v := actual.(type) {
|
||||
case promisedQuery:
|
||||
assert.Equal(t, &query, v.Query)
|
||||
assert.Equal(t, errDummy, v.promise.keep(errDummy))
|
||||
default:
|
||||
t.Fail()
|
||||
}
|
||||
c.brk = new(dropBreaker)
|
||||
actual = c.FindId(nil)
|
||||
assert.Equal(t, rejectedQuery{}, actual)
|
||||
}
|
||||
|
||||
func TestCollectionInsert(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().Insert(nil, nil).Return(errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
err := c.Insert(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
err = c.Insert(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCollectionPipe(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
var pipe mgo.Pipe
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().Pipe(gomock.Any()).Return(&pipe)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
actual := c.Pipe(nil)
|
||||
switch v := actual.(type) {
|
||||
case promisedPipe:
|
||||
assert.Equal(t, &pipe, v.Pipe)
|
||||
assert.Equal(t, errDummy, v.promise.keep(errDummy))
|
||||
default:
|
||||
t.Fail()
|
||||
}
|
||||
c.brk = new(dropBreaker)
|
||||
actual = c.Pipe(nil)
|
||||
assert.Equal(t, rejectedPipe{}, actual)
|
||||
}
|
||||
|
||||
func TestCollectionRemove(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().Remove(gomock.Any()).Return(errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
err := c.Remove(nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
err = c.Remove(nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCollectionRemoveAll(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().RemoveAll(gomock.Any()).Return(nil, errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
_, err := c.RemoveAll(nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
_, err = c.RemoveAll(nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCollectionRemoveId(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().RemoveId(gomock.Any()).Return(errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
err := c.RemoveId(nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
err = c.RemoveId(nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCollectionUpdate(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().Update(gomock.Any(), gomock.Any()).Return(errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
err := c.Update(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
err = c.Update(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCollectionUpdateId(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().UpdateId(gomock.Any(), gomock.Any()).Return(errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
err := c.UpdateId(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
err = c.UpdateId(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
func TestCollectionUpsert(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
col := internal.NewMockMgoCollection(ctrl)
|
||||
col.EXPECT().Upsert(gomock.Any(), gomock.Any()).Return(nil, errDummy)
|
||||
c := decoratedCollection{
|
||||
collection: col,
|
||||
brk: breaker.NewBreaker(),
|
||||
}
|
||||
_, err := c.Upsert(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
c.brk = new(dropBreaker)
|
||||
_, err = c.Upsert(nil, nil)
|
||||
assert.Equal(t, errDummy, err)
|
||||
}
|
||||
|
||||
type mockPromise struct {
|
||||
accepted bool
|
||||
reason string
|
||||
@@ -68,3 +278,31 @@ func (p *mockPromise) Accept() {
|
||||
func (p *mockPromise) Reject(reason string) {
|
||||
p.reason = reason
|
||||
}
|
||||
|
||||
type dropBreaker struct {
|
||||
}
|
||||
|
||||
func (d *dropBreaker) Name() string {
|
||||
return "dummy"
|
||||
}
|
||||
|
||||
func (d *dropBreaker) Allow() (breaker.Promise, error) {
|
||||
return nil, errDummy
|
||||
}
|
||||
|
||||
func (d *dropBreaker) Do(req func() error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dropBreaker) DoWithAcceptable(req func() error, acceptable breaker.Acceptable) error {
|
||||
return errDummy
|
||||
}
|
||||
|
||||
func (d *dropBreaker) DoWithFallback(req func() error, fallback func(err error) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *dropBreaker) DoWithFallbackAcceptable(req func() error, fallback func(err error) error,
|
||||
acceptable breaker.Acceptable) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
17
core/stores/mongo/internal/collection.go
Normal file
17
core/stores/mongo/internal/collection.go
Normal file
@@ -0,0 +1,17 @@
|
||||
//go:generate mockgen -package internal -destination collection_mock.go -source collection.go
|
||||
package internal
|
||||
|
||||
import "github.com/globalsign/mgo"
|
||||
|
||||
type MgoCollection interface {
|
||||
Find(query interface{}) *mgo.Query
|
||||
FindId(id interface{}) *mgo.Query
|
||||
Insert(docs ...interface{}) error
|
||||
Pipe(pipeline interface{}) *mgo.Pipe
|
||||
Remove(selector interface{}) error
|
||||
RemoveAll(selector interface{}) (*mgo.ChangeInfo, error)
|
||||
RemoveId(id interface{}) error
|
||||
Update(selector, update interface{}) error
|
||||
UpdateId(id, update interface{}) error
|
||||
Upsert(selector, update interface{}) (*mgo.ChangeInfo, error)
|
||||
}
|
||||
180
core/stores/mongo/internal/collection_mock.go
Normal file
180
core/stores/mongo/internal/collection_mock.go
Normal file
@@ -0,0 +1,180 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: collection.go
|
||||
|
||||
// Package internal is a generated GoMock package.
|
||||
package internal
|
||||
|
||||
import (
|
||||
mgo "github.com/globalsign/mgo"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockMgoCollection is a mock of MgoCollection interface
|
||||
type MockMgoCollection struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockMgoCollectionMockRecorder
|
||||
}
|
||||
|
||||
// MockMgoCollectionMockRecorder is the mock recorder for MockMgoCollection
|
||||
type MockMgoCollectionMockRecorder struct {
|
||||
mock *MockMgoCollection
|
||||
}
|
||||
|
||||
// NewMockMgoCollection creates a new mock instance
|
||||
func NewMockMgoCollection(ctrl *gomock.Controller) *MockMgoCollection {
|
||||
mock := &MockMgoCollection{ctrl: ctrl}
|
||||
mock.recorder = &MockMgoCollectionMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use
|
||||
func (m *MockMgoCollection) EXPECT() *MockMgoCollectionMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Find mocks base method
|
||||
func (m *MockMgoCollection) Find(query interface{}) *mgo.Query {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Find", query)
|
||||
ret0, _ := ret[0].(*mgo.Query)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Find indicates an expected call of Find
|
||||
func (mr *MockMgoCollectionMockRecorder) Find(query interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Find", reflect.TypeOf((*MockMgoCollection)(nil).Find), query)
|
||||
}
|
||||
|
||||
// FindId mocks base method
|
||||
func (m *MockMgoCollection) FindId(id interface{}) *mgo.Query {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "FindId", id)
|
||||
ret0, _ := ret[0].(*mgo.Query)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// FindId indicates an expected call of FindId
|
||||
func (mr *MockMgoCollectionMockRecorder) FindId(id interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindId", reflect.TypeOf((*MockMgoCollection)(nil).FindId), id)
|
||||
}
|
||||
|
||||
// Insert mocks base method
|
||||
func (m *MockMgoCollection) Insert(docs ...interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
varargs := []interface{}{}
|
||||
for _, a := range docs {
|
||||
varargs = append(varargs, a)
|
||||
}
|
||||
ret := m.ctrl.Call(m, "Insert", varargs...)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Insert indicates an expected call of Insert
|
||||
func (mr *MockMgoCollectionMockRecorder) Insert(docs ...interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockMgoCollection)(nil).Insert), docs...)
|
||||
}
|
||||
|
||||
// Pipe mocks base method
|
||||
func (m *MockMgoCollection) Pipe(pipeline interface{}) *mgo.Pipe {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Pipe", pipeline)
|
||||
ret0, _ := ret[0].(*mgo.Pipe)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Pipe indicates an expected call of Pipe
|
||||
func (mr *MockMgoCollectionMockRecorder) Pipe(pipeline interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pipe", reflect.TypeOf((*MockMgoCollection)(nil).Pipe), pipeline)
|
||||
}
|
||||
|
||||
// Remove mocks base method
|
||||
func (m *MockMgoCollection) Remove(selector interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Remove", selector)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Remove indicates an expected call of Remove
|
||||
func (mr *MockMgoCollectionMockRecorder) Remove(selector interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockMgoCollection)(nil).Remove), selector)
|
||||
}
|
||||
|
||||
// RemoveAll mocks base method
|
||||
func (m *MockMgoCollection) RemoveAll(selector interface{}) (*mgo.ChangeInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveAll", selector)
|
||||
ret0, _ := ret[0].(*mgo.ChangeInfo)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// RemoveAll indicates an expected call of RemoveAll
|
||||
func (mr *MockMgoCollectionMockRecorder) RemoveAll(selector interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveAll", reflect.TypeOf((*MockMgoCollection)(nil).RemoveAll), selector)
|
||||
}
|
||||
|
||||
// RemoveId mocks base method
|
||||
func (m *MockMgoCollection) RemoveId(id interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveId", id)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveId indicates an expected call of RemoveId
|
||||
func (mr *MockMgoCollectionMockRecorder) RemoveId(id interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveId", reflect.TypeOf((*MockMgoCollection)(nil).RemoveId), id)
|
||||
}
|
||||
|
||||
// Update mocks base method
|
||||
func (m *MockMgoCollection) Update(selector, update interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Update", selector, update)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Update indicates an expected call of Update
|
||||
func (mr *MockMgoCollectionMockRecorder) Update(selector, update interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockMgoCollection)(nil).Update), selector, update)
|
||||
}
|
||||
|
||||
// UpdateId mocks base method
|
||||
func (m *MockMgoCollection) UpdateId(id, update interface{}) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateId", id, update)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateId indicates an expected call of UpdateId
|
||||
func (mr *MockMgoCollectionMockRecorder) UpdateId(id, update interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateId", reflect.TypeOf((*MockMgoCollection)(nil).UpdateId), id, update)
|
||||
}
|
||||
|
||||
// Upsert mocks base method
|
||||
func (m *MockMgoCollection) Upsert(selector, update interface{}) (*mgo.ChangeInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Upsert", selector, update)
|
||||
ret0, _ := ret[0].(*mgo.ChangeInfo)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Upsert indicates an expected call of Upsert
|
||||
func (mr *MockMgoCollectionMockRecorder) Upsert(selector, update interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upsert", reflect.TypeOf((*MockMgoCollection)(nil).Upsert), selector, update)
|
||||
}
|
||||
110
core/stores/redis/conf_test.go
Normal file
110
core/stores/redis/conf_test.go
Normal 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())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ func (cc CachedConn) QueryRowIndex(v interface{}, key string, keyer func(primary
|
||||
indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error {
|
||||
var primaryKey interface{}
|
||||
var found bool
|
||||
|
||||
if err := cc.cache.TakeWithExpire(&primaryKey, key, func(val interface{}, expire time.Duration) (err error) {
|
||||
primaryKey, err = indexQuery(cc.db, v)
|
||||
if err != nil {
|
||||
|
||||
@@ -79,9 +79,29 @@ func TestCachedConn_QueryRowIndex_NoCache(t *testing.T) {
|
||||
}
|
||||
|
||||
r := redis.NewRedis(s.Addr(), redis.NodeType)
|
||||
c := NewNodeConn(dummySqlConn{}, r, cache.WithExpiry(time.Second*10))
|
||||
c := NewConn(dummySqlConn{}, cache.CacheConf{
|
||||
{
|
||||
RedisConf: redis.RedisConf{
|
||||
Host: s.Addr(),
|
||||
Type: redis.NodeType,
|
||||
},
|
||||
Weight: 100,
|
||||
},
|
||||
}, cache.WithExpiry(time.Second*10))
|
||||
|
||||
var str string
|
||||
err = c.QueryRowIndex(&str, "index", func(s interface{}) string {
|
||||
return fmt.Sprintf("%s/1234", s)
|
||||
}, func(conn sqlx.SqlConn, v interface{}) (interface{}, error) {
|
||||
*v.(*string) = "zero"
|
||||
return "primary", errors.New("foo")
|
||||
}, func(conn sqlx.SqlConn, v, pri interface{}) error {
|
||||
assert.Equal(t, "primary", pri)
|
||||
*v.(*string) = "xin"
|
||||
return nil
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
err = c.QueryRowIndex(&str, "index", func(s interface{}) string {
|
||||
return fmt.Sprintf("%s/1234", s)
|
||||
}, func(conn sqlx.SqlConn, v interface{}) (interface{}, error) {
|
||||
@@ -135,6 +155,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 +265,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),
|
||||
@@ -401,6 +520,10 @@ func TestCachedConnExecDropCache(t *testing.T) {
|
||||
assert.True(t, conn.execValue)
|
||||
_, err = s.Get(key)
|
||||
assert.Exactly(t, miniredis.ErrKeyNotFound, err)
|
||||
_, err = c.Exec(func(conn sqlx.SqlConn) (result sql.Result, e error) {
|
||||
return nil, errors.New("foo")
|
||||
}, key)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestCachedConnExecDropCacheFailed(t *testing.T) {
|
||||
@@ -446,6 +569,31 @@ 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 resetStats() {
|
||||
atomic.StoreUint64(&stats.Total, 0)
|
||||
atomic.StoreUint64(&stats.Hit, 0)
|
||||
@@ -454,6 +602,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 +614,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
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package sqlx
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
@@ -11,14 +12,15 @@ import (
|
||||
)
|
||||
|
||||
type mockedConn struct {
|
||||
query string
|
||||
args []interface{}
|
||||
query string
|
||||
args []interface{}
|
||||
execErr error
|
||||
}
|
||||
|
||||
func (c *mockedConn) Exec(query string, args ...interface{}) (sql.Result, error) {
|
||||
c.query = query
|
||||
c.args = args
|
||||
return nil, nil
|
||||
return nil, c.execErr
|
||||
}
|
||||
|
||||
func (c *mockedConn) Prepare(query string) (StmtSession, error) {
|
||||
@@ -68,9 +70,12 @@ func TestBulkInserterSuffix(t *testing.T) {
|
||||
inserter, err := NewBulkInserter(&conn, `INSERT INTO classroom_dau(classroom, user, count) VALUES`+
|
||||
`(?, ?, ?) ON DUPLICATE KEY UPDATE is_overtime=VALUES(is_overtime)`)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, inserter.UpdateStmt(`INSERT INTO classroom_dau(classroom, user, count) VALUES`+
|
||||
`(?, ?, ?) ON DUPLICATE KEY UPDATE is_overtime=VALUES(is_overtime)`))
|
||||
for i := 0; i < 5; i++ {
|
||||
assert.Nil(t, inserter.Insert("class_"+strconv.Itoa(i), "user_"+strconv.Itoa(i), i))
|
||||
}
|
||||
inserter.SetResultHandler(func(result sql.Result, err error) {})
|
||||
inserter.Flush()
|
||||
assert.Equal(t, `INSERT INTO classroom_dau(classroom, user, count) VALUES `+
|
||||
`('class_0', 'user_0', 0), ('class_1', 'user_1', 1), ('class_2', 'user_2', 2), `+
|
||||
@@ -80,6 +85,33 @@ func TestBulkInserterSuffix(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBulkInserterBadStatement(t *testing.T) {
|
||||
runSqlTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||
var conn mockedConn
|
||||
_, err := NewBulkInserter(&conn, "foo")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestBulkInserter_Update(t *testing.T) {
|
||||
conn := mockedConn{
|
||||
execErr: errors.New("foo"),
|
||||
}
|
||||
_, err := NewBulkInserter(&conn, `INSERT INTO classroom_dau(classroom, user, count) VALUES()`)
|
||||
assert.NotNil(t, err)
|
||||
_, err = NewBulkInserter(&conn, `INSERT INTO classroom_dau(classroom, user, count) VALUES(?)`)
|
||||
assert.NotNil(t, err)
|
||||
inserter, err := NewBulkInserter(&conn, `INSERT INTO classroom_dau(classroom, user, count) VALUES(?, ?, ?)`)
|
||||
assert.Nil(t, err)
|
||||
inserter.inserter.Execute([]string{"bar"})
|
||||
inserter.SetResultHandler(func(result sql.Result, err error) {
|
||||
})
|
||||
inserter.UpdateOrDelete(func() {})
|
||||
inserter.inserter.Execute([]string(nil))
|
||||
assert.NotNil(t, inserter.UpdateStmt("foo"))
|
||||
assert.NotNil(t, inserter.Insert("foo", "bar"))
|
||||
}
|
||||
|
||||
func runSqlTest(t *testing.T, fn func(db *sql.DB, mock sqlmock.Sqlmock)) {
|
||||
logx.Disable()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package sqlx
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
@@ -22,6 +23,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 +241,22 @@ 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 TestUnmarshalRowsBool(t *testing.T) {
|
||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||
var expect = []bool{true, false}
|
||||
@@ -955,6 +984,62 @@ func TestCommonSqlConn_QueryRowOptional(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestUnmarshalRowError(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
colErr error
|
||||
scanErr error
|
||||
err error
|
||||
next int
|
||||
validate func(err error)
|
||||
}{
|
||||
{
|
||||
name: "with error",
|
||||
err: errors.New("foo"),
|
||||
validate: func(err error) {
|
||||
assert.NotNil(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "without next",
|
||||
validate: func(err error) {
|
||||
assert.Equal(t, ErrNotFound, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with error",
|
||||
scanErr: errors.New("foo"),
|
||||
next: 1,
|
||||
validate: func(err error) {
|
||||
assert.Equal(t, ErrNotFound, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
runOrmTest(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
|
||||
rs := sqlmock.NewRows([]string{"age"}).FromCSVString("5")
|
||||
mock.ExpectQuery("select (.+) from users where user=?").WithArgs(
|
||||
"anyone").WillReturnRows(rs)
|
||||
|
||||
var r struct {
|
||||
User string `db:"user"`
|
||||
Age int `db:"age"`
|
||||
}
|
||||
test.validate(query(db, func(rows *sql.Rows) error {
|
||||
scanner := mockedScanner{
|
||||
colErr: test.colErr,
|
||||
scanErr: test.scanErr,
|
||||
err: test.err,
|
||||
}
|
||||
return unmarshalRow(&r, &scanner, false)
|
||||
}, "select age from users where user=?", "anyone"))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runOrmTest(t *testing.T, fn func(db *sql.DB, mock sqlmock.Sqlmock)) {
|
||||
logx.Disable()
|
||||
|
||||
@@ -970,3 +1055,30 @@ func runOrmTest(t *testing.T, fn func(db *sql.DB, mock sqlmock.Sqlmock)) {
|
||||
t.Errorf("there were unfulfilled expectations: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
type mockedScanner struct {
|
||||
colErr error
|
||||
scanErr error
|
||||
err error
|
||||
next int
|
||||
}
|
||||
|
||||
func (m *mockedScanner) Columns() ([]string, error) {
|
||||
return nil, m.colErr
|
||||
}
|
||||
|
||||
func (m *mockedScanner) Err() error {
|
||||
return m.err
|
||||
}
|
||||
|
||||
func (m *mockedScanner) Next() bool {
|
||||
if m.next > 0 {
|
||||
m.next--
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *mockedScanner) Scan(v ...interface{}) error {
|
||||
return m.scanErr
|
||||
}
|
||||
|
||||
@@ -6,18 +6,22 @@ import (
|
||||
"github.com/tal-tech/go-zero/core/lang"
|
||||
)
|
||||
|
||||
var ErrReturn = errors.New("discarding limited token, resource pool is full, someone returned multiple times")
|
||||
// ErrLimitReturn indicates that the more than borrowed elements were returned.
|
||||
var ErrLimitReturn = errors.New("discarding limited token, resource pool is full, someone returned multiple times")
|
||||
|
||||
// Limit controls the concurrent requests.
|
||||
type Limit struct {
|
||||
pool chan lang.PlaceholderType
|
||||
}
|
||||
|
||||
// NewLimit creates a Limit that can borrow n elements from it concurrently.
|
||||
func NewLimit(n int) Limit {
|
||||
return Limit{
|
||||
pool: make(chan lang.PlaceholderType, n),
|
||||
}
|
||||
}
|
||||
|
||||
// Borrow borrows an element from Limit in blocking mode.
|
||||
func (l Limit) Borrow() {
|
||||
l.pool <- lang.Placeholder
|
||||
}
|
||||
@@ -28,10 +32,12 @@ func (l Limit) Return() error {
|
||||
case <-l.pool:
|
||||
return nil
|
||||
default:
|
||||
return ErrReturn
|
||||
return ErrLimitReturn
|
||||
}
|
||||
}
|
||||
|
||||
// TryBorrow tries to borrow an element from Limit, in non-blocking mode.
|
||||
// If success, true returned, false for otherwise.
|
||||
func (l Limit) TryBorrow() bool {
|
||||
select {
|
||||
case l.pool <- lang.Placeholder:
|
||||
|
||||
@@ -13,5 +13,5 @@ func TestLimit(t *testing.T) {
|
||||
assert.False(t, limit.TryBorrow())
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Equal(t, ErrReturn, limit.Return())
|
||||
assert.Equal(t, ErrLimitReturn, limit.Return())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -29,5 +29,5 @@ func TestTimeoutLimit(t *testing.T) {
|
||||
assert.Equal(t, ErrTimeout, limit.Borrow(time.Millisecond*100))
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Nil(t, limit.Return())
|
||||
assert.Equal(t, ErrReturn, limit.Return())
|
||||
assert.Equal(t, ErrLimitReturn, limit.Return())
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ And now, let’s 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, let’s 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
|
||||
|
||||
@@ -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 |
@@ -72,7 +72,7 @@ And now, let’s 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, let’s 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
|
||||
|
||||
@@ -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. 完整调用演示
|
||||
|
||||
|
||||
1
go.mod
1
go.mod
@@ -58,6 +58,7 @@ require (
|
||||
google.golang.org/grpc v1.29.1
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
gopkg.in/h2non/gock.v1 v1.0.15
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
|
||||
5
go.sum
5
go.sum
@@ -141,6 +141,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334 h1:VHgatEHNcBFEB7inlalqfNqw65aNkM1lGX2yt3NmbS8=
|
||||
@@ -205,6 +207,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
|
||||
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
|
||||
@@ -429,6 +432,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
|
||||
@@ -169,4 +169,4 @@ go get -u github.com/tal-tech/go-zero
|
||||
|
||||
## 9. 微信交流群
|
||||
|
||||
<img src="doc/images/wechat.jpg" alt="wechat" width="300" />
|
||||
<img src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/wechat.jpg" alt="wechat" width="300" />
|
||||
|
||||
@@ -28,7 +28,7 @@ func MaxConns(n int) func(http.Handler) http.Handler {
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
} else {
|
||||
internal.Errorf(r, "Concurrent connections over %d, rejected with code %d",
|
||||
internal.Errorf(r, "concurrent connections over %d, rejected with code %d",
|
||||
n, http.StatusServiceUnavailable)
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
}
|
||||
|
||||
@@ -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)"`
|
||||
|
||||
166
tools/goctl/api/parser/parser_test.go
Normal file
166
tools/goctl/api/parser/parser_test.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testApiTemplate = `
|
||||
info(
|
||||
title: doc title
|
||||
desc: >
|
||||
doc description first part,
|
||||
doc description second part<
|
||||
version: 1.0
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
|
||||
service A-api {
|
||||
@server(
|
||||
handler: GreetHandler
|
||||
)
|
||||
get /greet/from/:name(Request) returns (Response)
|
||||
|
||||
@server(
|
||||
handler: NoResponseHandler
|
||||
)
|
||||
get /greet/get(Request) returns
|
||||
}
|
||||
`
|
||||
|
||||
const testMultiServiceTemplate = `
|
||||
info(
|
||||
title: doc title
|
||||
desc: >
|
||||
doc description first part,
|
||||
doc description second part<
|
||||
version: 1.0
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
|
||||
service A-api {
|
||||
@server(
|
||||
handler: GreetHandler
|
||||
)
|
||||
get /greet/from/:name(Request) returns (Response)
|
||||
}
|
||||
|
||||
service A-api {
|
||||
@server(
|
||||
handler: NoResponseHandler
|
||||
)
|
||||
get /greet/get(Request) returns
|
||||
}
|
||||
`
|
||||
|
||||
const apiNoInfo = `
|
||||
type Request struct {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
|
||||
service A-api {
|
||||
@server(
|
||||
handler: GreetHandler
|
||||
)
|
||||
get /greet/from/:name(Request) returns (Response)
|
||||
}
|
||||
`
|
||||
|
||||
const invalidApiFile = `
|
||||
type Request struct {
|
||||
Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Message string ` + "`" + `json:"message"` + "`" + `
|
||||
}
|
||||
|
||||
service A-api
|
||||
@server(
|
||||
handler: GreetHandler
|
||||
)
|
||||
get /greet/from/:name(Request) returns (Response)
|
||||
}
|
||||
`
|
||||
|
||||
func TestParser(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
parser, err := NewParser(filename)
|
||||
assert.Nil(t, err)
|
||||
|
||||
api, err := parser.Parse()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, len(api.Types), 2)
|
||||
assert.Equal(t, len(api.Service.Routes), 2)
|
||||
|
||||
assert.Equal(t, api.Service.Routes[0].Path, "/greet/from/:name")
|
||||
assert.Equal(t, api.Service.Routes[1].Path, "/greet/get")
|
||||
|
||||
assert.Equal(t, api.Service.Routes[1].RequestType.Name, "Request")
|
||||
assert.Equal(t, api.Service.Routes[1].ResponseType.Name, "")
|
||||
}
|
||||
|
||||
func TestMultiService(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(testMultiServiceTemplate), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
parser, err := NewParser(filename)
|
||||
assert.Nil(t, err)
|
||||
|
||||
api, err := parser.Parse()
|
||||
assert.Nil(t, err)
|
||||
|
||||
assert.Equal(t, len(api.Service.Routes), 2)
|
||||
assert.Equal(t, len(api.Service.Groups), 2)
|
||||
}
|
||||
|
||||
func TestApiNoInfo(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(apiNoInfo), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
parser, err := NewParser(filename)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, err = parser.Parse()
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestInvalidApiFile(t *testing.T) {
|
||||
filename := "greet.api"
|
||||
err := ioutil.WriteFile(filename, []byte(invalidApiFile), os.ModePerm)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(filename)
|
||||
|
||||
_, err = NewParser(filename)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
84
zrpc/internal/auth/auth_test.go
Normal file
84
zrpc/internal/auth/auth_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/alicebob/miniredis"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tal-tech/go-zero/core/stores/redis"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
func TestAuthenticator(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
app string
|
||||
token string
|
||||
strict bool
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "strict=false",
|
||||
strict: false,
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "strict=true",
|
||||
strict: true,
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "strict=true,with token",
|
||||
app: "foo",
|
||||
token: "bar",
|
||||
strict: true,
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "strict=true,with error token",
|
||||
app: "foo",
|
||||
token: "error",
|
||||
strict: true,
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
|
||||
r := miniredis.NewMiniRedis()
|
||||
assert.Nil(t, r.Start())
|
||||
defer r.Close()
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
store := redis.NewRedis(r.Addr(), redis.NodeType)
|
||||
if len(test.app) > 0 {
|
||||
assert.Nil(t, store.Hset("apps", test.app, test.token))
|
||||
defer store.Hdel("apps", test.app)
|
||||
}
|
||||
|
||||
authenticator, err := NewAuthenticator(store, "apps", test.strict)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, authenticator.Authenticate(context.Background()))
|
||||
md := metadata.New(map[string]string{})
|
||||
ctx := metadata.NewIncomingContext(context.Background(), md)
|
||||
assert.NotNil(t, authenticator.Authenticate(ctx))
|
||||
md = metadata.New(map[string]string{
|
||||
"app": "",
|
||||
"token": "",
|
||||
})
|
||||
ctx = metadata.NewIncomingContext(context.Background(), md)
|
||||
assert.NotNil(t, authenticator.Authenticate(ctx))
|
||||
md = metadata.New(map[string]string{
|
||||
"app": "foo",
|
||||
"token": "bar",
|
||||
})
|
||||
ctx = metadata.NewIncomingContext(context.Background(), md)
|
||||
err = authenticator.Authenticate(ctx)
|
||||
if test.hasError {
|
||||
assert.NotNil(t, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user