Skip to content

Commit 08882b7

Browse files
committed
fix #7
Signed-off-by: monkey <[email protected]>
1 parent f956dbb commit 08882b7

File tree

2 files changed

+162
-16
lines changed

2 files changed

+162
-16
lines changed

mock.go

+103-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"reflect"
77
"regexp"
8+
"strings"
89
"time"
910

1011
"github.com/go-redis/redis/v8"
@@ -156,28 +157,116 @@ func (m *mock) match(expect expectation, cmd redis.Cmder) error {
156157
return fn(expectArgs, cmdArgs)
157158
}
158159

160+
isMapArgs := m.mapArgs(cmd.Name(), &cmdArgs)
161+
if isMapArgs {
162+
m.mapArgs(expect.name(), &expectArgs)
163+
}
164+
159165
for i := 0; i < len(expectArgs); i++ {
160-
expr, ok := expectArgs[i].(string)
161-
162-
// regular
163-
if expect.regexp() && ok {
164-
cmdValue := fmt.Sprint(cmdArgs[i])
165-
re, err := regexp.Compile(expr)
166-
if err != nil {
167-
return err
166+
// is map?
167+
if isMapArgs {
168+
expectMapArgs, expectOK := expectArgs[i].(map[string]interface{})
169+
cmdMapArgs, cmdOK := cmdArgs[i].(map[string]interface{})
170+
if expectOK && cmdOK {
171+
// there may be the same key
172+
if len(expectMapArgs) != len(cmdMapArgs) {
173+
return fmt.Errorf("wrong number of arguments, expectation regular: '%+v', but gave: '%+v'",
174+
expectArgs, cmdArgs)
175+
}
176+
for expectKey, expectMapVal := range expectMapArgs {
177+
cmdMapVal, ok := cmdMapArgs[expectKey]
178+
if !ok {
179+
return fmt.Errorf("missing command(%s) parameters: %s", expect.name(), expectKey)
180+
}
181+
if err := m.compare(expect.regexp(), expectMapVal, cmdMapVal); err != nil {
182+
return err
183+
}
184+
}
185+
continue
168186
}
169-
if !re.MatchString(cmdValue) {
170-
return fmt.Errorf("%d column does not match, expectation regular: '%s', but gave: '%s'", i, expr, cmdValue)
171-
}
172-
} else if !reflect.DeepEqual(expectArgs[i], cmdArgs[i]) {
173-
return fmt.Errorf("%d column does not `DeepEqual`, expectation: '%+v', but gave: '%+v'",
174-
i, expectArgs[i], cmdArgs[i])
187+
}
188+
if err := m.compare(expect.regexp(), expectArgs[i], cmdArgs[i]); err != nil {
189+
return err
175190
}
176191
}
177192

178193
return nil
179194
}
180195

196+
func (m *mock) compare(isRegexp bool, expect, cmd interface{}) error {
197+
expr, ok := expect.(string)
198+
if isRegexp && ok {
199+
cmdValue := fmt.Sprint(cmd)
200+
re, err := regexp.Compile(expr)
201+
if err != nil {
202+
return err
203+
}
204+
if !re.MatchString(cmdValue) {
205+
return fmt.Errorf("args not match, expectation regular: '%s', but gave: '%s'", expr, cmdValue)
206+
}
207+
} else if !reflect.DeepEqual(expect, cmd) {
208+
return fmt.Errorf("args not `DeepEqual`, expectation: '%+v', but gave: '%+v'", expect, cmd)
209+
}
210+
return nil
211+
}
212+
213+
// using map in command leads to disorder, change the command parameter to map[string]interface{}
214+
// for example:
215+
// [mset key1 value1 key2 value2] => [mset map[string]interface{}{"key1": "value1", "key2": "value2"}]
216+
// return bool, is it handled
217+
func (m *mock) mapArgs(cmd string, cmdArgs *[]interface{}) bool {
218+
var cut int
219+
cmd = strings.ToLower(cmd)
220+
switch cmd {
221+
case "mset", "msetnx":
222+
// 1
223+
cut = 1
224+
case "hset", "hmset":
225+
// 2
226+
cut = 2
227+
case "eval", "evalsha":
228+
// more, i guess nobody uses it (eval/evalsha), miss
229+
return false
230+
default:
231+
return false
232+
}
233+
234+
if n := len(*cmdArgs); n <= cut || (n > (cut+1) && (n-cut)%2 != 0) {
235+
return false
236+
}
237+
238+
mapArgs := make(map[string]interface{})
239+
args := (*cmdArgs)[cut:]
240+
switch v := args[0].(type) {
241+
//[]string and map[string]interface{}, types will not appear normally
242+
case []string:
243+
if len(v)%2 != 0 {
244+
return false
245+
}
246+
for i := 0; i < len(v); i += 2 {
247+
mapArgs[v[i]] = v[i+1]
248+
}
249+
case map[string]interface{}:
250+
if len(v) > 0 {
251+
mapArgs = v
252+
}
253+
default:
254+
for i := 0; i < len(args); i += 2 {
255+
mapArgs[fmt.Sprint(args[i])] = args[i+1]
256+
}
257+
}
258+
259+
if len(mapArgs) == 0 {
260+
return false
261+
}
262+
263+
newCmd := make([]interface{}, cut, cut+1)
264+
copy(newCmd[:cut], (*cmdArgs)[:cut])
265+
newCmd = append(newCmd, mapArgs)
266+
*cmdArgs = newCmd
267+
return true
268+
}
269+
181270
func (m *mock) pushExpect(e expectation) {
182271
if m.expectRegexp {
183272
e.setRegexpMatch()

mock_test.go

+59-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,21 @@ var _ = Describe("RedisMock", func() {
2121
ctx := context.TODO()
2222

2323
var (
24-
client *redis.Client
25-
mock ClientMock
24+
client *redis.Client
25+
mock ClientMock
26+
disorder func() map[string]interface{}
2627
)
2728

2829
BeforeEach(func() {
2930
client, mock = NewClientMock()
31+
disorder = func() map[string]interface{} {
32+
d := make(map[string]interface{})
33+
for i := 0; i < 16; i++ {
34+
k, v := fmt.Sprintf("key-%d", i), fmt.Sprintf("value-%d", i)
35+
d[k] = v
36+
}
37+
return d
38+
}
3039
})
3140

3241
AfterEach(func() {
@@ -756,6 +765,18 @@ var _ = Describe("RedisMock", func() {
756765
})
757766
})
758767

768+
It("MSet Map", func() {
769+
mock.ExpectMSet(disorder()).SetVal("OK")
770+
res, err := client.MSet(ctx, disorder()).Result()
771+
Expect(err).NotTo(HaveOccurred())
772+
Expect(res).To(Equal("OK"))
773+
774+
mock.ExpectMSet("key1", "value1", "key2", "value2").SetVal("OK")
775+
res, err = client.MSet(ctx, "key2", "value2", "key1", "value1").Result()
776+
Expect(err).NotTo(HaveOccurred())
777+
Expect(res).To(Equal("OK"))
778+
})
779+
759780
It("MSetNX", func() {
760781
operationBoolCmd(mock, func() *ExpectedBool {
761782
return mock.ExpectMSetNX()
@@ -764,6 +785,18 @@ var _ = Describe("RedisMock", func() {
764785
})
765786
})
766787

788+
It("MSetNX Map", func() {
789+
mock.ExpectMSetNX(disorder()).SetVal(true)
790+
res, err := client.MSetNX(ctx, disorder()).Result()
791+
Expect(err).NotTo(HaveOccurred())
792+
Expect(res).To(BeTrue())
793+
794+
mock.ExpectMSetNX("key1", "value1", "key2", "value2").SetVal(true)
795+
res, err = client.MSetNX(ctx, "key2", "value2", "key1", "value1").Result()
796+
Expect(err).NotTo(HaveOccurred())
797+
Expect(res).To(BeTrue())
798+
})
799+
767800
It("Set", func() {
768801
operationStatusCmd(mock, func() *ExpectedStatus {
769802
return mock.ExpectSet("key", "value", 1*time.Minute)
@@ -1002,6 +1035,18 @@ var _ = Describe("RedisMock", func() {
10021035
})
10031036
})
10041037

1038+
It("HSet Map", func() {
1039+
mock.ExpectHSet("key", disorder()).SetVal(1)
1040+
res, err := client.HSet(ctx, "key", disorder()).Result()
1041+
Expect(err).NotTo(HaveOccurred())
1042+
Expect(res).To(Equal(int64(1)))
1043+
1044+
mock.ExpectHSet("key", "key1", "value1", "key2", "value2").SetVal(1)
1045+
res, err = client.HSet(ctx, "key", "key2", "value2", "key1", "value1").Result()
1046+
Expect(err).NotTo(HaveOccurred())
1047+
Expect(res).To(Equal(int64(1)))
1048+
})
1049+
10051050
It("HMSet", func() {
10061051
operationBoolCmd(mock, func() *ExpectedBool {
10071052
return mock.ExpectHMSet("key", "field1", "value1", "field2", "value2")
@@ -1010,6 +1055,18 @@ var _ = Describe("RedisMock", func() {
10101055
})
10111056
})
10121057

1058+
It("HMSet Map", func() {
1059+
mock.ExpectHMSet("key", disorder()).SetVal(true)
1060+
res, err := client.HMSet(ctx, "key", disorder()).Result()
1061+
Expect(err).NotTo(HaveOccurred())
1062+
Expect(res).To(BeTrue())
1063+
1064+
mock.ExpectHMSet("key", "key1", "value1", "key2", "value2").SetVal(true)
1065+
res, err = client.HMSet(ctx, "key", "key2", "value2", "key1", "value1").Result()
1066+
Expect(err).NotTo(HaveOccurred())
1067+
Expect(res).To(BeTrue())
1068+
})
1069+
10131070
It("HSetNX", func() {
10141071
operationBoolCmd(mock, func() *ExpectedBool {
10151072
return mock.ExpectHSetNX("key", "field", "value")

0 commit comments

Comments
 (0)