familia 2.0.0.pre16 → 2.0.0.pre18
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/.github/workflows/ci.yml +2 -2
- data/.github/workflows/{code-smellage.yml → code-smells.yml} +3 -63
- data/.gitignore +2 -0
- data/.rubocop.yml +6 -0
- data/CHANGELOG.rst +82 -0
- data/CLAUDE.md +47 -2
- data/Gemfile.lock +1 -1
- data/README.md +13 -0
- data/bin/irb +1 -1
- data/docs/archive/FAMILIA_TECHNICAL.md +1 -1
- data/docs/guides/core-field-system.md +48 -26
- data/docs/migrating/v2.0.0-pre18.md +58 -0
- data/docs/overview.md +2 -2
- data/docs/qodo-merge-compliance.md +96 -0
- data/docs/reference/api-technical.md +1 -1
- data/examples/encrypted_fields.rb +1 -1
- data/examples/safe_dump.rb +1 -1
- data/lib/familia/base.rb +6 -6
- data/lib/familia/connection/middleware.rb +58 -4
- data/lib/familia/connection.rb +1 -1
- data/lib/familia/data_type/class_methods.rb +63 -0
- data/lib/familia/data_type/connection.rb +83 -0
- data/lib/familia/data_type/{commands.rb → database_commands.rb} +2 -2
- data/lib/familia/data_type/serialization.rb +5 -5
- data/lib/familia/data_type/settings.rb +96 -0
- data/lib/familia/data_type/types/hashkey.rb +2 -1
- data/lib/familia/data_type/types/sorted_set.rb +113 -10
- data/lib/familia/data_type/types/stringkey.rb +0 -4
- data/lib/familia/data_type.rb +8 -195
- data/lib/familia/encryption/encrypted_data.rb +12 -2
- data/lib/familia/encryption/manager.rb +11 -4
- data/lib/familia/features/autoloader.rb +3 -1
- data/lib/familia/features/encrypted_fields/encrypted_field_type.rb +11 -3
- data/lib/familia/features/encrypted_fields.rb +5 -2
- data/lib/familia/features/external_identifier.rb +49 -8
- data/lib/familia/features/object_identifier.rb +84 -12
- data/lib/familia/features/relationships/indexing/multi_index_generators.rb +9 -9
- data/lib/familia/features/relationships/indexing/unique_index_generators.rb +45 -26
- data/lib/familia/features/relationships/indexing.rb +7 -1
- data/lib/familia/features/relationships/participation/participant_methods.rb +6 -2
- data/lib/familia/features/safe_dump.rb +2 -3
- data/lib/familia/features/transient_fields.rb +7 -2
- data/lib/familia/features.rb +6 -1
- data/lib/familia/field_type.rb +0 -18
- data/lib/familia/horreum/{core/connection.rb → connection.rb} +21 -0
- data/lib/familia/horreum/{core/database_commands.rb → database_commands.rb} +1 -1
- data/lib/familia/horreum/{subclass/definition.rb → definition.rb} +102 -56
- data/lib/familia/horreum/{subclass/management.rb → management.rb} +18 -15
- data/lib/familia/horreum/{core/serialization.rb → persistence.rb} +73 -170
- data/lib/familia/horreum/{subclass/related_fields_management.rb → related_fields.rb} +22 -2
- data/lib/familia/horreum/serialization.rb +190 -0
- data/lib/familia/horreum.rb +39 -14
- data/lib/familia/identifier_extractor.rb +60 -0
- data/lib/familia/logging.rb +271 -112
- data/lib/familia/refinements.rb +0 -1
- data/lib/familia/version.rb +1 -1
- data/lib/familia.rb +2 -2
- data/lib/middleware/{database_middleware.rb → database_logger.rb} +47 -14
- data/pr_agent.toml +31 -0
- data/pr_compliance_checklist.yaml +45 -0
- data/try/edge_cases/empty_identifiers_try.rb +1 -1
- data/try/edge_cases/hash_symbolization_try.rb +31 -31
- data/try/edge_cases/json_serialization_try.rb +2 -2
- data/try/edge_cases/legacy_data_detection/deserialization_edge_cases_try.rb +170 -0
- data/try/edge_cases/race_conditions_try.rb +1 -1
- data/try/edge_cases/reserved_keywords_try.rb +1 -1
- data/try/edge_cases/string_coercion_try.rb +1 -1
- data/try/edge_cases/ttl_side_effects_try.rb +1 -1
- data/try/features/encrypted_fields/aad_protection_try.rb +1 -1
- data/try/features/encrypted_fields/concealed_string_core_try.rb +1 -1
- data/try/features/encrypted_fields/context_isolation_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_core_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_integration_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_no_cache_security_try.rb +1 -1
- data/try/features/encrypted_fields/encrypted_fields_security_try.rb +1 -1
- data/try/features/encrypted_fields/error_conditions_try.rb +1 -1
- data/try/features/encrypted_fields/fresh_key_derivation_try.rb +1 -1
- data/try/features/encrypted_fields/fresh_key_try.rb +1 -1
- data/try/features/encrypted_fields/key_rotation_try.rb +1 -1
- data/try/features/encrypted_fields/memory_security_try.rb +1 -1
- data/try/features/encrypted_fields/missing_current_key_version_try.rb +1 -1
- data/try/features/encrypted_fields/nonce_uniqueness_try.rb +1 -1
- data/try/features/encrypted_fields/secure_by_default_behavior_try.rb +1 -1
- data/try/features/encrypted_fields/thread_safety_try.rb +1 -1
- data/try/features/encrypted_fields/universal_serialization_safety_try.rb +1 -1
- data/try/{encryption → features/encryption}/config_persistence_try.rb +1 -1
- data/try/{encryption/encryption_core_try.rb → features/encryption/core_try.rb} +2 -2
- data/try/{encryption → features/encryption}/instance_variable_scope_try.rb +1 -1
- data/try/{encryption → features/encryption}/module_loading_try.rb +1 -1
- data/try/{encryption → features/encryption}/providers/aes_gcm_provider_try.rb +1 -1
- data/try/{encryption → features/encryption}/providers/xchacha20_poly1305_provider_try.rb +1 -1
- data/try/{encryption → features/encryption}/roundtrip_validation_try.rb +1 -1
- data/try/{encryption → features/encryption}/secure_memory_handling_try.rb +2 -2
- data/try/features/expiration/expiration_try.rb +1 -1
- data/try/features/external_identifier/external_identifier_try.rb +1 -1
- data/try/features/feature_dependencies_try.rb +1 -1
- data/try/features/feature_improvements_try.rb +1 -1
- data/try/features/field_groups_try.rb +244 -0
- data/try/features/object_identifier/object_identifier_integration_try.rb +1 -1
- data/try/features/object_identifier/object_identifier_try.rb +1 -1
- data/try/features/quantization/quantization_try.rb +1 -1
- data/try/features/real_feature_integration_try.rb +17 -14
- data/try/features/relationships/indexing_commands_verification_try.rb +8 -3
- data/try/features/relationships/indexing_try.rb +16 -1
- data/try/features/relationships/participation_commands_verification_spec.rb +1 -1
- data/try/features/relationships/participation_commands_verification_try.rb +4 -4
- data/try/features/relationships/participation_performance_improvements_try.rb +1 -1
- data/try/features/relationships/participation_reverse_index_try.rb +1 -1
- data/try/features/relationships/relationships_api_changes_try.rb +1 -1
- data/try/features/relationships/relationships_edge_cases_try.rb +3 -3
- data/try/features/relationships/relationships_performance_minimal_try.rb +1 -1
- data/try/features/relationships/relationships_performance_simple_try.rb +1 -1
- data/try/features/relationships/relationships_performance_try.rb +1 -1
- data/try/features/relationships/relationships_performance_working_try.rb +1 -1
- data/try/features/relationships/relationships_try.rb +1 -1
- data/try/features/safe_dump/safe_dump_advanced_try.rb +1 -1
- data/try/features/safe_dump/safe_dump_try.rb +1 -1
- data/try/features/transient_fields/redacted_string_try.rb +1 -1
- data/try/features/transient_fields/refresh_reset_try.rb +3 -1
- data/try/features/transient_fields/single_use_redacted_string_try.rb +1 -1
- data/try/features/transient_fields/transient_fields_core_try.rb +1 -1
- data/try/features/transient_fields/transient_fields_integration_try.rb +1 -1
- data/try/{connection → integration/connection}/fiber_context_preservation_try.rb +1 -1
- data/try/{connection → integration/connection}/handler_constraints_try.rb +1 -1
- data/try/{core → integration/connection}/isolated_dbclient_try.rb +3 -3
- data/try/integration/connection/middleware_reconnect_try.rb +87 -0
- data/try/{connection → integration/connection}/operation_mode_guards_try.rb +1 -1
- data/try/{connection → integration/connection}/pipeline_fallback_integration_try.rb +1 -1
- data/try/{core → integration/connection}/pools_try.rb +1 -1
- data/try/{connection → integration/connection}/responsibility_chain_tracking_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_fallback_integration_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_permissive_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_strict_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_mode_warn_try.rb +1 -1
- data/try/{connection → integration/connection}/transaction_modes_try.rb +1 -1
- data/try/{core → integration}/conventional_inheritance_try.rb +1 -1
- data/try/{core → integration}/create_method_try.rb +1 -1
- data/try/integration/cross_component_try.rb +1 -1
- data/try/{core → integration}/database_consistency_try.rb +12 -8
- data/try/{core → integration}/familia_extended_try.rb +1 -1
- data/try/{core → integration}/familia_members_methods_try.rb +1 -1
- data/try/{models → integration/models}/customer_safe_dump_try.rb +1 -1
- data/try/{models → integration/models}/customer_try.rb +6 -6
- data/try/{models → integration/models}/datatype_base_try.rb +1 -1
- data/try/{models → integration/models}/familia_object_try.rb +1 -1
- data/try/{core → integration}/persistence_operations_try.rb +1 -1
- data/try/integration/relationships_persistence_round_trip_try.rb +441 -0
- data/try/{configuration → integration}/scenarios_try.rb +2 -2
- data/try/{core → integration}/secure_identifier_try.rb +1 -1
- data/try/{core → integration}/verifiable_identifier_try.rb +1 -1
- data/try/performance/benchmarks_try.rb +2 -2
- data/try/support/benchmarks/deserialization_benchmark.rb +180 -0
- data/try/support/benchmarks/deserialization_correctness_test.rb +237 -0
- data/try/{helpers → support/helpers}/test_helpers.rb +15 -7
- data/try/{memory → support/memory}/memory_docker_ruby_dump.sh +1 -1
- data/try/{core → unit/core}/autoloader_try.rb +1 -1
- data/try/{core → unit/core}/base_enhancements_try.rb +1 -9
- data/try/{core → unit/core}/connection_try.rb +5 -5
- data/try/{core → unit/core}/errors_try.rb +4 -4
- data/try/{core → unit/core}/extensions_try.rb +1 -1
- data/try/unit/core/familia_logger_try.rb +110 -0
- data/try/{core → unit/core}/familia_try.rb +2 -2
- data/try/{core → unit/core}/middleware_try.rb +41 -1
- data/try/{core → unit/core}/settings_try.rb +1 -1
- data/try/{core → unit/core}/time_utils_try.rb +1 -1
- data/try/{core → unit/core}/tools_try.rb +3 -3
- data/try/{core → unit/core}/utils_try.rb +17 -14
- data/try/{data_types → unit/data_types}/boolean_try.rb +1 -1
- data/try/{data_types → unit/data_types}/counter_try.rb +1 -1
- data/try/{data_types → unit/data_types}/datatype_base_try.rb +1 -1
- data/try/{data_types → unit/data_types}/hash_try.rb +1 -1
- data/try/{data_types → unit/data_types}/list_try.rb +1 -1
- data/try/{data_types → unit/data_types}/lock_try.rb +1 -1
- data/try/{data_types → unit/data_types}/sorted_set_try.rb +1 -1
- data/try/unit/data_types/sorted_set_zadd_options_try.rb +625 -0
- data/try/{data_types → unit/data_types}/string_try.rb +1 -1
- data/try/{data_types → unit/data_types}/unsortedset_try.rb +1 -1
- data/try/unit/horreum/auto_indexing_on_save_try.rb +212 -0
- data/try/{horreum → unit/horreum}/base_try.rb +3 -3
- data/try/{horreum → unit/horreum}/class_methods_try.rb +1 -1
- data/try/{horreum → unit/horreum}/commands_try.rb +3 -1
- data/try/unit/horreum/defensive_initialization_try.rb +86 -0
- data/try/{horreum → unit/horreum}/destroy_related_fields_cleanup_try.rb +3 -1
- data/try/{horreum → unit/horreum}/enhanced_conflict_handling_try.rb +1 -1
- data/try/{horreum → unit/horreum}/field_categories_try.rb +27 -18
- data/try/{horreum → unit/horreum}/field_definition_try.rb +1 -1
- data/try/{horreum → unit/horreum}/initialization_try.rb +2 -2
- data/try/unit/horreum/json_type_preservation_try.rb +248 -0
- data/try/{horreum → unit/horreum}/relations_try.rb +1 -1
- data/try/{horreum → unit/horreum}/serialization_persistent_fields_try.rb +24 -18
- data/try/{horreum → unit/horreum}/serialization_try.rb +4 -4
- data/try/{horreum → unit/horreum}/settings_try.rb +3 -1
- data/try/{refinements → unit/refinements}/dear_json_array_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/dear_json_hash_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/time_literals_numeric_methods_try.rb +1 -1
- data/try/{refinements → unit/refinements}/time_literals_string_methods_try.rb +1 -1
- data/try/valkey.conf +26 -0
- metadata +149 -132
- data/lib/familia/distinguisher.rb +0 -85
- data/lib/familia/horreum/core.rb +0 -21
- data/lib/familia/refinements/logger_trace.rb +0 -60
- data/try/refinements/logger_trace_methods_try.rb +0 -44
- /data/lib/familia/horreum/{shared/settings.rb → settings.rb} +0 -0
- /data/lib/familia/horreum/{core/utils.rb → utils.rb} +0 -0
- /data/try/{debugging → support/debugging}/README.md +0 -0
- /data/try/{debugging → support/debugging}/cache_behavior_tracer.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_aad_process.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_concealed_internal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_concealed_reveal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_context_aad.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_context_simple.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_cross_context.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_database_load.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_encrypted_json_check.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_encrypted_json_step_by_step.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_exists_lifecycle.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_field_decrypt.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_fresh_cross_context.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_load_path.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_method_definition.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_method_resolution.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_minimal.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_provider.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_secure_behavior.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_string_class.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_test.rb +0 -0
- /data/try/{debugging → support/debugging}/debug_test_design.rb +0 -0
- /data/try/{debugging → support/debugging}/encryption_method_tracer.rb +0 -0
- /data/try/{debugging → support/debugging}/provider_diagnostics.rb +0 -0
- /data/try/{helpers → support/helpers}/test_cleanup.rb +0 -0
- /data/try/{memory → support/memory}/memory_basic_test.rb +0 -0
- /data/try/{memory → support/memory}/memory_detailed_test.rb +0 -0
- /data/try/{memory → support/memory}/memory_search_for_string.rb +0 -0
- /data/try/{memory → support/memory}/test_actual_redactedstring_protection.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v1_context_proxy.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v2_connection_switching.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v3_connection_pool.rb +0 -0
- /data/try/{prototypes → support/prototypes}/atomic_saves_v4.rb +0 -0
- /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v2_connection_switching_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/README.md +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/configurable_stress_test.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/atomic_saves_v3_connection_pool_helpers.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_metrics.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_stress_test.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/connection_pool_threading_models.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/lib/visualize_stress_results.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/pool_siege.rb +0 -0
- /data/try/{prototypes → support/prototypes}/pooling/run_stress_tests.rb +0 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
# Custom Compliance Checklist for Familia
|
2
|
+
# Documentation: https://qodo-merge-docs.qodo.ai/tools/compliance/
|
3
|
+
|
4
|
+
pr_compliances:
|
5
|
+
- title: "ErrorHandling"
|
6
|
+
compliance_label: true
|
7
|
+
objective: "All external API calls and database operations must have proper error handling"
|
8
|
+
success_criteria: "Try-catch blocks around external calls with appropriate logging or error handling mechanisms"
|
9
|
+
failure_criteria: "External API calls, database operations, or network requests without error handling"
|
10
|
+
|
11
|
+
- title: "TestCoverage"
|
12
|
+
compliance_label: true
|
13
|
+
objective: "New features must include corresponding tests using the Tryouts framework"
|
14
|
+
success_criteria: "Test files present in try/ directory for new functionality following *_try.rb or *.try.rb naming convention"
|
15
|
+
failure_criteria: "New code without test coverage or tests not following Tryouts framework conventions"
|
16
|
+
|
17
|
+
- title: "ChangelogFragment"
|
18
|
+
compliance_label: true
|
19
|
+
objective: "User-facing changes must include a changelog"
|
20
|
+
success_criteria: "New fragment file in changelog.d/ directory following the naming convention and RST format, or updates to CHANGELOG.rst in root directory, or explicit justification for omission"
|
21
|
+
failure_criteria: "User-facing changes without changelog fragment, CHANGELOG.rst updates, or documentation updates"
|
22
|
+
|
23
|
+
- title: "DocumentationUpdates"
|
24
|
+
compliance_label: true
|
25
|
+
objective: "API changes must be reflected in documentation"
|
26
|
+
success_criteria: "YARD documentation comments for new public methods, or updates to docs/ for significant changes"
|
27
|
+
failure_criteria: "New public APIs or significant behavior changes without documentation updates"
|
28
|
+
|
29
|
+
- title: "BackwardCompatibility"
|
30
|
+
compliance_label: true
|
31
|
+
objective: "Changes must maintain backward compatibility or document breaking changes"
|
32
|
+
success_criteria: "No breaking changes to public APIs, or breaking changes clearly documented in migration guides"
|
33
|
+
failure_criteria: "Breaking changes without migration documentation or deprecation warnings"
|
34
|
+
|
35
|
+
- title: "ThreadSafety"
|
36
|
+
compliance_label: true
|
37
|
+
objective: "Code handling shared state must be thread-safe"
|
38
|
+
success_criteria: "Proper synchronization for shared mutable state, or clear documentation of thread-safety assumptions"
|
39
|
+
failure_criteria: "Shared mutable state accessed without synchronization in concurrent contexts"
|
40
|
+
|
41
|
+
- title: "DatabaseKeyNaming"
|
42
|
+
compliance_label: true
|
43
|
+
objective: "Database key generation must follow Familia conventions"
|
44
|
+
success_criteria: "Keys use delim separator, avoid reserved keywords (ttl, db, valkey, redis), and handle empty identifiers"
|
45
|
+
failure_criteria: "Keys using reserved keywords, empty identifiers, or non-standard separators"
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# bug in Tryouts 3.1 that prevents the setup instance vars from
|
5
5
|
# being available to the testcases.
|
6
6
|
|
7
|
-
require_relative '../helpers/test_helpers'
|
7
|
+
require_relative '../support/helpers/test_helpers'
|
8
8
|
|
9
9
|
Familia.debug = false
|
10
10
|
|
@@ -25,22 +25,31 @@ end
|
|
25
25
|
@test_hash.keys
|
26
26
|
#=> ["name", "age", "nested"]
|
27
27
|
|
28
|
-
## After save and refresh, default behavior uses
|
28
|
+
## After save and refresh, default behavior uses string keys
|
29
29
|
@test_obj.refresh!
|
30
30
|
@test_obj.config.keys
|
31
|
-
#=> [
|
31
|
+
#=> ["name", "age", "nested"]
|
32
32
|
|
33
|
-
## Nested hash also has
|
34
|
-
@test_obj.config[
|
35
|
-
#=> [
|
33
|
+
## Nested hash also has string keys
|
34
|
+
@test_obj.config["nested"].keys
|
35
|
+
#=> ["theme"]
|
36
36
|
|
37
37
|
## Get raw JSON from Valkey/Redis
|
38
38
|
@raw_json = @test_obj.hget('config')
|
39
39
|
@raw_json.class
|
40
40
|
#=> String
|
41
41
|
|
42
|
-
## deserialize_value with default symbolize:
|
43
|
-
@
|
42
|
+
## deserialize_value with default symbolize: false returns string keys
|
43
|
+
@string_result_default = @test_obj.deserialize_value(@raw_json)
|
44
|
+
@string_result_default.keys
|
45
|
+
#=> ["name", "age", "nested"]
|
46
|
+
|
47
|
+
## Nested hash in default result also has string keys
|
48
|
+
@string_result_default["nested"].keys
|
49
|
+
#=> ["theme"]
|
50
|
+
|
51
|
+
## deserialize_value with symbolize: true returns symbol keys
|
52
|
+
@symbol_result = @test_obj.deserialize_value(@raw_json, symbolize: true)
|
44
53
|
@symbol_result.keys
|
45
54
|
#=> [:name, :age, :nested]
|
46
55
|
|
@@ -48,21 +57,12 @@ end
|
|
48
57
|
@symbol_result[:nested].keys
|
49
58
|
#=> [:theme]
|
50
59
|
|
51
|
-
##
|
52
|
-
@string_result = @test_obj.deserialize_value(@raw_json, symbolize: false)
|
53
|
-
@string_result.keys
|
54
|
-
#=> ["name", "age", "nested"]
|
55
|
-
|
56
|
-
## Nested hash in string result also has string keys
|
57
|
-
@string_result['nested'].keys
|
58
|
-
#=> ["theme"]
|
59
|
-
|
60
|
-
## Values are preserved correctly in both cases
|
60
|
+
## Values are preserved correctly with symbol keys
|
61
61
|
@symbol_result[:name]
|
62
62
|
#=> "John"
|
63
63
|
|
64
|
-
##
|
65
|
-
@
|
64
|
+
## Values are preserved correctly with string keys
|
65
|
+
@string_result_default['name']
|
66
66
|
#=> "John"
|
67
67
|
|
68
68
|
## Arrays are handled correctly too
|
@@ -71,27 +71,27 @@ end
|
|
71
71
|
@array_json = @test_obj.hget('config')
|
72
72
|
#=> "[{\"item\":\"value\"},\"string\",123]"
|
73
73
|
|
74
|
+
## Array with default (symbolize: false) keeps hash keys as strings
|
75
|
+
@string_array_default = @test_obj.deserialize_value(@array_json)
|
76
|
+
@string_array_default[0].keys
|
77
|
+
#=> ["item"]
|
78
|
+
|
74
79
|
## Array with symbolize: true converts hash keys to symbols
|
75
|
-
@symbol_array = @test_obj.deserialize_value(@array_json)
|
80
|
+
@symbol_array = @test_obj.deserialize_value(@array_json, symbolize: true)
|
76
81
|
@symbol_array[0].keys
|
77
82
|
#=> [:item]
|
78
83
|
|
79
|
-
##
|
80
|
-
@string_array = @test_obj.deserialize_value(@array_json, symbolize: false)
|
81
|
-
@string_array[0].keys
|
82
|
-
#=> ["item"]
|
83
|
-
|
84
|
-
## Non-hash/array values are returned as-is
|
84
|
+
## JSON-encoded string is parsed correctly
|
85
85
|
@test_obj.deserialize_value('"just a string"')
|
86
|
-
#=> "
|
86
|
+
#=> "just a string"
|
87
87
|
|
88
|
-
## Non-
|
88
|
+
## Non-JSON string returns as-is
|
89
89
|
@test_obj.deserialize_value('just a string')
|
90
90
|
#=> "just a string"
|
91
91
|
|
92
|
-
##
|
92
|
+
## JSON number is parsed to Integer
|
93
93
|
@test_obj.deserialize_value('42')
|
94
|
-
#=>
|
94
|
+
#=> 42
|
95
95
|
|
96
96
|
## Invalid JSON returns original string
|
97
97
|
@test_obj.deserialize_value('invalid json')
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# try/edge_cases/json_serialization_try.rb
|
2
2
|
|
3
|
-
require_relative '../helpers/test_helpers'
|
3
|
+
require_relative '../support/helpers/test_helpers'
|
4
4
|
|
5
5
|
Familia.debug = false
|
6
6
|
|
@@ -44,7 +44,7 @@ test_obj.simple = 'just a string'
|
|
44
44
|
test_obj.tags = %w[ruby valkey json familia]
|
45
45
|
test_obj.save
|
46
46
|
test_obj.hgetall
|
47
|
-
#=> {"id"=>"json_test_1", "config"=>"{\"theme\":\"dark\",\"notifications\":true,\"settings\":{\"volume\":80}}", "tags"=>"[\"ruby\",\"valkey\",\"json\",\"familia\"]", "simple"=>"just a string"}
|
47
|
+
#=> {"id"=>"\"json_test_1\"", "config"=>"{\"theme\":\"dark\",\"notifications\":true,\"settings\":{\"volume\":80}}", "tags"=>"[\"ruby\",\"valkey\",\"json\",\"familia\"]", "simple"=>"\"just a string\""}
|
48
48
|
|
49
49
|
## Test 4: Hash should be deserialized back to Hash
|
50
50
|
test_obj = JsonTest.new 'any_id_will_do'
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# Edge case tests for deserialize_value with legacy data detection
|
2
|
+
#
|
3
|
+
# Tests the nuanced deserialization that distinguishes between:
|
4
|
+
# - Corrupted JSON (data that looks like JSON but fails to parse)
|
5
|
+
# - Legacy plain strings (data that was never JSON)
|
6
|
+
# - Valid JSON data
|
7
|
+
|
8
|
+
require_relative '../../../lib/familia'
|
9
|
+
require 'logger'
|
10
|
+
require 'stringio'
|
11
|
+
|
12
|
+
# Capture log output for verification
|
13
|
+
@log_output = StringIO.new
|
14
|
+
@original_logger = Familia.instance_variable_get(:@logger)
|
15
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
16
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
17
|
+
|
18
|
+
class TestModel < Familia::Horreum
|
19
|
+
identifier_field :test_id
|
20
|
+
field :test_id
|
21
|
+
field :data
|
22
|
+
end
|
23
|
+
|
24
|
+
@model = TestModel.new(test_id: "test1")
|
25
|
+
|
26
|
+
## Valid JSON number deserializes correctly
|
27
|
+
@result = @model.deserialize_value("123", field_name: :data)
|
28
|
+
@result
|
29
|
+
#=> 123
|
30
|
+
|
31
|
+
## Valid JSON boolean deserializes correctly
|
32
|
+
@result = @model.deserialize_value("true", field_name: :data)
|
33
|
+
@result
|
34
|
+
#=> true
|
35
|
+
|
36
|
+
## Valid JSON string deserializes correctly
|
37
|
+
@result = @model.deserialize_value('"hello"', field_name: :data)
|
38
|
+
@result
|
39
|
+
#=> "hello"
|
40
|
+
|
41
|
+
## Valid JSON array deserializes correctly
|
42
|
+
@result = @model.deserialize_value('[1,2,3]', field_name: :data)
|
43
|
+
@result
|
44
|
+
#=> [1, 2, 3]
|
45
|
+
|
46
|
+
## Valid JSON object deserializes correctly
|
47
|
+
@result = @model.deserialize_value('{"key":"value"}', field_name: :data)
|
48
|
+
@result
|
49
|
+
#=> {"key"=>"value"}
|
50
|
+
|
51
|
+
## Plain string (legacy data) returns as-is
|
52
|
+
@log_output = StringIO.new
|
53
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
54
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
55
|
+
@result = @model.deserialize_value("plain text", field_name: :data)
|
56
|
+
@result
|
57
|
+
#=> "plain text"
|
58
|
+
|
59
|
+
## Legacy data logs at debug level
|
60
|
+
@log_output.rewind
|
61
|
+
@log_content = @log_output.read
|
62
|
+
puts "LOG CONTENT: #{@log_content.inspect}" if ENV['DEBUG']
|
63
|
+
@log_content
|
64
|
+
#=~> /Legacy plain string/
|
65
|
+
|
66
|
+
## Corrupted JSON starting with { logs error
|
67
|
+
@log_output = StringIO.new
|
68
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
69
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
70
|
+
@result = @model.deserialize_value("{broken", field_name: :data)
|
71
|
+
@result
|
72
|
+
#=> "{broken"
|
73
|
+
|
74
|
+
## Corrupted JSON logs at error level
|
75
|
+
@log_output.rewind
|
76
|
+
@log_content = @log_output.read
|
77
|
+
puts "LOG CONTENT: #{@log_content.inspect}" if ENV['DEBUG']
|
78
|
+
@log_content.match?(/Corrupted JSON/)
|
79
|
+
#=> true
|
80
|
+
|
81
|
+
## Corrupted JSON starting with [ logs error
|
82
|
+
@log_output = StringIO.new
|
83
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
84
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
85
|
+
@result = @model.deserialize_value("[1,2,", field_name: :data)
|
86
|
+
@result
|
87
|
+
#=> "[1,2,"
|
88
|
+
|
89
|
+
## Corrupted array logs at error level
|
90
|
+
@log_output.rewind
|
91
|
+
@log_content = @log_output.read
|
92
|
+
@log_content.match?(/Corrupted JSON/)
|
93
|
+
#=> true
|
94
|
+
|
95
|
+
## Corrupted JSON starting with quote logs error
|
96
|
+
@log_output = StringIO.new
|
97
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
98
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
99
|
+
@result = @model.deserialize_value('"unterminated', field_name: :data)
|
100
|
+
@result
|
101
|
+
#=> '"unterminated'
|
102
|
+
|
103
|
+
## Unterminated string logs at error level
|
104
|
+
@log_output.rewind
|
105
|
+
@log_content = @log_output.read
|
106
|
+
@log_content.match?(/Corrupted JSON/)
|
107
|
+
#=> true
|
108
|
+
|
109
|
+
## Corrupted boolean-like value logs error
|
110
|
+
@log_output = StringIO.new
|
111
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
112
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
113
|
+
@result = @model.deserialize_value("true123", field_name: :data)
|
114
|
+
@result
|
115
|
+
#=> "true123"
|
116
|
+
|
117
|
+
## Plain text starting with 'true' is legacy data
|
118
|
+
@log_output.rewind
|
119
|
+
@log_content = @log_output.read
|
120
|
+
@log_content
|
121
|
+
#=~> /Legacy plain string/
|
122
|
+
|
123
|
+
## Field name context appears in error messages
|
124
|
+
@log_output = StringIO.new
|
125
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
126
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
127
|
+
@result = @model.deserialize_value("{broken", field_name: :important_field)
|
128
|
+
@log_output.rewind
|
129
|
+
@log_content = @log_output.read
|
130
|
+
@log_content.match?(/TestModel#important_field/)
|
131
|
+
#=> true
|
132
|
+
|
133
|
+
## dbkey context appears in error messages when available
|
134
|
+
@model.save
|
135
|
+
@log_output = StringIO.new
|
136
|
+
Familia.instance_variable_set(:@logger, Logger.new(@log_output))
|
137
|
+
Familia.instance_variable_get(:@logger).level = Logger::DEBUG
|
138
|
+
@result = @model.deserialize_value("{broken", field_name: :data)
|
139
|
+
@log_output.rewind
|
140
|
+
@log_content = @log_output.read
|
141
|
+
@log_content.match?(/#{Regexp.escape(@model.dbkey)}/)
|
142
|
+
#=> true
|
143
|
+
|
144
|
+
## Empty string returns nil
|
145
|
+
@result = @model.deserialize_value("", field_name: :data)
|
146
|
+
@result
|
147
|
+
#=> nil
|
148
|
+
|
149
|
+
## nil returns nil
|
150
|
+
@result = @model.deserialize_value(nil, field_name: :data)
|
151
|
+
@result
|
152
|
+
#=> nil
|
153
|
+
|
154
|
+
## JSON null deserializes to nil
|
155
|
+
@result = @model.deserialize_value("null", field_name: :data)
|
156
|
+
@result
|
157
|
+
#=> nil
|
158
|
+
|
159
|
+
## Symbolize option works with hash keys
|
160
|
+
@result = @model.deserialize_value('{"name":"test"}', symbolize: true, field_name: :data)
|
161
|
+
@result.keys.first.class
|
162
|
+
#=> Symbol
|
163
|
+
|
164
|
+
## Default keeps string keys
|
165
|
+
@result = @model.deserialize_value('{"name":"test"}', field_name: :data)
|
166
|
+
@result.keys.first.class
|
167
|
+
#=> String
|
168
|
+
|
169
|
+
# Teardown
|
170
|
+
Familia.instance_variable_set(:@logger, @original_logger)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Security tests for the no-cache encryption strategy
|
4
4
|
# These tests verify that we maintain security properties by NOT caching derived keys
|
5
5
|
|
6
|
-
require_relative '../../helpers/test_helpers'
|
6
|
+
require_relative '../../support/helpers/test_helpers'
|
7
7
|
|
8
8
|
test_keys = {
|
9
9
|
v1: Base64.strict_encode64('a' * 32),
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'base64'
|
4
4
|
|
5
|
-
require_relative '../../helpers/test_helpers'
|
5
|
+
require_relative '../../support/helpers/test_helpers'
|
6
6
|
|
7
7
|
# This tryouts file is based on the premise that there is no current key
|
8
8
|
# version set. This is a global setting so if other tryouts rely on
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# try/encryption/encryption_core_try.rb
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
3
|
+
require_relative '../../support/helpers/test_helpers'
|
4
|
+
require_relative '../../../lib/familia/encryption'
|
5
5
|
require 'base64'
|
6
6
|
|
7
7
|
# Test constants will be redefined in each test since variables don't persist
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# try/encryption/secure_memory_handling_try.rb
|
2
2
|
|
3
|
-
require_relative '
|
4
|
-
require_relative '
|
3
|
+
require_relative '../../support/helpers/test_helpers'
|
4
|
+
require_relative '../../../lib/familia/encryption/providers/secure_xchacha20_poly1305_provider'
|
5
5
|
require 'base64'
|
6
6
|
|
7
7
|
# SETUP
|