mudis-ql 0.1.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.
@@ -0,0 +1,330 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "MudisQL Error Handling and Edge Cases" do
4
+ before do
5
+ Mudis.serializer = JSON
6
+ end
7
+
8
+ describe "invalid namespace scenarios" do
9
+ it "handles empty namespace gracefully" do
10
+ store = MudisQL::Store.new("")
11
+ expect { store.all }.not_to raise_error
12
+ expect(store.all).to eq([])
13
+ end
14
+
15
+ it "handles non-existent namespace" do
16
+ results = MudisQL.from("nonexistent_namespace_12345").all
17
+ expect(results).to eq([])
18
+ end
19
+
20
+ it "handles querying for non-existent key in existing namespace" do
21
+ namespace = "existing_namespace_test"
22
+ Mudis.write("key1", { name: "Alice" }, namespace: namespace)
23
+ Mudis.write("key2", { name: "Bob" }, namespace: namespace)
24
+
25
+ # Query for a key that doesn't exist using _key field
26
+ results = MudisQL.from(namespace).where(_key: "nonexistent_key").all
27
+ expect(results).to eq([])
28
+
29
+ # Verify the namespace itself has data
30
+ all_results = MudisQL.from(namespace).all
31
+ expect(all_results.size).to eq(2)
32
+ end
33
+ end
34
+
35
+ describe "malformed data scenarios" do
36
+ let(:namespace) { "malformed_test" }
37
+
38
+ it "handles non-hash values in cache" do
39
+ Mudis.write("string", "just a string", namespace: namespace)
40
+ Mudis.write("number", 42, namespace: namespace)
41
+ Mudis.write("array", [1, 2, 3], namespace: namespace)
42
+
43
+ results = MudisQL.from(namespace).all
44
+
45
+ expect(results.size).to eq(3)
46
+ expect(results.find { |r| r["_key"] == "string" }["value"]).to eq("just a string")
47
+ expect(results.find { |r| r["_key"] == "number" }["value"]).to eq(42)
48
+ end
49
+
50
+ it "handles mixed hash and non-hash values" do
51
+ Mudis.write("obj1", { name: "Object" }, namespace: namespace)
52
+ Mudis.write("str1", "String value", namespace: namespace)
53
+
54
+ results = MudisQL.from(namespace).all
55
+
56
+ expect(results.size).to eq(2)
57
+ obj = results.find { |r| r["_key"] == "obj1" }
58
+ str = results.find { |r| r["_key"] == "str1" }
59
+
60
+ expect(obj).to have_key("name")
61
+ expect(str).to have_key("value")
62
+ end
63
+ end
64
+
65
+ describe "query condition edge cases" do
66
+ let(:namespace) { "edge_query" }
67
+
68
+ before do
69
+ Mudis.write("1", { value: 10, name: "Ten" }, namespace: namespace)
70
+ Mudis.write("2", { value: 20, name: "Twenty" }, namespace: namespace)
71
+ end
72
+
73
+ it "handles condition on non-existent field" do
74
+ results = MudisQL.from(namespace)
75
+ .where(nonexistent_field: "value")
76
+ .all
77
+
78
+ expect(results).to be_empty
79
+ end
80
+
81
+ it "handles proc that raises exception gracefully" do
82
+ # Proc that would error on nil
83
+ expect {
84
+ MudisQL.from(namespace)
85
+ .where(value: ->(v) { v.to_s.upcase })
86
+ .all
87
+ }.not_to raise_error
88
+ end
89
+
90
+ it "handles regex on non-string fields" do
91
+ results = MudisQL.from(namespace)
92
+ .where(value: /10/)
93
+ .all
94
+
95
+ # Should convert to string and match
96
+ expect(results.size).to eq(1)
97
+ end
98
+
99
+ it "handles range comparisons with incompatible types" do
100
+ Mudis.write("3", { value: "string" }, namespace: namespace)
101
+
102
+ # Should not crash when range comparison fails
103
+ expect {
104
+ MudisQL.from(namespace)
105
+ .where(value: 15..25)
106
+ .all
107
+ }.not_to raise_error
108
+ end
109
+ end
110
+
111
+ describe "ordering edge cases" do
112
+ let(:namespace) { "order_edge" }
113
+
114
+ before do
115
+ Mudis.write("1", { name: "Alpha", score: 10 }, namespace: namespace)
116
+ Mudis.write("2", { name: "Beta", score: nil }, namespace: namespace)
117
+ Mudis.write("3", { name: "Gamma", score: 5 }, namespace: namespace)
118
+ Mudis.write("4", { name: nil, score: 15 }, namespace: namespace)
119
+ end
120
+
121
+ it "orders with nil values ascending" do
122
+ results = MudisQL.from(namespace)
123
+ .order(:score, :asc)
124
+ .pluck(:name, :score)
125
+
126
+ # Nil should be at the end
127
+ expect(results.last[1]).to be_nil
128
+ end
129
+
130
+ it "orders with nil values descending" do
131
+ results = MudisQL.from(namespace)
132
+ .order(:score, :desc)
133
+ .pluck(:name, :score)
134
+
135
+ # Nil should be at the end (in desc, that's still last)
136
+ expect(results.last[1]).to be_nil
137
+ end
138
+
139
+ it "orders by non-existent field" do
140
+ results = MudisQL.from(namespace)
141
+ .order(:nonexistent)
142
+ .all
143
+
144
+ # Should not crash, order may be arbitrary
145
+ expect(results.size).to eq(4)
146
+ end
147
+
148
+ it "orders strings and numbers in same field" do
149
+ Mudis.write("5", { mixed: "zebra" }, namespace: namespace)
150
+ Mudis.write("6", { mixed: 100 }, namespace: namespace)
151
+
152
+ expect {
153
+ MudisQL.from(namespace)
154
+ .order(:mixed)
155
+ .all
156
+ }.not_to raise_error
157
+ end
158
+ end
159
+
160
+ describe "pagination edge cases" do
161
+ let(:namespace) { "pagination_edge" }
162
+
163
+ before do
164
+ 5.times { |i| Mudis.write("item#{i}", { value: i }, namespace: namespace) }
165
+ end
166
+
167
+ it "handles offset larger than result set" do
168
+ results = MudisQL.from(namespace)
169
+ .offset(100)
170
+ .all
171
+
172
+ expect(results).to be_empty
173
+ end
174
+
175
+ it "handles limit of 0" do
176
+ results = MudisQL.from(namespace)
177
+ .limit(0)
178
+ .all
179
+
180
+ expect(results).to be_empty
181
+ end
182
+
183
+ it "handles negative offset (should treat as 0)" do
184
+ results = MudisQL.from(namespace)
185
+ .offset(-5)
186
+ .all
187
+
188
+ # Should return all items
189
+ expect(results.size).to eq(5)
190
+ end
191
+
192
+ it "handles very large limit" do
193
+ results = MudisQL.from(namespace)
194
+ .limit(1_000_000)
195
+ .all
196
+
197
+ # Should return all available items
198
+ expect(results.size).to eq(5)
199
+ end
200
+ end
201
+
202
+ describe "pluck edge cases" do
203
+ let(:namespace) { "pluck_edge" }
204
+
205
+ before do
206
+ Mudis.write("1", { a: 1, b: 2, c: 3 }, namespace: namespace)
207
+ Mudis.write("2", { a: 4, b: nil, c: 6 }, namespace: namespace)
208
+ end
209
+
210
+ it "plucks non-existent fields" do
211
+ results = MudisQL.from(namespace).pluck(:nonexistent)
212
+
213
+ expect(results).to eq([nil, nil])
214
+ end
215
+
216
+ it "plucks with mixed existent and non-existent fields" do
217
+ results = MudisQL.from(namespace).pluck(:a, :nonexistent)
218
+
219
+ expect(results).to contain_exactly([1, nil], [4, nil])
220
+ end
221
+
222
+ it "plucks no fields" do
223
+ results = MudisQL.from(namespace).pluck
224
+
225
+ expect(results.size).to eq(2)
226
+ expect(results.first).to eq([])
227
+ end
228
+
229
+ it "plucks with nil values" do
230
+ results = MudisQL.from(namespace).pluck(:b)
231
+
232
+ expect(results).to contain_exactly(2, nil)
233
+ end
234
+ end
235
+
236
+ describe "chaining and scope state" do
237
+ let(:namespace) { "chain_test" }
238
+
239
+ before do
240
+ 3.times { |i| Mudis.write("i#{i}", { value: i * 10 }, namespace: namespace) }
241
+ end
242
+
243
+ it "maintains independent scope chains" do
244
+ # Create fresh scopes for each test
245
+ scope1 = MudisQL.from(namespace).where(value: 0)
246
+ count1 = scope1.count
247
+
248
+ scope2 = MudisQL.from(namespace).where(value: 10)
249
+ count2 = scope2.count
250
+
251
+ expect(count1).to eq(1)
252
+ expect(count2).to eq(1)
253
+ # Verify scope1 is still independent
254
+ expect(scope1.count).to eq(1)
255
+ end
256
+
257
+ it "allows multiple calls to same method" do
258
+ results = MudisQL.from(namespace)
259
+ .where(value: ->(v) { v >= 0 })
260
+ .where(value: ->(v) { v <= 20 })
261
+ .all
262
+
263
+ expect(results.size).to eq(3)
264
+ end
265
+
266
+ it "order can be called multiple times (last wins)" do
267
+ results = MudisQL.from(namespace)
268
+ .order(:value, :asc)
269
+ .order(:value, :desc)
270
+ .pluck(:value)
271
+
272
+ expect(results.first).to be > results.last
273
+ end
274
+ end
275
+
276
+ describe "concurrent access scenarios" do
277
+ let(:namespace) { "concurrent" }
278
+
279
+ it "handles modifications during query execution" do
280
+ 10.times { |i| Mudis.write("i#{i}", { value: i }, namespace: namespace) }
281
+
282
+ # Start a query
283
+ query = MudisQL.from(namespace).where(value: ->(v) { v < 5 })
284
+
285
+ # Modify data before executing
286
+ Mudis.write("i11", { value: 2 }, namespace: namespace)
287
+
288
+ results = query.all
289
+
290
+ # Should still execute without error
291
+ expect(results).to be_an(Array)
292
+ end
293
+ end
294
+
295
+ describe "type coercion and comparison" do
296
+ let(:namespace) { "type_test" }
297
+
298
+ before do
299
+ Mudis.write("1", { value: "100", type: "string" }, namespace: namespace)
300
+ Mudis.write("2", { value: 100, type: "integer" }, namespace: namespace)
301
+ Mudis.write("3", { value: 100.0, type: "float" }, namespace: namespace)
302
+ Mudis.write("4", { value: true, type: "boolean" }, namespace: namespace)
303
+ end
304
+
305
+ it "handles exact matches with different types" do
306
+ string_results = MudisQL.from(namespace).where(value: "100").count
307
+ # Note: 100.0 and 100 are equal in Ruby and JSON doesn't distinguish
308
+ int_results = MudisQL.from(namespace).where(value: 100).count
309
+
310
+ expect(string_results).to eq(1)
311
+ expect(int_results).to eq(2) # Matches both integer and float
312
+ end
313
+
314
+ it "handles boolean values" do
315
+ results = MudisQL.from(namespace).where(value: true).all
316
+
317
+ expect(results.size).to eq(1)
318
+ expect(results.first["type"]).to eq("boolean")
319
+ end
320
+
321
+ it "handles proc comparisons with type flexibility" do
322
+ results = MudisQL.from(namespace)
323
+ .where(value: ->(v) { v.to_s == "100" })
324
+ .all
325
+
326
+ expect(results.size).to eq(2) # String "100" and integer 100 (float becomes int in JSON)
327
+ end
328
+ end
329
+ end
330
+
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe "MudisQL Integration Tests" do
4
+ let(:namespace) { "test_integration" }
5
+
6
+ before do
7
+ Mudis.serializer = JSON
8
+ end
9
+
10
+ describe "E-commerce product catalog scenario" do
11
+ before do
12
+ # Seed a realistic product catalog
13
+ products = [
14
+ { id: "p001", name: "MacBook Pro 16", price: 2499, category: "laptops", brand: "Apple", rating: 4.8, stock: 15, tags: ["premium", "professional"] },
15
+ { id: "p002", name: "Dell XPS 15", price: 1899, category: "laptops", brand: "Dell", rating: 4.6, stock: 25, tags: ["business", "professional"] },
16
+ { id: "p003", name: "ThinkPad X1", price: 1599, category: "laptops", brand: "Lenovo", rating: 4.7, stock: 30, tags: ["business", "durable"] },
17
+ { id: "p004", name: "Magic Mouse", price: 79, category: "accessories", brand: "Apple", rating: 4.2, stock: 100, tags: ["wireless"] },
18
+ { id: "p005", name: "Logitech MX Master", price: 99, category: "accessories", brand: "Logitech", rating: 4.9, stock: 75, tags: ["wireless", "ergonomic"] },
19
+ { id: "p006", name: "iPad Pro 12.9", price: 1099, category: "tablets", brand: "Apple", rating: 4.8, stock: 40, tags: ["premium", "creative"] },
20
+ { id: "p007", name: "Surface Pro 9", price: 999, category: "tablets", brand: "Microsoft", rating: 4.5, stock: 35, tags: ["business", "versatile"] },
21
+ { id: "p008", name: "AirPods Pro", price: 249, category: "accessories", brand: "Apple", rating: 4.7, stock: 200, tags: ["wireless", "premium"] },
22
+ { id: "p009", name: "Samsung Galaxy Tab", price: 449, category: "tablets", brand: "Samsung", rating: 4.4, stock: 50, tags: ["android", "affordable"] },
23
+ { id: "p010", name: "Mechanical Keyboard", price: 159, category: "accessories", brand: "Keychron", rating: 4.6, stock: 45, tags: ["mechanical", "rgb"] }
24
+ ]
25
+
26
+ products.each do |product|
27
+ Mudis.write(product[:id], product, namespace: namespace, expires_in: 3600)
28
+ end
29
+ end
30
+
31
+ it "finds premium Apple products over $1000" do
32
+ results = MudisQL.from(namespace)
33
+ .where(brand: "Apple")
34
+ .where(price: ->(p) { p > 1000 })
35
+ .order(:price, :desc)
36
+ .all
37
+
38
+ expect(results.size).to eq(2)
39
+ expect(results[0]["name"]).to eq("MacBook Pro 16")
40
+ expect(results[1]["name"]).to eq("iPad Pro 12.9")
41
+ end
42
+
43
+ it "finds highly rated products (>4.5) with good stock" do
44
+ results = MudisQL.from(namespace)
45
+ .where(rating: ->(r) { r > 4.5 })
46
+ .where(stock: ->(s) { s >= 30 })
47
+ .order(:rating, :desc)
48
+ .pluck(:name, :rating, :stock)
49
+
50
+ expect(results.size).to eq(5)
51
+ expect(results.first).to eq(["Logitech MX Master", 4.9, 75])
52
+ end
53
+
54
+ it "paginates through laptop results" do
55
+ page1 = MudisQL.from(namespace)
56
+ .where(category: "laptops")
57
+ .order(:price)
58
+ .limit(2)
59
+ .offset(0)
60
+ .all
61
+
62
+ page2 = MudisQL.from(namespace)
63
+ .where(category: "laptops")
64
+ .order(:price)
65
+ .limit(2)
66
+ .offset(2)
67
+ .all
68
+
69
+ expect(page1.map { |p| p["name"] }).to eq(["ThinkPad X1", "Dell XPS 15"])
70
+ expect(page2.map { |p| p["name"] }).to eq(["MacBook Pro 16"])
71
+ end
72
+
73
+ it "finds products in price range with specific brand" do
74
+ results = MudisQL.from(namespace)
75
+ .where(price: 100..1000)
76
+ .where(brand: /^(Apple|Microsoft)$/i)
77
+ .order(:price)
78
+ .all
79
+
80
+ expect(results.map { |p| p["name"] }).to eq(["AirPods Pro", "Surface Pro 9"])
81
+ end
82
+
83
+ it "gets product count by category" do
84
+ laptop_count = MudisQL.from(namespace).where(category: "laptops").count
85
+ accessory_count = MudisQL.from(namespace).where(category: "accessories").count
86
+ tablet_count = MudisQL.from(namespace).where(category: "tablets").count
87
+
88
+ expect(laptop_count).to eq(3)
89
+ expect(accessory_count).to eq(4)
90
+ expect(tablet_count).to eq(3)
91
+ end
92
+ end
93
+
94
+ describe "User analytics scenario" do
95
+ before do
96
+ # Seed user activity data
97
+ users = [
98
+ { id: "u001", name: "Alice", email: "alice@example.com", status: "active", last_login_days: 2, orders: 15, lifetime_value: 2500 },
99
+ { id: "u002", name: "Bob", email: "bob@example.com", status: "active", last_login_days: 1, orders: 8, lifetime_value: 800 },
100
+ { id: "u003", name: "Charlie", email: "charlie@example.com", status: "inactive", last_login_days: 45, orders: 3, lifetime_value: 150 },
101
+ { id: "u004", name: "Diana", email: "diana@example.com", status: "active", last_login_days: 5, orders: 25, lifetime_value: 4200 },
102
+ { id: "u005", name: "Eve", email: "eve@example.com", status: "suspended", last_login_days: 120, orders: 1, lifetime_value: 50 },
103
+ { id: "u006", name: "Frank", email: "frank@example.com", status: "active", last_login_days: 3, orders: 12, lifetime_value: 1800 },
104
+ { id: "u007", name: "Grace", email: "grace@example.com", status: "inactive", last_login_days: 60, orders: 0, lifetime_value: 0 }
105
+ ]
106
+
107
+ users.each do |user|
108
+ Mudis.write(user[:id], user, namespace: "users", expires_in: 1800)
109
+ end
110
+ end
111
+
112
+ it "identifies VIP customers (high value, active)" do
113
+ vips = MudisQL.from("users")
114
+ .where(status: "active")
115
+ .where(lifetime_value: ->(v) { v >= 2000 })
116
+ .order(:lifetime_value, :desc)
117
+ .all
118
+
119
+ expect(vips.size).to eq(2)
120
+ expect(vips.map { |u| u["name"] }).to eq(["Diana", "Alice"])
121
+ end
122
+
123
+ it "finds at-risk users (active but haven't logged in recently)" do
124
+ at_risk = MudisQL.from("users")
125
+ .where(status: "active")
126
+ .where(last_login_days: ->(d) { d > 4 })
127
+ .pluck(:name, :last_login_days, :orders)
128
+
129
+ expect(at_risk.size).to eq(1)
130
+ expect(at_risk.first[0]).to eq("Diana")
131
+ end
132
+
133
+ it "segments users by engagement level" do
134
+ high_engagement = MudisQL.from("users")
135
+ .where(orders: ->(o) { o >= 10 })
136
+ .where(status: "active")
137
+ .count
138
+
139
+ low_engagement = MudisQL.from("users")
140
+ .where(orders: ->(o) { o < 5 })
141
+ .where(status: "active")
142
+ .count
143
+
144
+ expect(high_engagement).to eq(3)
145
+ expect(low_engagement).to eq(0) # Bob has 8 orders, so no active users with < 5 orders
146
+ end
147
+
148
+ it "finds reactivation candidates" do
149
+ candidates = MudisQL.from("users")
150
+ .where(status: "inactive")
151
+ .where(orders: ->(o) { o > 0 })
152
+ .where(lifetime_value: ->(v) { v > 100 })
153
+ .order(:last_login_days)
154
+ .all
155
+
156
+ expect(candidates.size).to eq(1)
157
+ expect(candidates.first["name"]).to eq("Charlie")
158
+ end
159
+ end
160
+
161
+ describe "Complex query combinations" do
162
+ before do
163
+ # Mixed data types
164
+ data = [
165
+ { id: "1", name: "Alpha", score: 95, tags: ["premium"], created_at: "2025-01-01" },
166
+ { id: "2", name: "Beta", score: 82, tags: ["standard"], created_at: "2025-01-15" },
167
+ { id: "3", name: "Gamma", score: 78, tags: ["premium", "featured"], created_at: "2025-02-01" },
168
+ { id: "4", name: "Delta", score: 91, tags: ["standard"], created_at: "2025-01-20" },
169
+ { id: "5", name: "Epsilon", score: 88, tags: ["featured"], created_at: "2025-02-10" }
170
+ ]
171
+
172
+ data.each { |d| Mudis.write(d[:id], d, namespace: "items") }
173
+ end
174
+
175
+ it "handles multiple regex patterns" do
176
+ results = MudisQL.from("items")
177
+ .where(name: /^[AE]/)
178
+ .where(score: ->(s) { s > 85 })
179
+ .all
180
+
181
+ expect(results.map { |r| r["name"] }).to contain_exactly("Alpha", "Epsilon")
182
+ end
183
+
184
+ it "combines ranges with other conditions" do
185
+ results = MudisQL.from("items")
186
+ .where(score: 80..90)
187
+ .where(name: ->(n) { n.length > 5 }) # Changed to > 5 to exclude "Delta"
188
+ .order(:score, :desc)
189
+ .all
190
+
191
+ expect(results.map { |r| r["name"] }).to eq(["Epsilon"])
192
+ end
193
+
194
+ it "handles empty result sets gracefully" do
195
+ results = MudisQL.from("items")
196
+ .where(score: ->(s) { s > 100 })
197
+ .all
198
+
199
+ expect(results).to be_empty
200
+ expect(MudisQL.from("items").where(score: 999).exists?).to be false
201
+ expect(MudisQL.from("items").where(score: 999).count).to eq(0)
202
+ expect(MudisQL.from("items").where(score: 999).first).to be_nil
203
+ end
204
+
205
+ it "plucks multiple fields with complex queries" do
206
+ results = MudisQL.from("items")
207
+ .where(score: ->(s) { s >= 85 })
208
+ .order(:score, :desc)
209
+ .pluck(:name, :score, :tags)
210
+
211
+ expect(results.size).to eq(3)
212
+ expect(results.first[0]).to eq("Alpha")
213
+ expect(results.first[1]).to eq(95)
214
+ end
215
+ end
216
+
217
+ describe "Edge cases and nil handling" do
218
+ before do
219
+ data = [
220
+ { id: "1", name: "Complete", value: 100, status: "active", metadata: { key: "val" } },
221
+ { id: "2", name: nil, value: 50, status: "pending", metadata: nil },
222
+ { id: "3", name: "Partial", value: nil, status: "active", metadata: { key: "val" } },
223
+ { id: "4", name: "", value: 0, status: nil, metadata: {} }
224
+ ]
225
+
226
+ data.each { |d| Mudis.write(d[:id], d, namespace: "edge_cases") }
227
+ end
228
+
229
+ it "handles nil values in comparisons" do
230
+ results = MudisQL.from("edge_cases")
231
+ .where(status: "active")
232
+ .all
233
+
234
+ expect(results.size).to eq(2)
235
+ end
236
+
237
+ it "handles nil values in ordering" do
238
+ results = MudisQL.from("edge_cases")
239
+ .order(:value)
240
+ .pluck(:id, :value)
241
+
242
+ # Nil values should be pushed to end
243
+ expect(results.last[1]).to be_nil
244
+ end
245
+
246
+ it "handles proc conditions with nil values" do
247
+ results = MudisQL.from("edge_cases")
248
+ .where(value: ->(v) { !v.nil? && v > 0 })
249
+ .all
250
+
251
+ expect(results.size).to eq(2)
252
+ expect(results.map { |r| r["id"] }).to contain_exactly("1", "2")
253
+ end
254
+
255
+ it "handles empty strings" do
256
+ results = MudisQL.from("edge_cases")
257
+ .where(name: "")
258
+ .all
259
+
260
+ expect(results.size).to eq(1)
261
+ expect(results.first["id"]).to eq("4")
262
+ end
263
+ end
264
+
265
+ describe "Performance and scale tests" do
266
+ it "handles larger datasets efficiently" do
267
+ # Create 100 records
268
+ 100.times do |i|
269
+ Mudis.write(
270
+ "item_#{i}",
271
+ { id: i, value: rand(1..1000), category: ["A", "B", "C"].sample },
272
+ namespace: "large_set"
273
+ )
274
+ end
275
+
276
+ # Complex query
277
+ results = MudisQL.from("large_set")
278
+ .where(category: "A")
279
+ .where(value: ->(v) { v > 500 })
280
+ .order(:value, :desc)
281
+ .limit(10)
282
+ .all
283
+
284
+ expect(results.size).to be <= 10
285
+ expect(results).to all(have_key("category"))
286
+ end
287
+
288
+ it "handles rapid consecutive queries" do
289
+ Mudis.write("test1", { value: 1 }, namespace: "rapid")
290
+ Mudis.write("test2", { value: 2 }, namespace: "rapid")
291
+
292
+ 10.times do
293
+ result = MudisQL.from("rapid").where(value: 1).first
294
+ expect(result).not_to be_nil
295
+ end
296
+ end
297
+ end
298
+
299
+ describe "Scope reusability" do
300
+ before do
301
+ 5.times do |i|
302
+ Mudis.write("p#{i}", { price: (i + 1) * 100, active: i.even? }, namespace: "products")
303
+ end
304
+ end
305
+
306
+ it "allows building reusable scope objects" do
307
+ # Each scope should be independent
308
+ cheap_active = MudisQL.from("products")
309
+ .where(active: true)
310
+ .where(price: ->(p) { p < 300 })
311
+ .all
312
+
313
+ expensive_active = MudisQL.from("products")
314
+ .where(active: true)
315
+ .where(price: ->(p) { p >= 300 })
316
+ .all
317
+
318
+ # With 5 products at 100, 200, 300, 400, 500
319
+ # Active are at indices 0, 2, 4 = 100, 300, 500
320
+ expect(cheap_active.size).to eq(1) # 100
321
+ expect(expensive_active.size).to eq(2) # 300, 500
322
+ end
323
+
324
+ it "maintains query independence" do
325
+ scope1 = MudisQL.from("products")
326
+ scope2 = MudisQL.from("products")
327
+
328
+ scope1.where(active: true)
329
+ scope2.where(active: false)
330
+
331
+ # Both queries should work independently
332
+ expect(scope1.count).to eq(3)
333
+ expect(scope2.count).to eq(2)
334
+ end
335
+ end
336
+ end
337
+