mock_redis 0.19.0 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +161 -0
  3. data/LICENSE.md +21 -0
  4. data/README.md +41 -17
  5. data/lib/mock_redis/connection_method.rb +13 -0
  6. data/lib/mock_redis/database.rb +116 -41
  7. data/lib/mock_redis/expire_wrapper.rb +1 -1
  8. data/lib/mock_redis/future.rb +1 -1
  9. data/lib/mock_redis/geospatial_methods.rb +13 -21
  10. data/lib/mock_redis/hash_methods.rb +34 -15
  11. data/lib/mock_redis/indifferent_hash.rb +0 -8
  12. data/lib/mock_redis/info_method.rb +2 -2
  13. data/lib/mock_redis/list_methods.rb +39 -4
  14. data/lib/mock_redis/memory_method.rb +11 -0
  15. data/lib/mock_redis/multi_db_wrapper.rb +4 -4
  16. data/lib/mock_redis/pipelined_wrapper.rb +32 -15
  17. data/lib/mock_redis/set_methods.rb +34 -6
  18. data/lib/mock_redis/stream/id.rb +58 -0
  19. data/lib/mock_redis/stream.rb +88 -0
  20. data/lib/mock_redis/stream_methods.rb +102 -0
  21. data/lib/mock_redis/string_methods.rb +81 -30
  22. data/lib/mock_redis/transaction_wrapper.rb +32 -21
  23. data/lib/mock_redis/utility_methods.rb +9 -4
  24. data/lib/mock_redis/version.rb +1 -1
  25. data/lib/mock_redis/zset.rb +5 -8
  26. data/lib/mock_redis/zset_methods.rb +64 -12
  27. data/lib/mock_redis.rb +38 -10
  28. metadata +40 -336
  29. data/.gitignore +0 -5
  30. data/.mailmap +0 -2
  31. data/.overcommit.yml +0 -21
  32. data/.rspec +0 -1
  33. data/.rubocop.yml +0 -121
  34. data/.rubocop_todo.yml +0 -35
  35. data/.simplecov +0 -4
  36. data/.travis.yml +0 -33
  37. data/Gemfile +0 -12
  38. data/LICENSE +0 -19
  39. data/Rakefile +0 -2
  40. data/mock_redis.gemspec +0 -30
  41. data/spec/client_spec.rb +0 -17
  42. data/spec/cloning_spec.rb +0 -95
  43. data/spec/commands/append_spec.rb +0 -24
  44. data/spec/commands/auth_spec.rb +0 -7
  45. data/spec/commands/bgrewriteaof_spec.rb +0 -7
  46. data/spec/commands/bgsave_spec.rb +0 -7
  47. data/spec/commands/bitcount_spec.rb +0 -25
  48. data/spec/commands/bitfield_spec.rb +0 -169
  49. data/spec/commands/blpop_spec.rb +0 -59
  50. data/spec/commands/brpop_spec.rb +0 -58
  51. data/spec/commands/brpoplpush_spec.rb +0 -52
  52. data/spec/commands/connected_spec.rb +0 -7
  53. data/spec/commands/dbsize_spec.rb +0 -18
  54. data/spec/commands/decr_spec.rb +0 -34
  55. data/spec/commands/decrby_spec.rb +0 -34
  56. data/spec/commands/del_spec.rb +0 -35
  57. data/spec/commands/disconnect_spec.rb +0 -7
  58. data/spec/commands/echo_spec.rb +0 -11
  59. data/spec/commands/eval_spec.rb +0 -7
  60. data/spec/commands/evalsha_spec.rb +0 -10
  61. data/spec/commands/exists_spec.rb +0 -14
  62. data/spec/commands/expire_spec.rb +0 -111
  63. data/spec/commands/expireat_spec.rb +0 -47
  64. data/spec/commands/flushall_spec.rb +0 -38
  65. data/spec/commands/flushdb_spec.rb +0 -38
  66. data/spec/commands/future_spec.rb +0 -20
  67. data/spec/commands/geoadd_spec.rb +0 -58
  68. data/spec/commands/geodist_spec.rb +0 -114
  69. data/spec/commands/geohash_spec.rb +0 -52
  70. data/spec/commands/geopos_spec.rb +0 -55
  71. data/spec/commands/get_spec.rb +0 -30
  72. data/spec/commands/getbit_spec.rb +0 -34
  73. data/spec/commands/getrange_spec.rb +0 -22
  74. data/spec/commands/getset_spec.rb +0 -23
  75. data/spec/commands/hash_operator_spec.rb +0 -21
  76. data/spec/commands/hdel_spec.rb +0 -54
  77. data/spec/commands/hexists_spec.rb +0 -27
  78. data/spec/commands/hget_spec.rb +0 -28
  79. data/spec/commands/hgetall_spec.rb +0 -32
  80. data/spec/commands/hincrby_spec.rb +0 -58
  81. data/spec/commands/hincrbyfloat_spec.rb +0 -58
  82. data/spec/commands/hkeys_spec.rb +0 -19
  83. data/spec/commands/hlen_spec.rb +0 -19
  84. data/spec/commands/hmget_spec.rb +0 -40
  85. data/spec/commands/hmset_spec.rb +0 -43
  86. data/spec/commands/hscan_each_spec.rb +0 -48
  87. data/spec/commands/hscan_spec.rb +0 -27
  88. data/spec/commands/hset_spec.rb +0 -38
  89. data/spec/commands/hsetnx_spec.rb +0 -44
  90. data/spec/commands/hvals_spec.rb +0 -19
  91. data/spec/commands/incr_spec.rb +0 -34
  92. data/spec/commands/incrby_spec.rb +0 -44
  93. data/spec/commands/incrbyfloat_spec.rb +0 -44
  94. data/spec/commands/info_spec.rb +0 -62
  95. data/spec/commands/keys_spec.rb +0 -122
  96. data/spec/commands/lastsave_spec.rb +0 -8
  97. data/spec/commands/lindex_spec.rb +0 -49
  98. data/spec/commands/linsert_spec.rb +0 -68
  99. data/spec/commands/llen_spec.rb +0 -16
  100. data/spec/commands/lpop_spec.rb +0 -34
  101. data/spec/commands/lpush_spec.rb +0 -43
  102. data/spec/commands/lpushx_spec.rb +0 -46
  103. data/spec/commands/lrange_spec.rb +0 -51
  104. data/spec/commands/lrem_spec.rb +0 -80
  105. data/spec/commands/lset_spec.rb +0 -43
  106. data/spec/commands/ltrim_spec.rb +0 -45
  107. data/spec/commands/mapped_hmget_spec.rb +0 -29
  108. data/spec/commands/mapped_hmset_spec.rb +0 -47
  109. data/spec/commands/mapped_mget_spec.rb +0 -22
  110. data/spec/commands/mapped_mset_spec.rb +0 -19
  111. data/spec/commands/mapped_msetnx_spec.rb +0 -26
  112. data/spec/commands/mget_spec.rb +0 -34
  113. data/spec/commands/move_spec.rb +0 -147
  114. data/spec/commands/mset_spec.rb +0 -29
  115. data/spec/commands/msetnx_spec.rb +0 -40
  116. data/spec/commands/persist_spec.rb +0 -48
  117. data/spec/commands/pexpire_spec.rb +0 -86
  118. data/spec/commands/pexpireat_spec.rb +0 -48
  119. data/spec/commands/ping_spec.rb +0 -7
  120. data/spec/commands/pipelined_spec.rb +0 -42
  121. data/spec/commands/pttl_spec.rb +0 -41
  122. data/spec/commands/quit_spec.rb +0 -7
  123. data/spec/commands/randomkey_spec.rb +0 -20
  124. data/spec/commands/rename_spec.rb +0 -42
  125. data/spec/commands/renamenx_spec.rb +0 -41
  126. data/spec/commands/rpop_spec.rb +0 -34
  127. data/spec/commands/rpoplpush_spec.rb +0 -50
  128. data/spec/commands/rpush_spec.rb +0 -43
  129. data/spec/commands/rpushx_spec.rb +0 -46
  130. data/spec/commands/sadd_spec.rb +0 -45
  131. data/spec/commands/save_spec.rb +0 -7
  132. data/spec/commands/scan_each_spec.rb +0 -39
  133. data/spec/commands/scan_spec.rb +0 -55
  134. data/spec/commands/scard_spec.rb +0 -18
  135. data/spec/commands/script_spec.rb +0 -9
  136. data/spec/commands/sdiff_spec.rb +0 -47
  137. data/spec/commands/sdiffstore_spec.rb +0 -58
  138. data/spec/commands/select_spec.rb +0 -61
  139. data/spec/commands/set_spec.rb +0 -63
  140. data/spec/commands/setbit_spec.rb +0 -54
  141. data/spec/commands/setex_spec.rb +0 -22
  142. data/spec/commands/setnx_spec.rb +0 -25
  143. data/spec/commands/setrange_spec.rb +0 -30
  144. data/spec/commands/sinter_spec.rb +0 -39
  145. data/spec/commands/sinterstore_spec.rb +0 -53
  146. data/spec/commands/sismember_spec.rb +0 -29
  147. data/spec/commands/smembers_spec.rb +0 -28
  148. data/spec/commands/smove_spec.rb +0 -41
  149. data/spec/commands/sort_list_spec.rb +0 -21
  150. data/spec/commands/sort_set_spec.rb +0 -21
  151. data/spec/commands/sort_zset_spec.rb +0 -21
  152. data/spec/commands/spop_spec.rb +0 -25
  153. data/spec/commands/srandmember_spec.rb +0 -49
  154. data/spec/commands/srem_spec.rb +0 -40
  155. data/spec/commands/sscan_each_spec.rb +0 -48
  156. data/spec/commands/sscan_spec.rb +0 -39
  157. data/spec/commands/strlen_spec.rb +0 -18
  158. data/spec/commands/sunion_spec.rb +0 -42
  159. data/spec/commands/sunionstore_spec.rb +0 -59
  160. data/spec/commands/ttl_spec.rb +0 -40
  161. data/spec/commands/type_spec.rb +0 -36
  162. data/spec/commands/unwatch_spec.rb +0 -7
  163. data/spec/commands/watch_spec.rb +0 -16
  164. data/spec/commands/zadd_spec.rb +0 -123
  165. data/spec/commands/zcard_spec.rb +0 -19
  166. data/spec/commands/zcount_spec.rb +0 -39
  167. data/spec/commands/zincrby_spec.rb +0 -31
  168. data/spec/commands/zinterstore_spec.rb +0 -96
  169. data/spec/commands/zrange_spec.rb +0 -80
  170. data/spec/commands/zrangebyscore_spec.rb +0 -83
  171. data/spec/commands/zrank_spec.rb +0 -29
  172. data/spec/commands/zrem_spec.rb +0 -43
  173. data/spec/commands/zremrangebyrank_spec.rb +0 -27
  174. data/spec/commands/zremrangebyscore_spec.rb +0 -35
  175. data/spec/commands/zrevrange_spec.rb +0 -56
  176. data/spec/commands/zrevrangebyscore_spec.rb +0 -58
  177. data/spec/commands/zrevrank_spec.rb +0 -29
  178. data/spec/commands/zscan_each_spec.rb +0 -48
  179. data/spec/commands/zscan_spec.rb +0 -26
  180. data/spec/commands/zscore_spec.rb +0 -22
  181. data/spec/commands/zunionstore_spec.rb +0 -104
  182. data/spec/mock_redis_spec.rb +0 -86
  183. data/spec/spec_helper.rb +0 -63
  184. data/spec/support/redis_multiplexer.rb +0 -106
  185. data/spec/support/shared_examples/only_operates_on_hashes.rb +0 -13
  186. data/spec/support/shared_examples/only_operates_on_lists.rb +0 -13
  187. data/spec/support/shared_examples/only_operates_on_sets.rb +0 -13
  188. data/spec/support/shared_examples/only_operates_on_strings.rb +0 -13
  189. data/spec/support/shared_examples/only_operates_on_zsets.rb +0 -57
  190. data/spec/support/shared_examples/sorts_enumerables.rb +0 -56
  191. data/spec/transactions_spec.rb +0 -159
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da2c2693b445e51b487e52f7c9b3986960417686792f9a5e554ca4d215c323e2
4
- data.tar.gz: 8da2170fcc49e768133a61deb716d72cc18d66291567008d96287b8d2104b15c
3
+ metadata.gz: 7e495091d84ea1fec4bf5206d2a02e32c446ef0bd89b0feaed38f31d95b142b2
4
+ data.tar.gz: 696f65058fe29cd5fa849363b6cfa60454a432c0d392f530ac1494cba31ff062
5
5
  SHA512:
6
- metadata.gz: 15b8b59cff1120de0befaee6cf08facfb120e239d3fbc1b6b55f40bb60c526f57e5cbb61f1d9f6fd5538067122e49b80e9e782590cd98070a3d49943ec8bb929
7
- data.tar.gz: 52a09f0896a405137be0075db930bec09898dd96e3d5366a95ef6a189268348f91244c051dbe45db5b4b37023a12f87552868fcaf2a5383316bebe9bdaf44997
6
+ metadata.gz: 78b558108b350dc35df4d1f202e96326096394f9e23d89398d07cdbb33c6b088e0089467b14624a9e2c73210406457e291ab0cb09495a8d12a854df5ab8da1c3
7
+ data.tar.gz: f7e6d3021a4f2f5dd78e53d126ecea26fa7d3f8903bbcf96b0cf2a03b74049acbb169a18ff571c2aab18a0678cf57b90908d06b8c0e10039e00fce5b6050ad26
data/CHANGELOG.md CHANGED
@@ -1,5 +1,166 @@
1
1
  # MockRedis Changelog
2
2
 
3
+ ### 0.44.0
4
+
5
+ * Add suppore for `memory usage` command
6
+
7
+ ### 0.43.0
8
+
9
+ * Fix issue when using with `redis-store` gem
10
+
11
+ ### 0.42.0
12
+
13
+ * Drop support for Ruby 2.x
14
+ * Add support for `srem?`
15
+
16
+ ### 0.41.0
17
+
18
+ * Add support for `expire`-related command options `nx`/`xx`/`lt`/`gt`
19
+
20
+ ### 0.40.0
21
+
22
+ * Add support for `call` method name to be case insensitive
23
+ * Add support for `zmscore`
24
+
25
+ ### 0.39.0
26
+
27
+ * Add support for calling `call` in `pipelined` block
28
+ * Fix `sadd` to stringify array
29
+
30
+ ### 0.38.0
31
+
32
+ * Fix detection of stream key types
33
+ * Add support for `EXAT` AND `PXAT` arguments to `SET` command
34
+
35
+ ### 0.37.0
36
+
37
+ * Require Ruby 2.7 or newer, since Ruby 2.6 and older are EOL
38
+ * Remove unnecessary files from gem contents
39
+ * Add support for popping multiple items with `lpop`
40
+ * Add support for `scan` with type option
41
+ * Add support for `sadd?`
42
+ * Add support for scanning on keys containing hash tags (`{...}`)
43
+
44
+ ### 0.36.0
45
+
46
+ * Add support for `smismember`
47
+ * Add support for `lmove` and `blmove`
48
+ * Fix `zrem` to support passing array of integers
49
+
50
+ ### 0.35.0
51
+
52
+ * Add support for `getdel`
53
+
54
+ ### 0.34.0
55
+
56
+ * Add support for `with`
57
+
58
+ ### 0.33.0
59
+
60
+ * Add support for `GET` argument to `SET` command
61
+
62
+ ### 0.32.0
63
+
64
+ * Add support for `psetex`
65
+
66
+ ### 0.31.0
67
+
68
+ * Allow `ping` to take argument
69
+ * Raise `CommandError` on `hmget` with empty list of fields
70
+
71
+ ### 0.30.0
72
+
73
+ * Drop support for Ruby 2.4 and Ruby 2.5 since they are EOL
74
+ * Fix `expire` to to raise error on invalid integer
75
+
76
+ ### 0.29.0
77
+
78
+ * Add support for `logger` option ([#211](https://github.com/sds/mock_redis/pull/211))
79
+ * Fix `zadd` to not perform conditional type conversion ([#214](https://github.com/sds/mock_redis/pull/214))
80
+ * Fix `hdel` to raise error when called with empty array ([#215](https://github.com/sds/mock_redis/pull/215))
81
+
82
+ ### 0.28.0
83
+
84
+ * Fix `hmset` exception ([#206](https://github.com/sds/mock_redis/pull/206))
85
+ * Fix `hmset` to accept hashes in addition to key/value pairs ([#208](https://github.com/sds/mock_redis/pull/208))
86
+ * Fix stream ID regex to support `(` ([#209](https://github.com/sds/mock_redis/pull/209))
87
+ * Allow `mget` to accept a block ([#210](https://github.com/sds/mock_redis/pull/210))
88
+
89
+ ### 0.27.3
90
+
91
+ * Ensure `ruby2_keywords` dependency is `require`d at runtime
92
+
93
+ ### 0.27.2
94
+
95
+ * Switch `ruby2_keywords` gem from development dependency to runtime dependency
96
+
97
+ ### 0.27.1
98
+
99
+ * Fix missing `ruby2_keywords` gem
100
+ * Allow passing string `offset` to `getbit` ([#203](https://github.com/sds/mock_redis/pull/203))
101
+
102
+ ### 0.27.0
103
+
104
+ * Fix handling of keyword arguments on Ruby 3 ([#199](https://github.com/sds/mock_redis/pull/199))
105
+ * Allow passing string `offset` to `setbit` ([#200](https://github.com/sds/mock_redis/pull/200))
106
+ * Add `connection` method ([#201](https://github.com/sds/mock_redis/pull/201))
107
+
108
+ ### 0.26.0
109
+
110
+ * Add block and count support to `xread` ([#194](https://github.com/sds/mock_redis/pull/194))
111
+
112
+ ### 0.25.0
113
+
114
+ * Add support for `xread` command ([#190](https://github.com/sds/mock_redis/pull/190))
115
+ * Fix `mget` to raise error when passing empty array ([#191](https://github.com/sds/mock_redis/pull/191))
116
+ * Fix `xadd` when `maxlen` is zero ([#192](https://github.com/sds/mock_redis/pull/192))
117
+
118
+ ### 0.24.0
119
+
120
+ * Fix handling of blocks within `multi` blocks ([#185](https://github.com/sds/mock_redis/pull/185))
121
+ * Fix handling of multiple consecutive `?` characters in key pattern matching ([#186](https://github.com/sds/mock_redis/pull/186))
122
+ * Change `exists` to return an integer and add `exists?` ([#188](https://github.com/sds/mock_redis/pull/188))
123
+
124
+ ### 0.23.0
125
+
126
+ * Raise error when `setex` called with negative timeout ([#174](https://github.com/sds/mock_redis/pull/174))
127
+ * Add support for `dump`/`restore` between MockRedis instances ([#176](https://github.com/sds/mock_redis/pull/176))
128
+ * Fix warnings for ZSET methods on Ruby 2.7 ([#177](https://github.com/sds/mock_redis/pull/177))
129
+ * Add support for returning time in pipelines ([#179](https://github.com/sds/mock_redis/pull/179))
130
+ * Fix SET methods to correct set milliseconds with `px` ([#180](https://github.com/sds/mock_redis/pull/180))
131
+ * Add support for unsorted sets within `zinterstore`/`zunionstore`([#182](https://github.com/sds/mock_redis/pull/182))
132
+
133
+ ### 0.22.0
134
+
135
+ * Gracefully handle cursors larger than the collection size in scan commands ([#171](https://github.com/sds/mock_redis/pull/171))
136
+ * Add `zpopmin` and `zpopmax` commands ([#172](https://github.com/sds/mock_redis/pull/172))
137
+ * Fix `hmset` to support array arguments ([#173](https://github.com/sds/mock_redis/pull/173))
138
+ * Fix `hmset` to always treat keys as strings ([#173](https://github.com/sds/mock_redis/pull/173))
139
+ * Remove unnecessary dependency on `rake` gem
140
+
141
+ ### 0.21.0
142
+
143
+ * Fix behavior of `time` to return array of two integers ([#161](https://github.com/sds/mock_redis/pull/161))
144
+ * Add support for `close` and `disconnect!` ([#163](https://github.com/sds/mock_redis/pull/163))
145
+ * Fix `set` to properly handle (and ignore) other options ([#164](https://github.com/sds/mock_redis/pull/163))
146
+ * Fix `srem` to allow array of integers as argument ([#166](https://github.com/sds/mock_redis/pull/166))
147
+ * Fix `hdel` to allow array as argument ([#168](https://github.com/sds/mock_redis/pull/168))
148
+
149
+ ### 0.20.0
150
+
151
+ * Add support for `count` parameter of `spop`
152
+ * Fix `mget` and `mset` to accept array as parameters
153
+ * Fix pipelined array replies
154
+ * Fix nested pipelining
155
+ * Allow nested multi
156
+ * Require Redis gem 4.0.1 or newer
157
+ * Add support for stream commands on Redis 5
158
+ * Keep empty strings on type mismatch
159
+ * Improve performance of `set_expiration`
160
+ * Fix `watch` to allow multiple keys
161
+ * Add `unlink` alias for `del`
162
+ * Drop support for Ruby 2.3 or older
163
+
3
164
  ### 0.19.0
4
165
 
5
166
  * Require Ruby 2.2+
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MockRedis released under the MIT license.
2
+
3
+ > Copyright (c) Shane da Silva. http://shane.io
4
+ >
5
+ > Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ > of this software and associated documentation files (the "Software"), to deal
7
+ > in the Software without restriction, including without limitation the rights
8
+ > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ > copies of the Software, and to permit persons to whom the Software is
10
+ > furnished to do so, subject to the following conditions:
11
+ >
12
+ > The above copyright notice and this permission notice shall be included in
13
+ > all copies or substantial portions of the Software.
14
+ >
15
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ > SOFTWARE.
data/README.md CHANGED
@@ -1,18 +1,27 @@
1
1
  # MockRedis
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/mock_redis.svg)](http://badge.fury.io/rb/mock_redis)
4
- [![Build Status](https://travis-ci.org/brigade/mock_redis.svg)](https://travis-ci.org/brigade/mock_redis)
5
- [![Coverage Status](https://coveralls.io/repos/brigade/mock_redis/badge.svg)](https://coveralls.io/r/brigade/mock_redis)
4
+ ![Build Status](https://github.com/sds/mock_redis/actions/workflows/tests.yml/badge.svg)
5
+ [![Coverage Status](https://coveralls.io/repos/sds/mock_redis/badge.svg)](https://coveralls.io/r/sds/mock_redis)
6
6
 
7
7
  MockRedis provides the same interface as `redis-rb`, but it stores its
8
8
  data in memory instead of talking to a Redis server. It is intended
9
9
  for use in tests.
10
10
 
11
- The current implementation is tested against *Redis 4*. Older versions
12
- of Redis may return different results or not support some commands.
11
+ ## Requirements
12
+
13
+ Ruby 3.0+
14
+
15
+ The current implementation is tested against Redis 6.2. Older versions may work, but can also return different results or not support some commands.
13
16
 
14
17
  ## Getting Started
15
18
 
19
+ Install the gem:
20
+
21
+ ```bash
22
+ gem install mock_redis
23
+ ```
24
+
16
25
  It's as easy as `require 'mock_redis'; mr = MockRedis.new`. Then you can
17
26
  call the same methods on it as you can call on a real `Redis` object.
18
27
 
@@ -46,8 +55,8 @@ since it's an in-memory object confined to a single process. MockRedis
46
55
  makes every attempt to be Redis-compatible, but there are some
47
56
  necessary exceptions.
48
57
 
49
- * Blocking list commands (`#blpop`, `#brpop`, and `#brpoplpush`) work
50
- as expected if there is data for them to retrieve. If you use one of
58
+ * Blocking list commands (`#blmove`, `#blpop`, `#brpop`, and `#brpoplpush`)
59
+ work as expected if there is data for them to retrieve. If you use one of
51
60
  these commands with a nonzero timeout and there is no data for it to
52
61
  retrieve, then the command returns immediately. However, if you ask
53
62
  one of these commands for data with a 0 timeout (means "wait
@@ -90,17 +99,32 @@ please submit a pull request with your (tested!) implementation.
90
99
  * `#config(:get|:set|:resetstat)` isn't done. They can just return
91
100
  canned values.
92
101
 
93
- ## Compatibility
102
+ ## Running tests
103
+
104
+ We recommend running Redis within a Docker container to make development as simple as possible, but as long as you have a Redis instance listening on `localhost:6379` you should be good to go.
105
+
106
+ 1. Start Redis.
107
+ ```bash
108
+ docker run --rm -p 6379:6379 redis:6.2-alpine
109
+ ```
110
+ 2. Install dependencies.
111
+ ```bash
112
+ bundle install
113
+ ```
114
+ 3. Run tests.
115
+ ```bash
116
+ bundle exec rspec
117
+ ```
118
+
119
+ These tests were written with Redis running on `localhost` without any
120
+ passwords required. If you're using a different version of Redis, you
121
+ may see failures due to error message text being different and other
122
+ breaking changes over time.
123
+
124
+ ## Changelog
94
125
 
95
- As of version `0.19.0`, Ruby 2.2 and above are supported. For
96
- older versions of Ruby, use `0.18.0` or older.
126
+ If you're interested in seeing the changes and bug fixes between each version of `mock_redis`, read the [MockRedis Changelog](CHANGELOG.md).
97
127
 
98
- ## Running the Tests
128
+ ## License
99
129
 
100
- If you want to work on this, you'll probably want to run the
101
- tests. (Just kidding! There's no probably about it.) These tests were
102
- written with Redis running on `localhost` without any passwords
103
- required. If you're using a different version of Redis, you may see
104
- failures due to error message text being different. If you're running
105
- a really old version of Redis, you'll definitely see failures due to
106
- stuff that doesn't work!
130
+ This project is released under the [MIT license](LICENSE.md).
@@ -0,0 +1,13 @@
1
+ class MockRedis
2
+ module ConnectionMethod
3
+ def connection
4
+ {
5
+ :host => @base.host,
6
+ :port => @base.port,
7
+ :db => @base.db,
8
+ :id => @base.id,
9
+ :location => "#{@base.host}:#{@base.port}"
10
+ }
11
+ end
12
+ end
13
+ end
@@ -10,6 +10,9 @@ require 'mock_redis/indifferent_hash'
10
10
  require 'mock_redis/info_method'
11
11
  require 'mock_redis/utility_methods'
12
12
  require 'mock_redis/geospatial_methods'
13
+ require 'mock_redis/stream_methods'
14
+ require 'mock_redis/connection_method'
15
+ require 'mock_redis/memory_method'
13
16
 
14
17
  class MockRedis
15
18
  class Database
@@ -22,6 +25,9 @@ class MockRedis
22
25
  include InfoMethod
23
26
  include UtilityMethods
24
27
  include GeospatialMethods
28
+ include StreamMethods
29
+ include ConnectionMethod
30
+ include MemoryMethod
25
31
 
26
32
  attr_reader :data, :expire_times
27
33
 
@@ -33,12 +39,19 @@ class MockRedis
33
39
 
34
40
  def initialize_copy(_source)
35
41
  @data = @data.clone
36
- @data.keys.each { |k| @data[k] = @data[k].clone }
42
+ @data.each_key { |k| @data[k] = @data[k].clone }
37
43
  @expire_times = @expire_times.map(&:clone)
38
44
  end
39
45
 
40
46
  # Redis commands go below this line and above 'private'
41
47
 
48
+ # FIXME: Current implementation of `call` does not work propetly with kwarg-options.
49
+ # i.e. `call("EXPIRE", "foo", 40, "NX")` (which redis-rb will simply transmit to redis-server)
50
+ # will be passed to `#expire` without keywords transformation.
51
+ def call(command, &_block)
52
+ public_send(command[0].downcase, *command[1..])
53
+ end
54
+
42
55
  def auth(_)
43
56
  'OK'
44
57
  end
@@ -54,6 +67,8 @@ class MockRedis
54
67
  def disconnect
55
68
  nil
56
69
  end
70
+ alias close disconnect
71
+ alias disconnect! close
57
72
 
58
73
  def connected?
59
74
  true
@@ -65,7 +80,7 @@ class MockRedis
65
80
 
66
81
  def del(*keys)
67
82
  keys = keys.flatten.map(&:to_s)
68
- assert_has_args(keys, 'del')
83
+ # assert_has_args(keys, 'del') # no longer errors in redis > v4.5
69
84
 
70
85
  keys.
71
86
  find_all { |key| data[key] }.
@@ -73,48 +88,81 @@ class MockRedis
73
88
  each { |k| data.delete(k) }.
74
89
  length
75
90
  end
91
+ alias unlink del
76
92
 
77
93
  def echo(msg)
78
94
  msg.to_s
79
95
  end
80
96
 
81
- def expire(key, seconds)
82
- pexpire(key, seconds.to_i * 1000)
97
+ def expire(key, seconds, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
98
+ assert_valid_integer(seconds)
99
+
100
+ pexpire(key, seconds.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt)
83
101
  end
84
102
 
85
- def pexpire(key, ms)
86
- now_ms = (@base.now.to_r * 1000).to_i
87
- pexpireat(key, now_ms + ms.to_i)
103
+ def pexpire(key, ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
104
+ assert_valid_integer(ms)
105
+
106
+ now, miliseconds = @base.now
107
+ now_ms = (now * 1000) + miliseconds
108
+ pexpireat(key, now_ms + ms.to_i, nx: nx, xx: xx, lt: lt, gt: gt)
88
109
  end
89
110
 
90
- def expireat(key, timestamp)
91
- unless looks_like_integer?(timestamp.to_s)
92
- raise Redis::CommandError, 'ERR value is not an integer or out of range'
93
- end
111
+ def expireat(key, timestamp, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
112
+ assert_valid_integer(timestamp)
94
113
 
95
- pexpireat(key, timestamp.to_i * 1000)
114
+ pexpireat(key, timestamp.to_i * 1000, nx: nx, xx: xx, lt: lt, gt: gt)
96
115
  end
97
116
 
98
- def pexpireat(key, timestamp_ms)
99
- unless looks_like_integer?(timestamp_ms.to_s)
100
- raise Redis::CommandError, 'ERR value is not an integer or out of range'
117
+ def pexpireat(key, timestamp_ms, nx: nil, xx: nil, lt: nil, gt: nil) # rubocop:disable Metrics/ParameterLists
118
+ assert_valid_integer(timestamp_ms)
119
+
120
+ if nx && gt || gt && lt || lt && nx || nx && xx
121
+ raise Redis::CommandError, <<~TXT.chomp
122
+ ERR NX and XX, GT or LT options at the same time are not compatible
123
+ TXT
101
124
  end
102
125
 
103
- if exists(key)
104
- timestamp = Rational(timestamp_ms.to_i, 1000)
105
- set_expiration(key, @base.time_at(timestamp))
126
+ return false unless exists?(key)
127
+
128
+ expiry = expiration(key)
129
+ new_expiry = @base.time_at(Rational(timestamp_ms.to_i, 1000))
130
+
131
+ if should_update_expiration?(expiry, new_expiry, nx: nx, xx: xx, lt: lt, gt: gt)
132
+ set_expiration(key, new_expiry)
106
133
  true
107
134
  else
108
135
  false
109
136
  end
110
137
  end
111
138
 
112
- def exists(key)
113
- data.key?(key)
139
+ def exists(*keys)
140
+ keys.count { |key| data.key?(key) }
141
+ end
142
+
143
+ def exists?(*keys)
144
+ keys.each { |key| return true if data.key?(key) }
145
+ false
114
146
  end
115
147
 
116
148
  def flushdb
117
- data.keys.each { |k| del(k) }
149
+ data.each_key { |k| del(k) }
150
+ 'OK'
151
+ end
152
+
153
+ def dump(key)
154
+ value = data[key]
155
+ value ? Marshal.dump(value) : nil
156
+ end
157
+
158
+ def restore(key, ttl, value, replace: false)
159
+ if !replace && exists?(key)
160
+ raise Redis::CommandError, 'BUSYKEY Target key name already exists.'
161
+ end
162
+ data[key] = Marshal.load(value) # rubocop:disable Security/MarshalLoad
163
+ if ttl > 0
164
+ pexpire(key, ttl)
165
+ end
118
166
  'OK'
119
167
  end
120
168
 
@@ -137,11 +185,11 @@ class MockRedis
137
185
  end
138
186
 
139
187
  def lastsave
140
- @base.now.to_i
188
+ now.first
141
189
  end
142
190
 
143
191
  def persist(key)
144
- if exists(key) && has_expiration?(key)
192
+ if exists?(key) && has_expiration?(key)
145
193
  remove_expiration(key)
146
194
  true
147
195
  else
@@ -149,8 +197,8 @@ class MockRedis
149
197
  end
150
198
  end
151
199
 
152
- def ping
153
- 'PONG'
200
+ def ping(response = 'PONG')
201
+ response
154
202
  end
155
203
 
156
204
  def quit
@@ -182,7 +230,7 @@ class MockRedis
182
230
  raise Redis::CommandError, 'ERR no such key'
183
231
  end
184
232
 
185
- if exists(newkey)
233
+ if exists?(newkey)
186
234
  false
187
235
  else
188
236
  rename(key, newkey)
@@ -195,27 +243,38 @@ class MockRedis
195
243
  end
196
244
 
197
245
  def ttl(key)
198
- if !exists(key)
246
+ if !exists?(key)
199
247
  -2
200
248
  elsif has_expiration?(key)
201
- expiration(key).to_i - @base.now.to_i
249
+ now, = @base.now
250
+ expiration(key).to_i - now
202
251
  else
203
252
  -1
204
253
  end
205
254
  end
206
255
 
207
256
  def pttl(key)
208
- if !exists(key)
257
+ now, miliseconds = @base.now
258
+ now_ms = now * 1000 + miliseconds
259
+
260
+ if !exists?(key)
209
261
  -2
210
262
  elsif has_expiration?(key)
211
- (expiration(key).to_r * 1000).to_i - (@base.now.to_r * 1000).to_i
263
+ (expiration(key).to_r * 1000).to_i - now_ms
212
264
  else
213
265
  -1
214
266
  end
215
267
  end
216
268
 
269
+ def now
270
+ current_time = @base.options[:time_class].now
271
+ miliseconds = (current_time.to_r - current_time.to_i) * 1_000
272
+ [current_time.to_i, miliseconds.to_i]
273
+ end
274
+ alias time now
275
+
217
276
  def type(key)
218
- if !exists(key)
277
+ if !exists?(key)
219
278
  'none'
220
279
  elsif hashy?(key)
221
280
  'hash'
@@ -227,6 +286,8 @@ class MockRedis
227
286
  'set'
228
287
  elsif zsety?(key)
229
288
  'zset'
289
+ elsif streamy?(key)
290
+ 'stream'
230
291
  else
231
292
  raise ArgumentError, "Not sure how #{data[key].inspect} got in here"
232
293
  end
@@ -240,6 +301,13 @@ class MockRedis
240
301
 
241
302
  private
242
303
 
304
+ def assert_valid_integer(integer)
305
+ unless looks_like_integer?(integer.to_s)
306
+ raise Redis::CommandError, 'ERR value is not an integer or out of range'
307
+ end
308
+ integer
309
+ end
310
+
243
311
  def assert_valid_timeout(timeout)
244
312
  if !looks_like_integer?(timeout.to_s)
245
313
  raise Redis::CommandError, 'ERR timeout is not an integer or out of range'
@@ -271,7 +339,7 @@ class MockRedis
271
339
  end
272
340
 
273
341
  def expiration(key)
274
- expire_times.find { |(_, k)| k == key.to_s }.first
342
+ expire_times.find { |(_, k)| k == key.to_s }&.first
275
343
  end
276
344
 
277
345
  def has_expiration?(key)
@@ -286,11 +354,19 @@ class MockRedis
286
354
  !!Float(str) rescue false
287
355
  end
288
356
 
357
+ def should_update_expiration?(expiry, new_expiry, nx:, xx:, lt:, gt:) # rubocop:disable Metrics/ParameterLists
358
+ return false if nx && expiry || xx && !expiry
359
+ return false if lt && expiry && new_expiry > expiry
360
+ return false if gt && (!expiry || new_expiry < expiry)
361
+
362
+ true
363
+ end
364
+
289
365
  def redis_pattern_to_ruby_regex(pattern)
290
366
  Regexp.new(
291
367
  "^#{pattern}$".
292
- gsub(/([+|()])/, '\\\\\1').
293
- gsub(/([^\\])\?/, '\\1.').
368
+ gsub(/([+|(){}])/, '\\\\\1').
369
+ gsub(/(?<!\\)\?/, '\\1.').
294
370
  gsub(/([^\\])\*/, '\\1.*')
295
371
  )
296
372
  end
@@ -303,11 +379,9 @@ class MockRedis
303
379
 
304
380
  def set_expiration(key, time)
305
381
  remove_expiration(key)
306
-
307
- expire_times << [time, key.to_s]
308
- expire_times.sort! do |a, b|
309
- a.first <=> b.first
310
- end
382
+ found = expire_times.each_with_index.to_a.bsearch { |item, _| item.first >= time }
383
+ index = found ? found.last : -1
384
+ expire_times.insert(index, [time, key.to_s])
311
385
  end
312
386
 
313
387
  def zero_pad(string, desired_length)
@@ -320,10 +394,11 @@ class MockRedis
320
394
  # This method isn't private, but it also isn't a Redis command, so
321
395
  # it doesn't belong up above with all the Redis commands.
322
396
  def expire_keys
323
- now = @base.now
397
+ now_sec, miliseconds = now
398
+ now_ms = now_sec * 1_000 + miliseconds
324
399
 
325
400
  to_delete = expire_times.take_while do |(time, _key)|
326
- (time.to_r * 1_000).to_i <= (now.to_r * 1_000).to_i
401
+ (time.to_r * 1_000).to_i <= now_ms
327
402
  end
328
403
 
329
404
  to_delete.each do |(_time, key)|
@@ -12,7 +12,7 @@ class MockRedis
12
12
  @db = db
13
13
  end
14
14
 
15
- def method_missing(method, *args, &block)
15
+ ruby2_keywords def method_missing(method, *args, &block)
16
16
  @db.expire_keys
17
17
  @db.send(method, *args, &block)
18
18
  end
@@ -17,7 +17,7 @@ class MockRedis
17
17
 
18
18
  def store_result(result)
19
19
  @result_set = true
20
- @result = result
20
+ @result = @block ? @block.call(result) : result
21
21
  end
22
22
  end
23
23
  end