Module: Familia::Validation::TestHelpers
- Included in:
- FamiliaTestHelpers
- Defined in:
- lib/familia/validation/test_helpers.rb
Overview
Test helper methods for integrating Redis command validation with the tryouts testing framework. Provides easy-to-use assertion methods and automatic setup/cleanup for command validation tests.
Instance Method Summary collapse
-
#any_number ⇒ Object
-
#any_string ⇒ Object
-
#any_value ⇒ Object
-
#assert_atomic_operation(message = nil, &block) ⇒ Object
Assert that a block executes Redis commands atomically.
-
#assert_command_count(expected_count, &block) ⇒ Object
Assert that a specific number of commands were executed.
-
#assert_commands_executed(*expected_commands) ⇒ Object
Assert that specific commands were executed (flexible order).
-
#assert_efficient_commands(&block) ⇒ Object
Assert efficient command usage (no N+1 patterns).
-
#assert_no_redis_commands(&block) ⇒ Object
Assert that no Redis commands were executed.
-
#assert_no_transaction_used(&block) ⇒ Object
Assert that commands were NOT executed within a transaction.
-
#assert_performance_within(max_duration_ms, &block) ⇒ Object
Performance assertion - assert operations complete within time limit.
-
#assert_redis_commands(message = nil, &block) ⇒ Object
Assert that a block executes the expected Redis commands.
-
#assert_transaction_used(&block) ⇒ Object
Assert that commands were executed within a transaction.
-
#capture_redis_commands(&block) ⇒ Object
Capture and return Redis commands without validation.
-
#debug_print_commands(command_sequence = nil) ⇒ Object
Debugging helpers.
-
#debug_print_performance(command_sequence = nil) ⇒ Object
-
#expect_data_type_operation(class_name, identifier, type_name, operation, *args) ⇒ Object
-
#expect_horreum_load(class_name, identifier, fields = []) ⇒ Object
-
#expect_horreum_save(class_name, identifier, fields = {}) ⇒ Object
Helper to create expectation builders for common patterns.
-
#match_command(cmd, *args) ⇒ Object
Matcher helpers for more readable tests.
-
#match_pattern(pattern) ⇒ Object
-
#setup_validation_test ⇒ Object
Setup and teardown helpers for validation tests.
-
#teardown_validation_test ⇒ Object
-
#with_validation_test(&block) ⇒ Object
Wrapper for validation tests with automatic setup/teardown.
Instance Method Details
#any_number ⇒ Object
347 348 349 |
# File 'lib/familia/validation/test_helpers.rb', line 347 def any_number ArgumentMatcher.new(:any_number) end |
#any_string ⇒ Object
343 344 345 |
# File 'lib/familia/validation/test_helpers.rb', line 343 def any_string ArgumentMatcher.new(:any_string) end |
#any_value ⇒ Object
351 352 353 |
# File 'lib/familia/validation/test_helpers.rb', line 351 def any_value ArgumentMatcher.new(:any_value) end |
#assert_atomic_operation(message = nil, &block) ⇒ Object
Assert that a block executes Redis commands atomically
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/familia/validation/test_helpers.rb', line 51 def assert_atomic_operation( = nil, &block) validator = Validator.new(strict_atomicity: true) if block.arity == 1 # Block expects expectations parameter result = validator.validate(&block) else # Block is just execution code - validate atomicity only result = validator.validate_atomicity(&block) end unless result.valid? error_msg = || "Atomic operation validation failed" error_msg += "\n" + result.detailed_report raise ValidationError, error_msg end result.valid? end |
#assert_command_count(expected_count, &block) ⇒ Object
Assert that a specific number of commands were executed
106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/familia/validation/test_helpers.rb', line 106 def assert_command_count(expected_count, &block) CommandRecorder.start_recording block.call if block_given? commands = CommandRecorder.stop_recording actual_count = commands.command_count unless actual_count == expected_count error_msg = "Expected #{expected_count} Redis commands, but #{actual_count} were executed" raise ValidationError, error_msg end true end |
#assert_commands_executed(*expected_commands) ⇒ Object
Assert that specific commands were executed (flexible order)
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/familia/validation/test_helpers.rb', line 72 def assert_commands_executed(*expected_commands) validator = Validator.new CommandRecorder.start_recording yield if block_given? actual_commands = CommandRecorder.stop_recording result = validator.assert_commands_executed(expected_commands, actual_commands) unless result.valid? error_msg = "Expected commands were not executed as specified" error_msg += "\n" + result.detailed_report raise ValidationError, error_msg end result.valid? end |
#assert_efficient_commands(&block) ⇒ Object
Assert efficient command usage (no N+1 patterns)
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/familia/validation/test_helpers.rb', line 175 def assert_efficient_commands(&block) validator = Validator.new(performance_tracking: true) commands = capture_redis_commands(&block) analysis = validator.analyze_performance(commands) if analysis[:efficiency_score] < 70 # Threshold for acceptable efficiency error_msg = "Inefficient Redis command usage detected (score: #{analysis[:efficiency_score]})" if analysis[:potential_n_plus_one].any? error_msg += "\nPotential N+1 patterns:" analysis[:potential_n_plus_one].each do |pattern| error_msg += "\n #{pattern[:command]}: #{pattern[:count]} calls - #{pattern[:suggestion]}" end end raise ValidationError, error_msg end true end |
#assert_no_redis_commands(&block) ⇒ Object
Assert that no Redis commands were executed
91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/familia/validation/test_helpers.rb', line 91 def assert_no_redis_commands(&block) CommandRecorder.start_recording block.call if block_given? commands = CommandRecorder.stop_recording unless commands.command_count == 0 error_msg = "Expected no Redis commands, but #{commands.command_count} were executed:" commands.commands.each { |cmd| error_msg += "\n #{cmd}" } raise ValidationError, error_msg end true end |
#assert_no_transaction_used(&block) ⇒ Object
Assert that commands were NOT executed within a transaction
135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/familia/validation/test_helpers.rb', line 135 def assert_no_transaction_used(&block) CommandRecorder.start_recording block.call if block_given? commands = CommandRecorder.stop_recording unless commands.transaction_count == 0 error_msg = "Expected operations to NOT use transactions, but #{commands.transaction_count} were found" raise ValidationError, error_msg end true end |
#assert_performance_within(max_duration_ms, &block) ⇒ Object
Performance assertion - assert operations complete within time limit
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/familia/validation/test_helpers.rb', line 156 def assert_performance_within(max_duration_ms, &block) start_time = Time.now CommandRecorder.start_recording result = block.call if block_given? commands = CommandRecorder.stop_recording actual_duration_ms = (Time.now - start_time) * 1000 if actual_duration_ms > max_duration_ms error_msg = "Operation took #{actual_duration_ms.round(2)}ms, " \ "expected less than #{max_duration_ms}ms" raise ValidationError, error_msg end result end |
#assert_redis_commands(message = nil, &block) ⇒ Object
Assert that a block executes the expected Redis commands
37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/familia/validation/test_helpers.rb', line 37 def assert_redis_commands( = nil, &block) validator = Validator.new result = validator.validate(&block) unless result.valid? error_msg = || "Redis command validation failed" error_msg += "\n" + result.detailed_report raise ValidationError, error_msg end result.valid? end |
#assert_transaction_used(&block) ⇒ Object
Assert that commands were executed within a transaction
121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/familia/validation/test_helpers.rb', line 121 def assert_transaction_used(&block) CommandRecorder.start_recording block.call if block_given? commands = CommandRecorder.stop_recording unless commands.transaction_count > 0 error_msg = "Expected operations to use transactions, but none were found" raise ValidationError, error_msg end true end |
#capture_redis_commands(&block) ⇒ Object
Capture and return Redis commands without validation
149 150 151 152 153 |
# File 'lib/familia/validation/test_helpers.rb', line 149 def capture_redis_commands(&block) CommandRecorder.start_recording block.call if block_given? CommandRecorder.stop_recording end |
#debug_print_commands(command_sequence = nil) ⇒ Object
Debugging helpers
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/familia/validation/test_helpers.rb', line 273 def debug_print_commands(command_sequence = nil) commands = command_sequence || capture_redis_commands { yield if block_given? } puts "Redis Commands Executed (#{commands.command_count} total):" puts "=" * 50 commands.commands.each_with_index do |cmd, i| prefix = cmd.atomic_command? ? "[TX]" : " " puts "#{prefix} #{i + 1}. #{cmd} (#{cmd.duration_us}µs)" end if commands.transaction_count > 0 puts "\nTransactions (#{commands.transaction_count} total):" commands.transaction_blocks.each_with_index do |tx, i| puts " #{i + 1}. #{tx.command_count} commands" end end puts "=" * 50 end |
#debug_print_performance(command_sequence = nil) ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/familia/validation/test_helpers.rb', line 294 def debug_print_performance(command_sequence = nil) commands = command_sequence || CommandRecorder.current_sequence validator = Validator.new(performance_tracking: true) analysis = validator.analyze_performance(commands) puts "Performance Analysis:" puts "=" * 30 puts "Total Commands: #{analysis[:total_commands]}" puts "Total Duration: #{analysis[:total_duration_ms].round(2)}ms" puts "Average Command Time: #{analysis[:average_command_time_us].round(2)}µs" puts "Efficiency Score: #{analysis[:efficiency_score].round(1)}/100" if analysis[:slowest_commands].any? puts "\nSlowest Commands:" analysis[:slowest_commands].each do |cmd| puts " #{cmd[:command]} (#{cmd[:duration_us]}µs)" end end if analysis[:potential_n_plus_one].any? puts "\nPotential N+1 Patterns:" analysis[:potential_n_plus_one].each do |pattern| puts " #{pattern[:command]}: #{pattern[:count]} calls" end end puts "=" * 30 end |
#expect_data_type_operation(class_name, identifier, type_name, operation, *args) ⇒ Object
265 266 267 268 269 270 |
# File 'lib/familia/validation/test_helpers.rb', line 265 def expect_data_type_operation(class_name, identifier, type_name, operation, *args) dbkey = "#{class_name.to_s.downcase}:#{identifier}:#{type_name}" expectations = CommandExpectations.new expectations.command(operation, dbkey, *args) end |
#expect_horreum_load(class_name, identifier, fields = []) ⇒ Object
250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/familia/validation/test_helpers.rb', line 250 def expect_horreum_load(class_name, identifier, fields = []) dbkey = "#{class_name.to_s.downcase}:#{identifier}:object" expectations = CommandExpectations.new if fields.empty? expectations.hgetall(dbkey) else fields.each do |field| expectations.hget(dbkey, field.to_s) end end expectations end |
#expect_horreum_save(class_name, identifier, fields = {}) ⇒ Object
Helper to create expectation builders for common patterns
239 240 241 242 243 244 245 246 247 248 |
# File 'lib/familia/validation/test_helpers.rb', line 239 def expect_horreum_save(class_name, identifier, fields = {}) dbkey = "#{class_name.to_s.downcase}:#{identifier}:object" expectations = CommandExpectations.new fields.each do |field, value| expectations.hset(dbkey, field.to_s, value.to_s) end expectations end |
#match_command(cmd, *args) ⇒ Object
Matcher helpers for more readable tests
324 325 326 327 328 329 330 |
# File 'lib/familia/validation/test_helpers.rb', line 324 def match_command(cmd, *args) if args.empty? ->(recorded) { recorded.command == cmd.to_s.upcase } else ->(recorded) { recorded.command == cmd.to_s.upcase && recorded.args == args.map(&:to_s) } end end |
#match_pattern(pattern) ⇒ Object
332 333 334 335 336 337 338 339 340 341 |
# File 'lib/familia/validation/test_helpers.rb', line 332 def match_pattern(pattern) case pattern when Regexp ->(recorded) { pattern.match?(recorded.to_s) } when String ->(recorded) { recorded.to_s.include?(pattern) } else pattern end end |
#setup_validation_test ⇒ Object
Setup and teardown helpers for validation tests
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/familia/validation/test_helpers.rb', line 198 def setup_validation_test # Ensure middleware is registered @original_middleware_state = CommandRecorder.recording? # Clear any existing state CommandRecorder.clear if CommandRecorder.recording? # Enable database logging for better debugging @original_logging_state = Familia.enable_database_logging Familia.enable_database_logging = true # Enable command counting @original_counter_state = Familia.enable_database_counter Familia.enable_database_counter = true DatabaseCommandCounter.reset end |
#teardown_validation_test ⇒ Object
216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/familia/validation/test_helpers.rb', line 216 def teardown_validation_test # Stop recording if active CommandRecorder.stop_recording if CommandRecorder.recording? # Restore original states Familia.enable_database_logging = @original_logging_state if @original_logging_state Familia.enable_database_counter = @original_counter_state if @original_counter_state # Reset counters DatabaseCommandCounter.reset end |
#with_validation_test(&block) ⇒ Object
Wrapper for validation tests with automatic setup/teardown
229 230 231 232 233 234 235 236 |
# File 'lib/familia/validation/test_helpers.rb', line 229 def with_validation_test(&block) setup_validation_test begin block.call ensure teardown_validation_test end end |