familia 2.0.0.pre23 → 2.0.0.pre25

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,431 @@
1
+ # try/unit/data_types/json_stringkey_try.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Comprehensive tests for Familia::JsonStringKey
6
+ # A StringKey variant that uses JSON serialization for type preservation.
7
+ # Unlike StringKey, does NOT support INCR/DECR/APPEND operations.
8
+
9
+ require_relative '../../support/helpers/test_helpers'
10
+
11
+ Familia.debug = false
12
+
13
+ # ========================================
14
+ # Type Preservation Tests (Critical)
15
+ # ========================================
16
+
17
+ ## JsonStringKey stores and retrieves Integer values with type preservation
18
+ @int_key = Familia::JsonStringKey.new 'test:json_string:integer'
19
+ @int_key.value = 42
20
+ @int_key.value
21
+ #=> 42
22
+
23
+ ## JsonStringKey Integer has correct class
24
+ @int_key.value.class
25
+ #=> Integer
26
+
27
+ ## JsonStringKey stores and retrieves zero (falsy but valid)
28
+ @zero_key = Familia::JsonStringKey.new 'test:json_string:zero'
29
+ @zero_key.value = 0
30
+ @zero_key.value
31
+ #=> 0
32
+
33
+ ## JsonStringKey zero is Integer not nil
34
+ @zero_key.value.class
35
+ #=> Integer
36
+
37
+ ## JsonStringKey stores and retrieves negative integers
38
+ @neg_key = Familia::JsonStringKey.new 'test:json_string:negative'
39
+ @neg_key.value = -99
40
+ @neg_key.value
41
+ #=> -99
42
+
43
+ ## JsonStringKey stores and retrieves Float values (timestamps, currency, etc.)
44
+ @float_key = Familia::JsonStringKey.new 'test:json_string:float'
45
+ @float_key.value = 1704067200.123
46
+ @float_key.value
47
+ #=> 1704067200.123
48
+
49
+ ## JsonStringKey Float has correct class
50
+ @float_key.value.class
51
+ #=> Float
52
+
53
+ ## JsonStringKey stores and retrieves Float precision
54
+ @precision_key = Familia::JsonStringKey.new 'test:json_string:precision'
55
+ @precision_key.value = 3.14159265358979
56
+ @precision_key.value
57
+ #=> 3.14159265358979
58
+
59
+ ## JsonStringKey stores and retrieves boolean true with type preservation
60
+ @true_key = Familia::JsonStringKey.new 'test:json_string:bool_true'
61
+ @true_key.value = true
62
+ @true_key.value
63
+ #=> true
64
+
65
+ ## JsonStringKey boolean true has correct class
66
+ @true_key.value.class
67
+ #=> TrueClass
68
+
69
+ ## JsonStringKey stores and retrieves boolean false with type preservation
70
+ @false_key = Familia::JsonStringKey.new 'test:json_string:bool_false'
71
+ @false_key.value = false
72
+ @false_key.value
73
+ #=> false
74
+
75
+ ## JsonStringKey boolean false has correct class (not nil, not string)
76
+ @false_key.value.class
77
+ #=> FalseClass
78
+
79
+ ## JsonStringKey stores and retrieves nil values
80
+ @nil_key = Familia::JsonStringKey.new 'test:json_string:nil_val'
81
+ @nil_key.value = nil
82
+ # nil is stored as JSON "null" and deserialized back to Ruby nil
83
+ @nil_key.value
84
+ #=> nil
85
+
86
+ ## JsonStringKey nil has correct class
87
+ @nil_key.value.class
88
+ #=> NilClass
89
+
90
+ ## JsonStringKey stores and retrieves String values
91
+ @str_key = Familia::JsonStringKey.new 'test:json_string:string'
92
+ @str_key.value = 'hello world'
93
+ @str_key.value
94
+ #=> 'hello world'
95
+
96
+ ## JsonStringKey String has correct class
97
+ @str_key.value.class
98
+ #=> String
99
+
100
+ ## JsonStringKey stores and retrieves empty string
101
+ @empty_str_key = Familia::JsonStringKey.new 'test:json_string:empty_string'
102
+ @empty_str_key.value = ''
103
+ @empty_str_key.value
104
+ #=> ''
105
+
106
+ ## JsonStringKey empty string is different from nil
107
+ @empty_str_key.value.nil?
108
+ #=> false
109
+
110
+ ## JsonStringKey stores and retrieves Hash with type preservation
111
+ @hash_key = Familia::JsonStringKey.new 'test:json_string:hash'
112
+ @hash_key.value = { 'name' => 'test', 'count' => 5 }
113
+ @hash_key.value
114
+ #=> {'name'=>'test', 'count'=>5}
115
+
116
+ ## JsonStringKey Hash has correct class
117
+ @hash_key.value.class
118
+ #=> Hash
119
+
120
+ ## JsonStringKey Hash preserves nested types (integer value)
121
+ @hash_key.value['count']
122
+ #=> 5
123
+
124
+ ## JsonStringKey Hash nested value has correct class
125
+ @hash_key.value['count'].class
126
+ #=> Integer
127
+
128
+ ## JsonStringKey stores and retrieves Array with type preservation
129
+ @array_key = Familia::JsonStringKey.new 'test:json_string:array'
130
+ @array_key.value = [1, 'two', true, nil, 3.5]
131
+ @array_key.value
132
+ #=> [1, 'two', true, nil, 3.5]
133
+
134
+ ## JsonStringKey Array has correct class
135
+ @array_key.value.class
136
+ #=> Array
137
+
138
+ ## JsonStringKey stores nested structures (hash with arrays)
139
+ @nested_key = Familia::JsonStringKey.new 'test:json_string:nested'
140
+ @nested_key.value = { 'users' => [{ 'id' => 1 }, { 'id' => 2 }], 'active' => true }
141
+ @nested_key.value
142
+ #=> {'users'=>[{'id'=>1}, {'id'=>2}], 'active'=>true}
143
+
144
+ ## JsonStringKey nested structure preserves all types
145
+ @nested_key.value['active'].class
146
+ #=> TrueClass
147
+
148
+ ## JsonStringKey nested array element is Hash
149
+ @nested_key.value['users'][0].class
150
+ #=> Hash
151
+
152
+ ## JsonStringKey nested array element has integer value
153
+ @nested_key.value['users'][0]['id']
154
+ #=> 1
155
+
156
+ # ========================================
157
+ # Basic Operations Tests
158
+ # ========================================
159
+
160
+ ## JsonStringKey#dbkey returns correct key
161
+ @basic_key = Familia::JsonStringKey.new 'arbitrary:json:key'
162
+ @basic_key.dbkey
163
+ #=> 'arbitrary:json:key'
164
+
165
+ ## JsonStringKey#value= sets a value and returns it
166
+ @basic_key.value = 'test value'
167
+ #=> 'test value'
168
+
169
+ ## JsonStringKey#value retrieves the set value
170
+ @basic_key.value
171
+ #=> 'test value'
172
+
173
+ ## JsonStringKey#set is alias for value=
174
+ @basic_key.set('another value')
175
+ @basic_key.value
176
+ #=> 'another value'
177
+
178
+ ## JsonStringKey#get is alias for value
179
+ @basic_key.value = 'get test'
180
+ @basic_key.get
181
+ #=> 'get test'
182
+
183
+ ## JsonStringKey#content is alias for value
184
+ @basic_key.content
185
+ #=> 'get test'
186
+
187
+ ## JsonStringKey#setnx sets value only if key does not exist (success case)
188
+ @setnx_key = Familia::JsonStringKey.new 'test:json_string:setnx'
189
+ @setnx_key.delete!
190
+ @setnx_key.setnx('first value')
191
+ #=> true
192
+
193
+ ## JsonStringKey#setnx value was set
194
+ @setnx_key.value
195
+ #=> 'first value'
196
+
197
+ ## JsonStringKey#setnx does not overwrite existing value (failure case)
198
+ @setnx_key.setnx('second value')
199
+ #=> false
200
+
201
+ ## JsonStringKey#setnx original value preserved
202
+ @setnx_key.value
203
+ #=> 'first value'
204
+
205
+ ## JsonStringKey#del removes the key
206
+ @del_key = Familia::JsonStringKey.new 'test:json_string:del'
207
+ @del_key.value = 'to be deleted'
208
+ @del_key.del
209
+ #=> true
210
+
211
+ ## JsonStringKey#del returns false when key does not exist
212
+ @del_key.del
213
+ #=> false
214
+
215
+ ## JsonStringKey#delete! removes the key (returns count)
216
+ @del2_key = Familia::JsonStringKey.new 'test:json_string:del2'
217
+ @del2_key.value = 'to be deleted'
218
+ @del2_key.delete!
219
+ #=> 1
220
+
221
+ ## JsonStringKey#empty? returns true when value is nil
222
+ @empty_check = Familia::JsonStringKey.new 'test:json_string:empty_check'
223
+ @empty_check.delete!
224
+ @empty_check.empty?
225
+ #=> true
226
+
227
+ ## JsonStringKey#empty? returns false when value exists
228
+ @empty_check.value = 'not empty'
229
+ @empty_check.empty?
230
+ #=> false
231
+
232
+ # ========================================
233
+ # Conversion Methods Tests
234
+ # ========================================
235
+
236
+ ## JsonStringKey#to_s returns deserialized value as string
237
+ @conv_key = Familia::JsonStringKey.new 'test:json_string:conversion'
238
+ @conv_key.value = 'string test'
239
+ @conv_key.to_s
240
+ #=> 'string test'
241
+
242
+ ## JsonStringKey#to_s converts integer to string
243
+ @conv_key.value = 42
244
+ @conv_key.to_s
245
+ #=> '42'
246
+
247
+ ## JsonStringKey#to_i converts string to integer
248
+ @conv_key.value = '123'
249
+ @conv_key.to_i
250
+ #=> 123
251
+
252
+ ## JsonStringKey#to_i returns integer as-is
253
+ @conv_key.value = 456
254
+ @conv_key.to_i
255
+ #=> 456
256
+
257
+ ## JsonStringKey#to_f converts string to float
258
+ @conv_key.value = '3.14'
259
+ @conv_key.to_f
260
+ #=> 3.14
261
+
262
+ ## JsonStringKey#to_f returns float as-is
263
+ @conv_key.value = 2.718
264
+ @conv_key.to_f
265
+ #=> 2.718
266
+
267
+ # ========================================
268
+ # Default Value Handling Tests
269
+ # ========================================
270
+
271
+ ## JsonStringKey with :default option returns default when key does not exist
272
+ @default_key = Familia::JsonStringKey.new 'test:json_string:with_default', default: 'default_value'
273
+ @default_key.delete!
274
+ @default_key.value
275
+ #=> 'default_value'
276
+
277
+ ## JsonStringKey with :default option for integer
278
+ @default_int = Familia::JsonStringKey.new 'test:json_string:default_int', default: 100
279
+ @default_int.delete!
280
+ @default_int.value
281
+ #=> 100
282
+
283
+ ## JsonStringKey with :default option still allows setting value
284
+ @default_key.value = 'custom value'
285
+ @default_key.value
286
+ #=> 'custom value'
287
+
288
+ ## JsonStringKey :default with boolean false
289
+ @default_false = Familia::JsonStringKey.new 'test:json_string:default_false', default: false
290
+ @default_false.delete!
291
+ @default_false.value
292
+ #=> false
293
+
294
+ ## JsonStringKey :default with zero
295
+ @default_zero = Familia::JsonStringKey.new 'test:json_string:default_zero', default: 0
296
+ @default_zero.delete!
297
+ @default_zero.value
298
+ #=> 0
299
+
300
+ # ========================================
301
+ # Edge Cases Tests
302
+ # ========================================
303
+
304
+ ## JsonStringKey handles symbols by converting to string
305
+ @symbol_key = Familia::JsonStringKey.new 'test:json_string:symbol'
306
+ @symbol_key.value = :active
307
+ @symbol_key.value
308
+ #=> 'active'
309
+
310
+ ## JsonStringKey handles very large integers
311
+ @large_int = Familia::JsonStringKey.new 'test:json_string:large_int'
312
+ @large_int.value = 9999999999999999999
313
+ @large_int.value
314
+ #=> 9999999999999999999
315
+
316
+ ## JsonStringKey handles very small floats
317
+ @small_float = Familia::JsonStringKey.new 'test:json_string:small_float'
318
+ @small_float.value = 0.000000001
319
+ @small_float.value
320
+ #=> 0.000000001
321
+
322
+ ## JsonStringKey handles unicode strings
323
+ @unicode_key = Familia::JsonStringKey.new 'test:json_string:unicode'
324
+ @unicode_key.value = 'Hello'
325
+ @unicode_key.value
326
+ #=> 'Hello'
327
+
328
+ ## JsonStringKey handles special characters
329
+ @special_key = Familia::JsonStringKey.new 'test:json_string:special'
330
+ @special_key.value = "line1\nline2\ttab\"quote"
331
+ @special_key.value
332
+ #=> "line1\nline2\ttab\"quote"
333
+
334
+ ## JsonStringKey handles deeply nested structure
335
+ @deep_key = Familia::JsonStringKey.new 'test:json_string:deep'
336
+ @deep_key.value = { 'a' => { 'b' => { 'c' => { 'd' => [1, 2, 3] } } } }
337
+ @deep_key.value['a']['b']['c']['d']
338
+ #=> [1, 2, 3]
339
+
340
+ # ========================================
341
+ # Registration Tests
342
+ # ========================================
343
+
344
+ ## JsonStringKey is registered as :json_string
345
+ Familia::DataType.registered_types[:json_string]
346
+ #=> Familia::JsonStringKey
347
+
348
+ ## JsonStringKey is registered as :json_stringkey
349
+ Familia::DataType.registered_types[:json_stringkey]
350
+ #=> Familia::JsonStringKey
351
+
352
+ ## JsonStringKey is registered as :jsonkey (alias like hashkey)
353
+ Familia::DataType.registered_types[:jsonkey]
354
+ #=> Familia::JsonStringKey
355
+
356
+ # ========================================
357
+ # Familia Object Reference Tests
358
+ # ========================================
359
+
360
+ ## JsonStringKey stores Familia object by identifier
361
+ @cust = Customer.new
362
+ @cust.custid = 'json_test_customer@example.com'
363
+ @ref_key = Familia::JsonStringKey.new 'test:json_string:familia_ref'
364
+ @ref_key.value = @cust
365
+ @ref_key.value
366
+ #=> 'json_test_customer@example.com'
367
+
368
+ # ========================================
369
+ # Type Comparison with StringKey
370
+ # ========================================
371
+
372
+ ## StringKey does NOT preserve integer type (stores as string)
373
+ @str_compare = Familia::StringKey.new 'test:stringkey:compare'
374
+ @str_compare.value = 42
375
+ @str_compare.value.class
376
+ #=> String
377
+
378
+ ## JsonStringKey DOES preserve integer type
379
+ @json_compare = Familia::JsonStringKey.new 'test:json_stringkey:compare'
380
+ @json_compare.value = 42
381
+ @json_compare.value.class
382
+ #=> Integer
383
+
384
+ ## StringKey does NOT preserve boolean type
385
+ @str_bool = Familia::StringKey.new 'test:stringkey:bool'
386
+ @str_bool.value = true
387
+ @str_bool.value.class
388
+ #=> String
389
+
390
+ ## JsonStringKey DOES preserve boolean type
391
+ @json_bool = Familia::JsonStringKey.new 'test:json_stringkey:bool'
392
+ @json_bool.value = true
393
+ @json_bool.value.class
394
+ #=> TrueClass
395
+
396
+ # ========================================
397
+ # Cleanup
398
+ # ========================================
399
+
400
+ @int_key.delete!
401
+ @zero_key.delete!
402
+ @neg_key.delete!
403
+ @float_key.delete!
404
+ @precision_key.delete!
405
+ @true_key.delete!
406
+ @false_key.delete!
407
+ @nil_key.delete!
408
+ @str_key.delete!
409
+ @empty_str_key.delete!
410
+ @hash_key.delete!
411
+ @array_key.delete!
412
+ @nested_key.delete!
413
+ @basic_key.delete!
414
+ @setnx_key.delete!
415
+ @empty_check.delete!
416
+ @conv_key.delete!
417
+ @default_key.delete!
418
+ @default_int.delete!
419
+ @default_false.delete!
420
+ @default_zero.delete!
421
+ @symbol_key.delete!
422
+ @large_int.delete!
423
+ @small_float.delete!
424
+ @unicode_key.delete!
425
+ @special_key.delete!
426
+ @deep_key.delete!
427
+ @ref_key.delete!
428
+ @str_compare.delete!
429
+ @json_compare.delete!
430
+ @str_bool.delete!
431
+ @json_bool.delete!
@@ -19,17 +19,6 @@ end
19
19
  user_class.new(email: "test@example.com")
20
20
  #==> _.logical_database == 5
21
21
 
22
- ## custom serialization methods
23
- user_class = Class.new(Familia::Horreum) do
24
- identifier_field :email
25
- field :email
26
- field :name
27
- end
28
-
29
- user_class.new(email: "test@example.com")
30
- #==> _.respond_to?(:dump_method)
31
- #==> _.respond_to?(:load_method)
32
-
33
22
  ## redisuri generation with suffix
34
23
  user_class = Class.new(Familia::Horreum) do
35
24
  identifier_field :email
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: familia
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre23
4
+ version: 2.0.0.pre25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -239,6 +239,7 @@ files:
239
239
  - lib/familia/data_type/settings.rb
240
240
  - lib/familia/data_type/types/counter.rb
241
241
  - lib/familia/data_type/types/hashkey.rb
242
+ - lib/familia/data_type/types/json_stringkey.rb
242
243
  - lib/familia/data_type/types/listkey.rb
243
244
  - lib/familia/data_type/types/lock.rb
244
245
  - lib/familia/data_type/types/sorted_set.rb
@@ -317,6 +318,7 @@ files:
317
318
  - pr_agent.toml
318
319
  - pr_compliance_checklist.yaml
319
320
  - try/edge_cases/empty_identifiers_try.rb
321
+ - try/edge_cases/find_by_dbkey_race_condition_try.rb
320
322
  - try/edge_cases/hash_symbolization_try.rb
321
323
  - try/edge_cases/json_serialization_try.rb
322
324
  - try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb
@@ -360,6 +362,9 @@ files:
360
362
  - try/features/object_identifier/object_identifier_try.rb
361
363
  - try/features/quantization/quantization_try.rb
362
364
  - try/features/real_feature_integration_try.rb
365
+ - try/features/relationships/class_level_multi_index_auto_try.rb
366
+ - try/features/relationships/class_level_multi_index_rebuild_try.rb
367
+ - try/features/relationships/class_level_multi_index_try.rb
363
368
  - try/features/relationships/indexing_commands_verification_try.rb
364
369
  - try/features/relationships/indexing_rebuild_try.rb
365
370
  - try/features/relationships/indexing_try.rb
@@ -503,6 +508,7 @@ files:
503
508
  - try/unit/data_types/counter_try.rb
504
509
  - try/unit/data_types/datatype_base_try.rb
505
510
  - try/unit/data_types/hash_try.rb
511
+ - try/unit/data_types/json_stringkey_try.rb
506
512
  - try/unit/data_types/list_try.rb
507
513
  - try/unit/data_types/lock_try.rb
508
514
  - try/unit/data_types/serialization_try.rb