mudis 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +695 -568
- data/lib/example_mudis_server_config.md +39 -0
- data/lib/mudis/version.rb +3 -3
- data/lib/mudis.rb +521 -497
- data/lib/mudis_client.rb +68 -0
- data/lib/mudis_config.rb +25 -25
- data/lib/mudis_server.rb +79 -0
- data/sig/mudis.rbs +56 -54
- data/sig/mudis_config.rbs +10 -10
- data/spec/eviction_spec.rb +29 -0
- data/spec/guardrails_spec.rb +138 -138
- data/spec/memory_guard_spec.rb +33 -0
- data/spec/metrics_spec.rb +34 -0
- data/spec/mudis_spec.rb +183 -314
- data/spec/namespace_spec.rb +69 -0
- data/spec/reset_spec.rb +31 -0
- metadata +16 -7
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Mudis Metrics" do # rubocop:disable Metrics/BlockLength
|
6
|
+
it "tracks hits and misses" do
|
7
|
+
Mudis.write("hit_me", "value")
|
8
|
+
Mudis.read("hit_me")
|
9
|
+
Mudis.read("miss_me")
|
10
|
+
metrics = Mudis.metrics
|
11
|
+
expect(metrics[:hits]).to eq(1)
|
12
|
+
expect(metrics[:misses]).to eq(1)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "includes per-bucket stats" do
|
16
|
+
Mudis.write("a", "x" * 50)
|
17
|
+
metrics = Mudis.metrics
|
18
|
+
expect(metrics).to include(:buckets)
|
19
|
+
expect(metrics[:buckets]).to be_an(Array)
|
20
|
+
expect(metrics[:buckets].first).to include(:index, :keys, :memory_bytes, :lru_size)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "resets only the metrics without clearing cache" do
|
24
|
+
Mudis.write("metrics_key", "value")
|
25
|
+
Mudis.read("metrics_key")
|
26
|
+
Mudis.read("missing_key")
|
27
|
+
expect(Mudis.metrics[:hits]).to eq(1)
|
28
|
+
expect(Mudis.metrics[:misses]).to eq(1)
|
29
|
+
Mudis.reset_metrics!
|
30
|
+
expect(Mudis.metrics[:hits]).to eq(0)
|
31
|
+
expect(Mudis.metrics[:misses]).to eq(0)
|
32
|
+
expect(Mudis.read("metrics_key")).to eq("value")
|
33
|
+
end
|
34
|
+
end
|
data/spec/mudis_spec.rb
CHANGED
@@ -1,314 +1,183 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "spec_helper"
|
4
|
-
|
5
|
-
RSpec.describe Mudis do # rubocop:disable Metrics/BlockLength
|
6
|
-
before(:each) do
|
7
|
-
Mudis.stop_expiry_thread
|
8
|
-
Mudis.instance_variable_set(:@buckets, nil)
|
9
|
-
Mudis.instance_variable_set(:@stores, Array.new(Mudis.buckets) { {} })
|
10
|
-
Mudis.instance_variable_set(:@mutexes, Array.new(Mudis.buckets) { Mutex.new })
|
11
|
-
Mudis.instance_variable_set(:@lru_heads, Array.new(Mudis.buckets) { nil })
|
12
|
-
Mudis.instance_variable_set(:@lru_tails, Array.new(Mudis.buckets) { nil })
|
13
|
-
Mudis.instance_variable_set(:@lru_nodes, Array.new(Mudis.buckets) { {} })
|
14
|
-
Mudis.instance_variable_set(:@current_bytes, Array.new(Mudis.buckets, 0))
|
15
|
-
Mudis.instance_variable_set(:@metrics, { hits: 0, misses: 0, evictions: 0, rejected: 0 })
|
16
|
-
Mudis.serializer = JSON
|
17
|
-
Mudis.compress = false
|
18
|
-
Mudis.max_value_bytes = nil
|
19
|
-
end
|
20
|
-
|
21
|
-
describe ".write and .read" do
|
22
|
-
it "writes and reads a value" do
|
23
|
-
Mudis.write("foo", { bar: "baz" })
|
24
|
-
result = Mudis.read("foo")
|
25
|
-
expect(result).to eq({ "bar" => "baz" })
|
26
|
-
end
|
27
|
-
|
28
|
-
it "returns nil for non-existent keys" do
|
29
|
-
expect(Mudis.read("nope")).to be_nil
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
describe ".exists?" do
|
34
|
-
it "returns true if key exists" do
|
35
|
-
Mudis.write("check", [1, 2, 3])
|
36
|
-
expect(Mudis.exists?("check")).to be true
|
37
|
-
end
|
38
|
-
|
39
|
-
it "returns false if key does not exist" do
|
40
|
-
expect(Mudis.exists?("missing")).to be false
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe ".delete" do
|
45
|
-
it "deletes a key" do
|
46
|
-
Mudis.write("temp", 42)
|
47
|
-
Mudis.delete("temp")
|
48
|
-
expect(Mudis.read("temp")).to be_nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe ".update" do
|
53
|
-
it "updates a cached value" do
|
54
|
-
Mudis.write("counter", 5)
|
55
|
-
Mudis.update("counter") { |v| v + 1 }
|
56
|
-
expect(Mudis.read("counter")).to eq(6)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
describe ".fetch" do
|
61
|
-
it "returns cached value if exists" do
|
62
|
-
Mudis.write("k", 123)
|
63
|
-
result = Mudis.fetch("k", expires_in: 60) { 999 } # fix: use keyword arg
|
64
|
-
expect(result).to eq(123)
|
65
|
-
end
|
66
|
-
|
67
|
-
it "writes and returns block result if missing" do
|
68
|
-
Mudis.delete("k")
|
69
|
-
result = Mudis.fetch("k", expires_in: 60) { 999 } # fix
|
70
|
-
expect(result).to eq(999)
|
71
|
-
expect(Mudis.read("k")).to eq(999)
|
72
|
-
end
|
73
|
-
|
74
|
-
it "forces overwrite if force: true" do
|
75
|
-
Mudis.write("k", 100)
|
76
|
-
result = Mudis.fetch("k", force: true) { 200 } # fix
|
77
|
-
expect(result).to eq(200)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
describe ".clear" do
|
82
|
-
it "removes a key from the cache" do
|
83
|
-
Mudis.write("to_clear", 123)
|
84
|
-
expect(Mudis.read("to_clear")).to eq(123)
|
85
|
-
Mudis.clear("to_clear")
|
86
|
-
expect(Mudis.read("to_clear")).to be_nil
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
describe ".replace" do
|
91
|
-
it "replaces value only if key exists" do
|
92
|
-
Mudis.write("to_replace", 100)
|
93
|
-
Mudis.replace("to_replace", 200)
|
94
|
-
expect(Mudis.read("to_replace")).to eq(200)
|
95
|
-
|
96
|
-
Mudis.delete("to_replace")
|
97
|
-
Mudis.replace("to_replace", 300)
|
98
|
-
expect(Mudis.read("to_replace")).to be_nil
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
describe ".inspect" do
|
103
|
-
it "returns metadata for a cached key" do
|
104
|
-
Mudis.write("key1", "abc", expires_in: 60)
|
105
|
-
meta = Mudis.inspect("key1")
|
106
|
-
|
107
|
-
expect(meta).to include(:key, :bucket, :expires_at, :created_at, :size_bytes, :compressed)
|
108
|
-
expect(meta[:key]).to eq("key1")
|
109
|
-
expect(meta[:compressed]).to eq(false)
|
110
|
-
end
|
111
|
-
|
112
|
-
it "returns nil for missing key" do
|
113
|
-
expect(Mudis.inspect("unknown")).to be_nil
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
describe "
|
118
|
-
it "
|
119
|
-
Mudis.
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
it "
|
127
|
-
Mudis.write("
|
128
|
-
Mudis.write("
|
129
|
-
expect(Mudis.
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
Mudis.write("
|
146
|
-
Mudis.
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
Mudis.
|
158
|
-
Mudis.
|
159
|
-
Mudis.
|
160
|
-
|
161
|
-
Mudis.
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
Mudis.
|
178
|
-
expect(Mudis.
|
179
|
-
expect(Mudis.
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
it "evicts old entries when size limit is reached" do
|
185
|
-
Mudis.stop_expiry_thread
|
186
|
-
|
187
|
-
# Force one bucket
|
188
|
-
Mudis.instance_variable_set(:@buckets, 1)
|
189
|
-
Mudis.instance_variable_set(:@stores, [{}])
|
190
|
-
Mudis.instance_variable_set(:@mutexes, [Mutex.new])
|
191
|
-
Mudis.instance_variable_set(:@lru_heads, [nil])
|
192
|
-
Mudis.instance_variable_set(:@lru_tails, [nil])
|
193
|
-
Mudis.instance_variable_set(:@lru_nodes, [{}])
|
194
|
-
Mudis.instance_variable_set(:@current_bytes, [0])
|
195
|
-
Mudis.hard_memory_limit = false
|
196
|
-
# Set very small threshold
|
197
|
-
Mudis.instance_variable_set(:@threshold_bytes, 60)
|
198
|
-
Mudis.max_value_bytes = 100
|
199
|
-
|
200
|
-
big_val1 = "a" * 50
|
201
|
-
big_val2 = "b" * 50
|
202
|
-
|
203
|
-
Mudis.write("a", big_val1)
|
204
|
-
Mudis.write("b", big_val2)
|
205
|
-
|
206
|
-
expect(Mudis.read("a")).to be_nil
|
207
|
-
expect(Mudis.read("b")).not_to be_nil
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
describe ".all_keys" do
|
212
|
-
it "lists all stored keys" do
|
213
|
-
Mudis.write("k1", 1)
|
214
|
-
Mudis.write("k2", 2)
|
215
|
-
expect(Mudis.all_keys).to include("k1", "k2")
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
it "respects max_bytes when updated externally" do
|
220
|
-
Mudis.max_bytes = 100
|
221
|
-
expect(Mudis.send(:max_bytes)).to eq(100)
|
222
|
-
end
|
223
|
-
|
224
|
-
describe ".current_memory_bytes" do
|
225
|
-
it "returns a non-zero byte count after writes" do
|
226
|
-
Mudis.write("size_test", "a" * 100)
|
227
|
-
expect(Mudis.current_memory_bytes).to be > 0
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
describe ".metrics" do
|
232
|
-
it "includes per-bucket stats" do
|
233
|
-
Mudis.write("a", "x" * 50)
|
234
|
-
metrics = Mudis.metrics
|
235
|
-
|
236
|
-
expect(metrics).to include(:buckets)
|
237
|
-
expect(metrics[:buckets]).to be_an(Array)
|
238
|
-
expect(metrics[:buckets].first).to include(:index, :keys, :memory_bytes, :lru_size)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
describe ".least_touched" do
|
243
|
-
it "returns keys with lowest read access counts" do
|
244
|
-
Mudis.reset!
|
245
|
-
Mudis.write("a", 1)
|
246
|
-
Mudis.write("b", 2)
|
247
|
-
Mudis.write("c", 3)
|
248
|
-
|
249
|
-
Mudis.read("a")
|
250
|
-
Mudis.read("a")
|
251
|
-
Mudis.read("b")
|
252
|
-
|
253
|
-
least = Mudis.least_touched(2)
|
254
|
-
expect(least.map(&:first)).to include("c") # Never read
|
255
|
-
expect(least.first.last).to eq(0)
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
describe ".configure" do
|
260
|
-
it "applies configuration settings correctly" do
|
261
|
-
Mudis.configure do |c|
|
262
|
-
c.serializer = JSON
|
263
|
-
c.compress = true
|
264
|
-
c.max_value_bytes = 12_345
|
265
|
-
c.hard_memory_limit = true
|
266
|
-
c.max_bytes = 987_654
|
267
|
-
end
|
268
|
-
|
269
|
-
expect(Mudis.compress).to eq(true)
|
270
|
-
expect(Mudis.max_value_bytes).to eq(12_345)
|
271
|
-
expect(Mudis.hard_memory_limit).to eq(true)
|
272
|
-
expect(Mudis.max_bytes).to eq(987_654)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
describe ".reset!" do
|
277
|
-
it "clears all stores, memory, and metrics" do
|
278
|
-
Mudis.write("reset_key", "value")
|
279
|
-
expect(Mudis.read("reset_key")).to eq("value")
|
280
|
-
expect(Mudis.current_memory_bytes).to be > 0
|
281
|
-
expect(Mudis.metrics[:hits]).to be >= 0
|
282
|
-
|
283
|
-
Mudis.reset!
|
284
|
-
|
285
|
-
metrics = Mudis.metrics
|
286
|
-
expect(metrics[:hits]).to eq(0)
|
287
|
-
expect(metrics[:misses]).to eq(0)
|
288
|
-
expect(metrics[:evictions]).to eq(0)
|
289
|
-
expect(metrics[:rejected]).to eq(0)
|
290
|
-
expect(Mudis.current_memory_bytes).to eq(0)
|
291
|
-
expect(Mudis.all_keys).to be_empty
|
292
|
-
|
293
|
-
# Optionally confirm reset_key is now gone
|
294
|
-
expect(Mudis.read("reset_key")).to be_nil
|
295
|
-
end
|
296
|
-
end
|
297
|
-
|
298
|
-
describe ".reset_metrics!" do
|
299
|
-
it "resets only the metrics without clearing cache" do
|
300
|
-
Mudis.write("metrics_key", "value")
|
301
|
-
Mudis.read("metrics_key") # generates :hits
|
302
|
-
Mudis.read("missing_key") # generates :misses
|
303
|
-
|
304
|
-
expect(Mudis.metrics[:hits]).to eq(1)
|
305
|
-
expect(Mudis.metrics[:misses]).to eq(1)
|
306
|
-
|
307
|
-
Mudis.reset_metrics!
|
308
|
-
|
309
|
-
expect(Mudis.metrics[:hits]).to eq(0)
|
310
|
-
expect(Mudis.metrics[:misses]).to eq(0)
|
311
|
-
expect(Mudis.read("metrics_key")).to eq("value") # still exists
|
312
|
-
end
|
313
|
-
end
|
314
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe Mudis do # rubocop:disable Metrics/BlockLength
|
6
|
+
before(:each) do
|
7
|
+
Mudis.stop_expiry_thread
|
8
|
+
Mudis.instance_variable_set(:@buckets, nil)
|
9
|
+
Mudis.instance_variable_set(:@stores, Array.new(Mudis.buckets) { {} })
|
10
|
+
Mudis.instance_variable_set(:@mutexes, Array.new(Mudis.buckets) { Mutex.new })
|
11
|
+
Mudis.instance_variable_set(:@lru_heads, Array.new(Mudis.buckets) { nil })
|
12
|
+
Mudis.instance_variable_set(:@lru_tails, Array.new(Mudis.buckets) { nil })
|
13
|
+
Mudis.instance_variable_set(:@lru_nodes, Array.new(Mudis.buckets) { {} })
|
14
|
+
Mudis.instance_variable_set(:@current_bytes, Array.new(Mudis.buckets, 0))
|
15
|
+
Mudis.instance_variable_set(:@metrics, { hits: 0, misses: 0, evictions: 0, rejected: 0 })
|
16
|
+
Mudis.serializer = JSON
|
17
|
+
Mudis.compress = false
|
18
|
+
Mudis.max_value_bytes = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
describe ".write and .read" do
|
22
|
+
it "writes and reads a value" do
|
23
|
+
Mudis.write("foo", { bar: "baz" })
|
24
|
+
result = Mudis.read("foo")
|
25
|
+
expect(result).to eq({ "bar" => "baz" })
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns nil for non-existent keys" do
|
29
|
+
expect(Mudis.read("nope")).to be_nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ".exists?" do
|
34
|
+
it "returns true if key exists" do
|
35
|
+
Mudis.write("check", [1, 2, 3])
|
36
|
+
expect(Mudis.exists?("check")).to be true
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns false if key does not exist" do
|
40
|
+
expect(Mudis.exists?("missing")).to be false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".delete" do
|
45
|
+
it "deletes a key" do
|
46
|
+
Mudis.write("temp", 42)
|
47
|
+
Mudis.delete("temp")
|
48
|
+
expect(Mudis.read("temp")).to be_nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".update" do
|
53
|
+
it "updates a cached value" do
|
54
|
+
Mudis.write("counter", 5)
|
55
|
+
Mudis.update("counter") { |v| v + 1 }
|
56
|
+
expect(Mudis.read("counter")).to eq(6)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ".fetch" do
|
61
|
+
it "returns cached value if exists" do
|
62
|
+
Mudis.write("k", 123)
|
63
|
+
result = Mudis.fetch("k", expires_in: 60) { 999 } # fix: use keyword arg
|
64
|
+
expect(result).to eq(123)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "writes and returns block result if missing" do
|
68
|
+
Mudis.delete("k")
|
69
|
+
result = Mudis.fetch("k", expires_in: 60) { 999 } # fix
|
70
|
+
expect(result).to eq(999)
|
71
|
+
expect(Mudis.read("k")).to eq(999)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "forces overwrite if force: true" do
|
75
|
+
Mudis.write("k", 100)
|
76
|
+
result = Mudis.fetch("k", force: true) { 200 } # fix
|
77
|
+
expect(result).to eq(200)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ".clear" do
|
82
|
+
it "removes a key from the cache" do
|
83
|
+
Mudis.write("to_clear", 123)
|
84
|
+
expect(Mudis.read("to_clear")).to eq(123)
|
85
|
+
Mudis.clear("to_clear")
|
86
|
+
expect(Mudis.read("to_clear")).to be_nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe ".replace" do
|
91
|
+
it "replaces value only if key exists" do
|
92
|
+
Mudis.write("to_replace", 100)
|
93
|
+
Mudis.replace("to_replace", 200)
|
94
|
+
expect(Mudis.read("to_replace")).to eq(200)
|
95
|
+
|
96
|
+
Mudis.delete("to_replace")
|
97
|
+
Mudis.replace("to_replace", 300)
|
98
|
+
expect(Mudis.read("to_replace")).to be_nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe ".inspect" do
|
103
|
+
it "returns metadata for a cached key" do
|
104
|
+
Mudis.write("key1", "abc", expires_in: 60)
|
105
|
+
meta = Mudis.inspect("key1")
|
106
|
+
|
107
|
+
expect(meta).to include(:key, :bucket, :expires_at, :created_at, :size_bytes, :compressed)
|
108
|
+
expect(meta[:key]).to eq("key1")
|
109
|
+
expect(meta[:compressed]).to eq(false)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "returns nil for missing key" do
|
113
|
+
expect(Mudis.inspect("unknown")).to be_nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "expiry handling" do
|
118
|
+
it "expires values after specified time" do
|
119
|
+
Mudis.write("short_lived", "gone soon", expires_in: 1)
|
120
|
+
sleep 2
|
121
|
+
expect(Mudis.read("short_lived")).to be_nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe ".all_keys" do
|
126
|
+
it "lists all stored keys" do
|
127
|
+
Mudis.write("k1", 1)
|
128
|
+
Mudis.write("k2", 2)
|
129
|
+
expect(Mudis.all_keys).to include("k1", "k2")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "respects max_bytes when updated externally" do
|
134
|
+
Mudis.max_bytes = 100
|
135
|
+
expect(Mudis.send(:max_bytes)).to eq(100)
|
136
|
+
end
|
137
|
+
|
138
|
+
describe ".current_memory_bytes" do
|
139
|
+
it "returns a non-zero byte count after writes" do
|
140
|
+
Mudis.configure do |c|
|
141
|
+
c.max_value_bytes = nil
|
142
|
+
c.hard_memory_limit = false
|
143
|
+
end
|
144
|
+
|
145
|
+
Mudis.write("size_test", "a" * 100)
|
146
|
+
expect(Mudis.current_memory_bytes).to be > 0
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe ".least_touched" do
|
151
|
+
it "returns keys with lowest read access counts" do
|
152
|
+
Mudis.reset!
|
153
|
+
Mudis.write("a", 1)
|
154
|
+
Mudis.write("b", 2)
|
155
|
+
Mudis.write("c", 3)
|
156
|
+
|
157
|
+
Mudis.read("a")
|
158
|
+
Mudis.read("a")
|
159
|
+
Mudis.read("b")
|
160
|
+
|
161
|
+
least = Mudis.least_touched(2)
|
162
|
+
expect(least.map(&:first)).to include("c") # Never read
|
163
|
+
expect(least.first.last).to eq(0)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe ".configure" do
|
168
|
+
it "applies configuration settings correctly" do
|
169
|
+
Mudis.configure do |c|
|
170
|
+
c.serializer = JSON
|
171
|
+
c.compress = true
|
172
|
+
c.max_value_bytes = 12_345
|
173
|
+
c.hard_memory_limit = true
|
174
|
+
c.max_bytes = 987_654
|
175
|
+
end
|
176
|
+
|
177
|
+
expect(Mudis.compress).to eq(true)
|
178
|
+
expect(Mudis.max_value_bytes).to eq(12_345)
|
179
|
+
expect(Mudis.hard_memory_limit).to eq(true)
|
180
|
+
expect(Mudis.max_bytes).to eq(987_654)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Mudis Namespace Operations" do # rubocop:disable Metrics/BlockLength
|
6
|
+
before(:each) do
|
7
|
+
Mudis.reset!
|
8
|
+
end
|
9
|
+
|
10
|
+
it "uses thread-local namespace in block" do
|
11
|
+
Mudis.with_namespace("test") do
|
12
|
+
Mudis.write("foo", "bar")
|
13
|
+
end
|
14
|
+
expect(Mudis.read("foo", namespace: "test")).to eq("bar")
|
15
|
+
expect(Mudis.read("foo")).to be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "supports explicit namespace override" do
|
19
|
+
Mudis.write("x", 1, namespace: "alpha")
|
20
|
+
Mudis.write("x", 2, namespace: "beta")
|
21
|
+
expect(Mudis.read("x", namespace: "alpha")).to eq(1)
|
22
|
+
expect(Mudis.read("x", namespace: "beta")).to eq(2)
|
23
|
+
expect(Mudis.read("x")).to be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".keys" do
|
27
|
+
it "returns only keys for the given namespace" do
|
28
|
+
Mudis.write("user:1", "Alice", namespace: "users")
|
29
|
+
Mudis.write("user:2", "Bob", namespace: "users")
|
30
|
+
Mudis.write("admin:1", "Charlie", namespace: "admins")
|
31
|
+
|
32
|
+
result = Mudis.keys(namespace: "users")
|
33
|
+
expect(result).to contain_exactly("user:1", "user:2")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns an empty array if no keys exist for namespace" do
|
37
|
+
expect(Mudis.keys(namespace: "nonexistent")).to eq([])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises an error if namespace is missing" do
|
41
|
+
expect { Mudis.keys(namespace: nil) }.to raise_error(ArgumentError, /namespace is required/)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ".clear_namespace" do
|
46
|
+
it "deletes all keys in the given namespace" do
|
47
|
+
Mudis.write("a", 1, namespace: "ns1")
|
48
|
+
Mudis.write("b", 2, namespace: "ns1")
|
49
|
+
Mudis.write("x", 9, namespace: "ns2")
|
50
|
+
|
51
|
+
expect(Mudis.read("a", namespace: "ns1")).to eq(1)
|
52
|
+
expect(Mudis.read("b", namespace: "ns1")).to eq(2)
|
53
|
+
|
54
|
+
Mudis.clear_namespace(namespace: "ns1")
|
55
|
+
|
56
|
+
expect(Mudis.read("a", namespace: "ns1")).to be_nil
|
57
|
+
expect(Mudis.read("b", namespace: "ns1")).to be_nil
|
58
|
+
expect(Mudis.read("x", namespace: "ns2")).to eq(9)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "does nothing if namespace has no keys" do
|
62
|
+
expect { Mudis.clear_namespace(namespace: "ghost") }.not_to raise_error
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raises an error if namespace is nil" do
|
66
|
+
expect { Mudis.clear_namespace(namespace: nil) }.to raise_error(ArgumentError, /namespace is required/)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/reset_spec.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Mudis Reset Features" do
|
6
|
+
before { Mudis.reset! }
|
7
|
+
|
8
|
+
describe ".reset!" do
|
9
|
+
it "clears all stores, memory, and metrics" do
|
10
|
+
Mudis.write("reset_key", "value")
|
11
|
+
expect(Mudis.read("reset_key")).to eq("value")
|
12
|
+
Mudis.reset!
|
13
|
+
expect(Mudis.metrics[:hits]).to eq(0)
|
14
|
+
expect(Mudis.all_keys).to be_empty
|
15
|
+
expect(Mudis.read("reset_key")).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe ".reset_metrics!" do
|
20
|
+
it "resets only the metrics without clearing cache" do
|
21
|
+
Mudis.write("metrics_key", "value")
|
22
|
+
Mudis.read("metrics_key")
|
23
|
+
Mudis.read("missing_key")
|
24
|
+
expect(Mudis.metrics[:hits]).to eq(1)
|
25
|
+
expect(Mudis.metrics[:misses]).to eq(1)
|
26
|
+
Mudis.reset_metrics!
|
27
|
+
expect(Mudis.metrics[:hits]).to eq(0)
|
28
|
+
expect(Mudis.read("metrics_key")).to eq("value")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|