familia 2.0.0.pre7 → 2.0.0.pre8
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/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/README.md +35 -0
- data/docs/wiki/Feature-System-Guide.md +0 -15
- data/lib/familia/features/external_identifiers/external_identifier_field_type.rb +120 -0
- data/lib/familia/features/external_identifiers.rb +111 -0
- data/lib/familia/features/object_identifiers/object_identifier_field_type.rb +91 -0
- data/lib/familia/features/object_identifiers.rb +194 -0
- data/lib/familia/features/relationships/cascading.rb +0 -1
- data/lib/familia/features/relationships/indexing.rb +0 -1
- data/lib/familia/features/relationships/membership.rb +0 -1
- data/lib/familia/features/relationships/querying.rb +7 -12
- data/lib/familia/features/relationships/score_encoding.rb +1 -3
- data/lib/familia/features/relationships/tracking.rb +0 -1
- data/lib/familia/features/transient_fields.rb +8 -10
- data/lib/familia/features.rb +16 -13
- data/lib/familia/horreum/core/serialization.rb +2 -5
- data/lib/familia/horreum/subclass/definition.rb +34 -0
- data/lib/familia/version.rb +1 -3
- data/try/core/errors_try.rb +1 -1
- data/try/features/{encrypted_fields_core_try.rb → encrypted_fields/encrypted_fields_core_try.rb} +1 -1
- data/try/features/{encrypted_fields_integration_try.rb → encrypted_fields/encrypted_fields_integration_try.rb} +1 -1
- data/try/features/{encrypted_fields_no_cache_security_try.rb → encrypted_fields/encrypted_fields_no_cache_security_try.rb} +1 -1
- data/try/features/{encrypted_fields_security_try.rb → encrypted_fields/encrypted_fields_security_try.rb} +1 -1
- data/try/features/{expiration_try.rb → expiration/expiration_try.rb} +1 -1
- data/try/features/external_identifiers/external_identifiers_try.rb +203 -0
- data/try/features/object_identifiers/object_identifiers_integration_try.rb +289 -0
- data/try/features/object_identifiers/object_identifiers_try.rb +191 -0
- data/try/features/{quantization_try.rb → quantization/quantization_try.rb} +1 -1
- data/try/features/{categorical_permissions_try.rb → relationships/categorical_permissions_try.rb} +1 -1
- data/try/features/{relationships_edge_cases_try.rb → relationships/relationships_edge_cases_try.rb} +1 -1
- data/try/features/{relationships_performance_minimal_try.rb → relationships/relationships_performance_minimal_try.rb} +1 -1
- data/try/features/{relationships_performance_simple_try.rb → relationships/relationships_performance_simple_try.rb} +1 -1
- data/try/features/{relationships_performance_try.rb → relationships/relationships_performance_try.rb} +1 -1
- data/try/features/{relationships_performance_working_try.rb → relationships/relationships_performance_working_try.rb} +1 -1
- data/try/features/{relationships_try.rb → relationships/relationships_try.rb} +1 -1
- data/try/features/{safe_dump_advanced_try.rb → safe_dump/safe_dump_advanced_try.rb} +1 -1
- data/try/features/{safe_dump_try.rb → safe_dump/safe_dump_try.rb} +1 -1
- data/try/features/{transient_fields_core_try.rb → transient_fields/transient_fields_core_try.rb} +1 -1
- data/try/features/{transient_fields_integration_try.rb → transient_fields/transient_fields_integration_try.rb} +1 -1
- metadata +38 -31
- /data/try/features/{encryption_fields → encrypted_fields}/aad_protection_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/concealed_string_core_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/context_isolation_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/error_conditions_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/fresh_key_derivation_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/fresh_key_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/key_rotation_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/memory_security_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/missing_current_key_version_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/nonce_uniqueness_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/secure_by_default_behavior_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/thread_safety_try.rb +0 -0
- /data/try/features/{encryption_fields → encrypted_fields}/universal_serialization_safety_try.rb +0 -0
@@ -0,0 +1,289 @@
|
|
1
|
+
# try/features/object_identifiers_integration_try.rb
|
2
|
+
|
3
|
+
require_relative '../../helpers/test_helpers'
|
4
|
+
|
5
|
+
Familia.debug = false
|
6
|
+
|
7
|
+
# Integration test for ObjectIdentifiers and ExternalIdentifiers features together
|
8
|
+
|
9
|
+
# Class using both features with defaults
|
10
|
+
class IntegrationTest < Familia::Horreum
|
11
|
+
feature :object_identifiers
|
12
|
+
feature :external_identifiers # This depends on :object_identifiers
|
13
|
+
identifier_field :id
|
14
|
+
field :id
|
15
|
+
field :name
|
16
|
+
field :email
|
17
|
+
end
|
18
|
+
|
19
|
+
# Class with custom configurations for both features
|
20
|
+
class CustomIntegrationTest < Familia::Horreum
|
21
|
+
feature :object_identifiers, generator: :hex
|
22
|
+
feature :external_identifiers, prefix: 'custom'
|
23
|
+
identifier_field :id
|
24
|
+
field :id
|
25
|
+
field :name
|
26
|
+
end
|
27
|
+
|
28
|
+
# Class testing full lifecycle with Redis persistence
|
29
|
+
class PersistenceTest < Familia::Horreum
|
30
|
+
feature :object_identifiers
|
31
|
+
feature :external_identifiers
|
32
|
+
identifier_field :id
|
33
|
+
field :id
|
34
|
+
field :name
|
35
|
+
field :created_at
|
36
|
+
end
|
37
|
+
|
38
|
+
# Setup test objects
|
39
|
+
@integration_obj = IntegrationTest.new(id: 'integration_1', name: 'Integration Test', email: 'test@example.com')
|
40
|
+
@custom_obj = CustomIntegrationTest.new(id: 'custom_1', name: 'Custom Test')
|
41
|
+
|
42
|
+
## Object identifiers feature is automatically included
|
43
|
+
IntegrationTest.features_enabled.include?(:object_identifiers)
|
44
|
+
#==> true
|
45
|
+
|
46
|
+
## External identifiers feature is included
|
47
|
+
IntegrationTest.features_enabled.include?(:external_identifiers)
|
48
|
+
#==> true
|
49
|
+
|
50
|
+
## Object responds to objid accessor
|
51
|
+
obj = IntegrationTest.new
|
52
|
+
obj.respond_to?(:objid)
|
53
|
+
#==> true
|
54
|
+
|
55
|
+
## Object responds to extid accessor
|
56
|
+
obj = IntegrationTest.new
|
57
|
+
obj.respond_to?(:extid)
|
58
|
+
#==> true
|
59
|
+
|
60
|
+
## Class responds to find_by_objid method
|
61
|
+
IntegrationTest.respond_to?(:find_by_objid)
|
62
|
+
#==> true
|
63
|
+
|
64
|
+
## Class responds to find_by_extid method
|
65
|
+
IntegrationTest.respond_to?(:find_by_extid)
|
66
|
+
#==> true
|
67
|
+
|
68
|
+
## objid and extid are different values
|
69
|
+
obj = IntegrationTest.new
|
70
|
+
obj.objid != obj.extid
|
71
|
+
#==> true
|
72
|
+
|
73
|
+
## extid is deterministically generated from objid
|
74
|
+
obj = IntegrationTest.new
|
75
|
+
original_objid = obj.objid
|
76
|
+
original_extid = obj.extid
|
77
|
+
# Create new object with same objid
|
78
|
+
obj2 = IntegrationTest.new
|
79
|
+
obj2.instance_variable_set(:@objid, original_objid)
|
80
|
+
obj2.extid == original_extid
|
81
|
+
#==> true
|
82
|
+
|
83
|
+
## Custom objid uses hex format (64 chars for 256-bit)
|
84
|
+
@custom_obj.objid.match(/\A[0-9a-f]{64}\z/)
|
85
|
+
#=*> nil
|
86
|
+
|
87
|
+
## Custom extid uses custom prefix
|
88
|
+
@custom_obj.extid.start_with?('custom_')
|
89
|
+
#==> true
|
90
|
+
|
91
|
+
## Both IDs persist through save/load cycle
|
92
|
+
persistence_obj = PersistenceTest.new
|
93
|
+
persistence_obj.id = 'persistence_test'
|
94
|
+
persistence_obj.name = 'Persistence Test Object'
|
95
|
+
persistence_obj.created_at = Time.now.to_i
|
96
|
+
original_objid = persistence_obj.objid
|
97
|
+
original_extid = persistence_obj.extid
|
98
|
+
persistence_obj.save
|
99
|
+
|
100
|
+
# Load from Redis
|
101
|
+
loaded_obj = PersistenceTest.new(id: 'persistence_test')
|
102
|
+
|
103
|
+
## objid persists after save/load
|
104
|
+
persistence_obj = PersistenceTest.new
|
105
|
+
persistence_obj.id = 'persistence_test'
|
106
|
+
persistence_obj.name = 'Persistence Test Object'
|
107
|
+
persistence_obj.created_at = Time.now.to_i
|
108
|
+
original_objid = persistence_obj.objid
|
109
|
+
persistence_obj.save
|
110
|
+
loaded_obj = PersistenceTest.new(id: 'persistence_test')
|
111
|
+
loaded_obj.objid == original_objid
|
112
|
+
#==> true
|
113
|
+
|
114
|
+
## extid persists after save/load
|
115
|
+
persistence_obj = PersistenceTest.new
|
116
|
+
persistence_obj.id = 'persistence_test'
|
117
|
+
persistence_obj.name = 'Persistence Test Object'
|
118
|
+
persistence_obj.created_at = Time.now.to_i
|
119
|
+
original_extid = persistence_obj.extid
|
120
|
+
persistence_obj.save
|
121
|
+
loaded_obj = PersistenceTest.new(id: 'persistence_test')
|
122
|
+
loaded_obj.extid == original_extid
|
123
|
+
#==> true
|
124
|
+
|
125
|
+
## objid instance variable starts nil (lazy generation)
|
126
|
+
lazy_obj = IntegrationTest.new
|
127
|
+
lazy_obj.instance_variable_get(:@objid)
|
128
|
+
#=> nil
|
129
|
+
|
130
|
+
## extid instance variable starts nil (lazy generation)
|
131
|
+
lazy_obj = IntegrationTest.new
|
132
|
+
lazy_obj.instance_variable_get(:@extid)
|
133
|
+
#=> nil
|
134
|
+
|
135
|
+
## Accessing objid first doesn't trigger extid generation
|
136
|
+
lazy_obj = IntegrationTest.new
|
137
|
+
lazy_obj.objid
|
138
|
+
lazy_obj.instance_variable_get(:@extid)
|
139
|
+
#=> nil
|
140
|
+
|
141
|
+
## Accessing extid triggers objid generation if needed
|
142
|
+
lazy_obj2 = IntegrationTest.new
|
143
|
+
lazy_obj2.extid # This should trigger objid generation too
|
144
|
+
lazy_obj2.instance_variable_get(:@objid)
|
145
|
+
#=*> nil
|
146
|
+
|
147
|
+
## Check field types objid
|
148
|
+
IntegrationTest.field_types[:objid].is_a?(Familia::Features::ObjectIdentifiers::ObjectIdentifierFieldType)
|
149
|
+
#==> true
|
150
|
+
|
151
|
+
## ObjectIdentifier fields have correct types in field registry
|
152
|
+
IntegrationTest.field_types[:objid].class.ancestors.include?(Familia::Features::ObjectIdentifiers::ObjectIdentifierFieldType)
|
153
|
+
#==> true
|
154
|
+
|
155
|
+
## ExternalIdentifier fields have correct types in field registry
|
156
|
+
IntegrationTest.field_types[:extid].class.ancestors.include?(Familia::Features::ExternalIdentifiers::ExternalIdentifierFieldType)
|
157
|
+
#==> true
|
158
|
+
|
159
|
+
## Object identifiers options are preserved
|
160
|
+
opts = IntegrationTest.feature_options
|
161
|
+
opts.key?(:object_identifiers)
|
162
|
+
#==> true
|
163
|
+
|
164
|
+
## External identifiers options are preserved
|
165
|
+
opts = IntegrationTest.feature_options
|
166
|
+
opts.key?(:external_identifiers)
|
167
|
+
#==> true
|
168
|
+
|
169
|
+
## Generator default configuration is applied correctly
|
170
|
+
IntegrationTest.feature_options(:object_identifiers)[:generator]
|
171
|
+
#=> :uuid_v7
|
172
|
+
|
173
|
+
## Prefix default configuration is applied correctly
|
174
|
+
IntegrationTest.feature_options(:external_identifiers)[:prefix]
|
175
|
+
#=> "ext"
|
176
|
+
|
177
|
+
## Custom generator configuration is applied correctly
|
178
|
+
CustomIntegrationTest.feature_options(:object_identifiers)[:generator]
|
179
|
+
#=> :hex
|
180
|
+
|
181
|
+
## Custom prefix configuration is applied correctly
|
182
|
+
CustomIntegrationTest.feature_options(:external_identifiers)[:prefix]
|
183
|
+
#=> "custom"
|
184
|
+
|
185
|
+
## objid is URL-safe (UUID format)
|
186
|
+
obj = IntegrationTest.new
|
187
|
+
obj.objid.match(/\A[A-Za-z0-9\-]+\z/)
|
188
|
+
#=*> nil
|
189
|
+
|
190
|
+
## extid is URL-safe (base36 format)
|
191
|
+
obj = IntegrationTest.new
|
192
|
+
obj.extid.match(/\A[a-z0-9_]+\z/)
|
193
|
+
#=*> nil
|
194
|
+
|
195
|
+
## Data integrity preserved during complex initialization
|
196
|
+
complex_obj = IntegrationTest.new(
|
197
|
+
id: 'complex_integration',
|
198
|
+
name: 'Complex Integration',
|
199
|
+
email: 'complex@test.com',
|
200
|
+
objid: 'preset_objid_123',
|
201
|
+
extid: 'preset_ext_456'
|
202
|
+
)
|
203
|
+
|
204
|
+
## Preset objid value is preserved
|
205
|
+
complex_obj = IntegrationTest.new(
|
206
|
+
id: 'complex_integration',
|
207
|
+
name: 'Complex Integration',
|
208
|
+
email: 'complex@test.com',
|
209
|
+
objid: 'preset_objid_123',
|
210
|
+
extid: 'preset_ext_456'
|
211
|
+
)
|
212
|
+
complex_obj.objid
|
213
|
+
#=> 'preset_objid_123'
|
214
|
+
|
215
|
+
## Preset extid value is preserved
|
216
|
+
complex_obj = IntegrationTest.new(
|
217
|
+
id: 'complex_integration',
|
218
|
+
name: 'Complex Integration',
|
219
|
+
email: 'complex@test.com',
|
220
|
+
objid: 'preset_objid_123',
|
221
|
+
extid: 'preset_ext_456'
|
222
|
+
)
|
223
|
+
complex_obj.extid
|
224
|
+
#=> 'preset_ext_456'
|
225
|
+
|
226
|
+
## find_by methods are available (stub implementations)
|
227
|
+
search_obj = IntegrationTest.new
|
228
|
+
search_obj.id = 'search_test'
|
229
|
+
search_obj.save
|
230
|
+
|
231
|
+
## find_by_objid returns nil (stub implementation)
|
232
|
+
search_obj = IntegrationTest.new
|
233
|
+
search_obj.id = 'search_test'
|
234
|
+
search_obj.save
|
235
|
+
found_by_objid = IntegrationTest.find_by_objid(search_obj.objid)
|
236
|
+
found_by_objid
|
237
|
+
#=> nil
|
238
|
+
|
239
|
+
## find_by_extid works with real implementation
|
240
|
+
@search_obj = IntegrationTest.new
|
241
|
+
@search_obj.id = 'search_test'
|
242
|
+
@search_obj.save
|
243
|
+
found_by_extid = IntegrationTest.find_by_extid(@search_obj.extid)
|
244
|
+
found_by_extid&.id
|
245
|
+
#=> "search_test"
|
246
|
+
|
247
|
+
## Both IDs remain stable across multiple accesses
|
248
|
+
stability_obj = IntegrationTest.new
|
249
|
+
first_objid = stability_obj.objid
|
250
|
+
first_extid = stability_obj.extid
|
251
|
+
second_objid = stability_obj.objid
|
252
|
+
second_extid = stability_obj.extid
|
253
|
+
|
254
|
+
## objid remains stable across accesses
|
255
|
+
stability_obj = IntegrationTest.new
|
256
|
+
first_objid = stability_obj.objid
|
257
|
+
second_objid = stability_obj.objid
|
258
|
+
first_objid == second_objid
|
259
|
+
#==> true
|
260
|
+
|
261
|
+
## extid remains stable across accesses
|
262
|
+
stability_obj = IntegrationTest.new
|
263
|
+
first_extid = stability_obj.extid
|
264
|
+
second_extid = stability_obj.extid
|
265
|
+
first_extid == second_extid
|
266
|
+
#==> true
|
267
|
+
|
268
|
+
## Feature dependency is enforced (external_identifiers requires object_identifiers)
|
269
|
+
# This is automatically handled by the feature system
|
270
|
+
IntegrationTest.features_enabled.include?(:object_identifiers)
|
271
|
+
#==> true
|
272
|
+
|
273
|
+
## Objects work with existing Horreum save pattern
|
274
|
+
obj = IntegrationTest.new
|
275
|
+
obj.respond_to?(:save)
|
276
|
+
#==> true
|
277
|
+
|
278
|
+
## Objects work with existing Horreum exists pattern
|
279
|
+
obj = IntegrationTest.new
|
280
|
+
obj.respond_to?(:exists?)
|
281
|
+
#==> true
|
282
|
+
|
283
|
+
## Objects work with existing Horreum delete pattern
|
284
|
+
obj = IntegrationTest.new
|
285
|
+
obj.respond_to?(:delete!)
|
286
|
+
#==> true
|
287
|
+
|
288
|
+
# Cleanup test objects
|
289
|
+
@search_obj.destroy! rescue nil
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# try/features/object_identifiers_try.rb
|
2
|
+
|
3
|
+
require_relative '../../helpers/test_helpers'
|
4
|
+
|
5
|
+
Familia.debug = false
|
6
|
+
|
7
|
+
# Test ObjectIdentifiers feature functionality
|
8
|
+
|
9
|
+
# Basic class using default UUID v7 generator
|
10
|
+
class BasicObjectTest < Familia::Horreum
|
11
|
+
feature :object_identifiers
|
12
|
+
identifier_field :id
|
13
|
+
field :id
|
14
|
+
field :name
|
15
|
+
end
|
16
|
+
|
17
|
+
# Class using UUID v4 generator
|
18
|
+
class UuidV4Test < Familia::Horreum
|
19
|
+
feature :object_identifiers, generator: :uuid_v4
|
20
|
+
identifier_field :id
|
21
|
+
field :id
|
22
|
+
field :name
|
23
|
+
end
|
24
|
+
|
25
|
+
# Class using hex generator
|
26
|
+
class HexTest < Familia::Horreum
|
27
|
+
feature :object_identifiers, generator: :hex
|
28
|
+
identifier_field :id
|
29
|
+
field :id
|
30
|
+
field :name
|
31
|
+
end
|
32
|
+
|
33
|
+
# Class using custom proc generator
|
34
|
+
class CustomProcTest < Familia::Horreum
|
35
|
+
feature :object_identifiers, generator: -> { "custom_#{SecureRandom.hex(4)}" }
|
36
|
+
identifier_field :id
|
37
|
+
field :id
|
38
|
+
field :name
|
39
|
+
end
|
40
|
+
|
41
|
+
# Class testing data integrity preservation
|
42
|
+
class DataIntegrityTest < Familia::Horreum
|
43
|
+
feature :object_identifiers
|
44
|
+
identifier_field :id
|
45
|
+
field :id
|
46
|
+
field :name
|
47
|
+
end
|
48
|
+
|
49
|
+
# Test with existing object ID during initialization
|
50
|
+
@existing_obj = DataIntegrityTest.new(id: 'test_id', objid: 'preset_id_123', name: 'Preset Object')
|
51
|
+
|
52
|
+
## Feature is available on class
|
53
|
+
BasicObjectTest.features_enabled.include?(:object_identifiers)
|
54
|
+
#==> true
|
55
|
+
|
56
|
+
## Class has objid field defined
|
57
|
+
BasicObjectTest.respond_to?(:objid)
|
58
|
+
#==> true
|
59
|
+
|
60
|
+
## Object has objid accessor
|
61
|
+
obj = BasicObjectTest.new
|
62
|
+
obj.respond_to?(:objid)
|
63
|
+
#==> true
|
64
|
+
|
65
|
+
## Default generator creates UUID v7 format
|
66
|
+
obj = BasicObjectTest.new
|
67
|
+
obj.name = 'Test Object'
|
68
|
+
objid = obj.objid
|
69
|
+
objid.is_a?(String) && objid.length == 36 && objid.include?('-')
|
70
|
+
#==> true
|
71
|
+
|
72
|
+
## UUID v7 objid has correct format (8-4-4-4-12 characters)
|
73
|
+
obj = BasicObjectTest.new
|
74
|
+
obj.objid.match(/\A[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/)
|
75
|
+
#=*> nil
|
76
|
+
|
77
|
+
## UUID v4 generator creates correct format
|
78
|
+
v4_obj = UuidV4Test.new
|
79
|
+
v4_objid = v4_obj.objid
|
80
|
+
v4_objid.match(/\A[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/)
|
81
|
+
#=*> nil
|
82
|
+
|
83
|
+
## Hex generator creates hex string
|
84
|
+
hex_obj = HexTest.new
|
85
|
+
hex_objid = hex_obj.objid
|
86
|
+
hex_objid.is_a?(String) && hex_objid.length == 16 && hex_objid.match(/\A[0-9a-f]+\z/)
|
87
|
+
#==> true
|
88
|
+
|
89
|
+
## Custom proc generator works
|
90
|
+
custom_obj = CustomProcTest.new
|
91
|
+
custom_objid = custom_obj.objid
|
92
|
+
custom_objid.start_with?('custom_') && custom_objid.length == 15
|
93
|
+
#==> true
|
94
|
+
|
95
|
+
## objid is lazy - not generated until accessed
|
96
|
+
lazy_obj = BasicObjectTest.new
|
97
|
+
lazy_obj.instance_variable_get(:@objid)
|
98
|
+
#=> nil
|
99
|
+
|
100
|
+
## objid is generated when first accessed
|
101
|
+
lazy_obj = BasicObjectTest.new
|
102
|
+
lazy_obj.objid
|
103
|
+
lazy_obj
|
104
|
+
#=*> _.instance_variable_get(:@objid)
|
105
|
+
|
106
|
+
## objid is generated when first accessed (alternatve testcase expectation)
|
107
|
+
lazy_obj = BasicObjectTest.new
|
108
|
+
lazy_obj.objid
|
109
|
+
lazy_obj.instance_variable_get(:@objid)
|
110
|
+
#=<> nil
|
111
|
+
|
112
|
+
## objid value is stable across multiple calls
|
113
|
+
lazy_obj = BasicObjectTest.new
|
114
|
+
first_call = lazy_obj.objid
|
115
|
+
second_call = lazy_obj.objid
|
116
|
+
first_call == second_call
|
117
|
+
#==> true
|
118
|
+
|
119
|
+
## Data integrity: preset objid is preserved
|
120
|
+
@existing_obj.objid
|
121
|
+
#=> "preset_id_123"
|
122
|
+
|
123
|
+
## Data integrity: preset objid not regenerated
|
124
|
+
@existing_obj.instance_variable_get(:@objid)
|
125
|
+
#=> "preset_id_123"
|
126
|
+
|
127
|
+
## find_by_objid class method exists
|
128
|
+
BasicObjectTest.respond_to?(:find_by_objid)
|
129
|
+
#==> true
|
130
|
+
|
131
|
+
## find_by_objid returns correct type (stub for now)
|
132
|
+
BasicObjectTest.find_by_objid('nonexistent')
|
133
|
+
#=> nil
|
134
|
+
|
135
|
+
## Generated objid is URL-safe (no special chars except hyphens)
|
136
|
+
url_obj = BasicObjectTest.new
|
137
|
+
objid = url_obj.objid
|
138
|
+
objid
|
139
|
+
#=*> _.match(/\A[A-Za-z0-9\-]+\z/)
|
140
|
+
|
141
|
+
## Different objects get different objids
|
142
|
+
obj1 = BasicObjectTest.new
|
143
|
+
obj2 = BasicObjectTest.new
|
144
|
+
obj1.objid != obj2.objid
|
145
|
+
#==> true
|
146
|
+
|
147
|
+
## objid persists through save/load cycle
|
148
|
+
save_obj = BasicObjectTest.new
|
149
|
+
save_obj.id = 'save_test'
|
150
|
+
save_obj.name = 'Save Test'
|
151
|
+
original_objid = save_obj.objid
|
152
|
+
save_obj.save
|
153
|
+
loaded_obj = BasicObjectTest.new(id: 'save_test')
|
154
|
+
loaded_obj.objid == original_objid
|
155
|
+
#==> true
|
156
|
+
|
157
|
+
## Class with different generator has different objid pattern
|
158
|
+
basic_obj = BasicObjectTest.new
|
159
|
+
hex_obj = HexTest.new
|
160
|
+
basic_obj.objid.include?('-') && !hex_obj.objid.include?('-')
|
161
|
+
#==> true
|
162
|
+
|
163
|
+
## objid field type is ObjectIdentifierFieldType
|
164
|
+
BasicObjectTest.field_types[:objid]
|
165
|
+
#=:> Familia::Features::ObjectIdentifiers::ObjectIdentifierFieldType
|
166
|
+
|
167
|
+
## Generator configuration is accessible through feature options
|
168
|
+
BasicObjectTest.feature_options(:object_identifiers)[:generator]
|
169
|
+
#=> :uuid_v7
|
170
|
+
|
171
|
+
## UUID v4 class has correct generator configured
|
172
|
+
UuidV4Test.feature_options(:object_identifiers)[:generator]
|
173
|
+
#=> :uuid_v4
|
174
|
+
|
175
|
+
## Hex class has correct generator configured
|
176
|
+
HexTest.feature_options(:object_identifiers)[:generator]
|
177
|
+
#=> :hex
|
178
|
+
|
179
|
+
## Custom proc class has proc generator
|
180
|
+
CustomProcTest.feature_options(:object_identifiers)[:generator]
|
181
|
+
#=:> Proc
|
182
|
+
|
183
|
+
## Empty initialization preserves nil objid for lazy generation
|
184
|
+
empty_obj = BasicObjectTest.new
|
185
|
+
empty_obj.instance_variable_get(:@objid)
|
186
|
+
#=> nil
|
187
|
+
|
188
|
+
## Objid generation works with complex initialization
|
189
|
+
complex_obj = BasicObjectTest.new(id: 'complex', name: 'Complex Object')
|
190
|
+
complex_obj
|
191
|
+
#=*> _.objid
|
data/try/features/{categorical_permissions_try.rb → relationships/categorical_permissions_try.rb}
RENAMED
@@ -4,7 +4,7 @@
|
|
4
4
|
# Validates the implementation of categorical permission management with
|
5
5
|
# two-stage filtering pattern for efficient permission-based queries.
|
6
6
|
|
7
|
-
require_relative '
|
7
|
+
require_relative '../../helpers/test_helpers'
|
8
8
|
|
9
9
|
# Categorical Permission System Setup
|
10
10
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Simplified Familia v2 relationship functionality tests - focusing on core working features
|
4
4
|
|
5
|
-
require_relative '
|
5
|
+
require_relative '../../helpers/test_helpers'
|
6
6
|
|
7
7
|
# Test classes for Familia v2 relationship functionality
|
8
8
|
class TestCustomer < Familia::Horreum
|