activesupport 1.2.4 → 8.1.2
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 +7 -0
- data/CHANGELOG.md +505 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +40 -0
- data/lib/active_support/actionable_error.rb +50 -0
- data/lib/active_support/all.rb +5 -0
- data/lib/active_support/array_inquirer.rb +50 -0
- data/lib/active_support/backtrace_cleaner.rb +234 -0
- data/lib/active_support/benchmark.rb +21 -0
- data/lib/active_support/benchmarkable.rb +53 -0
- data/lib/active_support/broadcast_logger.rb +238 -0
- data/lib/active_support/builder.rb +8 -0
- data/lib/active_support/cache/coder.rb +153 -0
- data/lib/active_support/cache/entry.rb +134 -0
- data/lib/active_support/cache/file_store.rb +244 -0
- data/lib/active_support/cache/mem_cache_store.rb +288 -0
- data/lib/active_support/cache/memory_store.rb +264 -0
- data/lib/active_support/cache/null_store.rb +62 -0
- data/lib/active_support/cache/redis_cache_store.rb +498 -0
- data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
- data/lib/active_support/cache/strategy/local_cache.rb +246 -0
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
- data/lib/active_support/cache.rb +1170 -0
- data/lib/active_support/callbacks.rb +960 -0
- data/lib/active_support/class_attribute.rb +33 -0
- data/lib/active_support/code_generator.rb +79 -0
- data/lib/active_support/concern.rb +217 -0
- data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
- data/lib/active_support/concurrency/null_lock.rb +13 -0
- data/lib/active_support/concurrency/share_lock.rb +225 -0
- data/lib/active_support/concurrency/thread_monitor.rb +55 -0
- data/lib/active_support/configurable.rb +193 -0
- data/lib/active_support/configuration_file.rb +60 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/array/access.rb +100 -0
- data/lib/active_support/core_ext/array/conversions.rb +209 -26
- data/lib/active_support/core_ext/array/extract.rb +21 -0
- data/lib/active_support/core_ext/array/extract_options.rb +31 -0
- data/lib/active_support/core_ext/array/grouping.rb +109 -0
- data/lib/active_support/core_ext/array/inquiry.rb +19 -0
- data/lib/active_support/core_ext/array/wrap.rb +48 -0
- data/lib/active_support/core_ext/array.rb +8 -4
- data/lib/active_support/core_ext/benchmark.rb +6 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
- data/lib/active_support/core_ext/big_decimal.rb +3 -0
- data/lib/active_support/core_ext/class/attribute.rb +137 -0
- data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
- data/lib/active_support/core_ext/class/subclasses.rb +24 -0
- data/lib/active_support/core_ext/class.rb +4 -0
- data/lib/active_support/core_ext/date/acts_like.rb +10 -0
- data/lib/active_support/core_ext/date/blank.rb +18 -0
- data/lib/active_support/core_ext/date/calculations.rb +161 -0
- data/lib/active_support/core_ext/date/conversions.rb +95 -28
- data/lib/active_support/core_ext/date/zones.rb +8 -0
- data/lib/active_support/core_ext/date.rb +6 -5
- data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
- data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
- data/lib/active_support/core_ext/date_time/blank.rb +18 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
- data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
- data/lib/active_support/core_ext/date_time.rb +7 -0
- data/lib/active_support/core_ext/digest/uuid.rb +76 -0
- data/lib/active_support/core_ext/digest.rb +3 -0
- data/lib/active_support/core_ext/enumerable.rb +277 -7
- data/lib/active_support/core_ext/erb/util.rb +201 -0
- data/lib/active_support/core_ext/file/atomic.rb +72 -0
- data/lib/active_support/core_ext/file.rb +3 -0
- data/lib/active_support/core_ext/hash/conversions.rb +262 -0
- data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
- data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
- data/lib/active_support/core_ext/hash/except.rb +12 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
- data/lib/active_support/core_ext/hash/keys.rb +134 -44
- data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
- data/lib/active_support/core_ext/hash/slice.rb +27 -0
- data/lib/active_support/core_ext/hash.rb +9 -8
- data/lib/active_support/core_ext/integer/inflections.rb +29 -13
- data/lib/active_support/core_ext/integer/multiple.rb +12 -0
- data/lib/active_support/core_ext/integer/time.rb +22 -0
- data/lib/active_support/core_ext/integer.rb +4 -6
- data/lib/active_support/core_ext/kernel/concern.rb +14 -0
- data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
- data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
- data/lib/active_support/core_ext/kernel.rb +4 -78
- data/lib/active_support/core_ext/load_error.rb +6 -35
- data/lib/active_support/core_ext/module/aliasing.rb +31 -0
- data/lib/active_support/core_ext/module/anonymous.rb +30 -0
- data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
- data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
- data/lib/active_support/core_ext/module/concerning.rb +140 -0
- data/lib/active_support/core_ext/module/delegation.rb +225 -0
- data/lib/active_support/core_ext/module/deprecation.rb +25 -0
- data/lib/active_support/core_ext/module/introspection.rb +65 -0
- data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
- data/lib/active_support/core_ext/module/remove_method.rb +17 -0
- data/lib/active_support/core_ext/module.rb +13 -0
- data/lib/active_support/core_ext/name_error.rb +59 -0
- data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
- data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
- data/lib/active_support/core_ext/numeric/time.rb +64 -57
- data/lib/active_support/core_ext/numeric.rb +4 -6
- data/lib/active_support/core_ext/object/acts_like.rb +45 -0
- data/lib/active_support/core_ext/object/blank.rb +199 -0
- data/lib/active_support/core_ext/object/conversions.rb +6 -0
- data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
- data/lib/active_support/core_ext/object/duplicable.rb +69 -0
- data/lib/active_support/core_ext/object/inclusion.rb +37 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
- data/lib/active_support/core_ext/object/json.rb +267 -0
- data/lib/active_support/core_ext/object/to_param.rb +3 -0
- data/lib/active_support/core_ext/object/to_query.rb +93 -0
- data/lib/active_support/core_ext/object/try.rb +158 -0
- data/lib/active_support/core_ext/object/with.rb +46 -0
- data/lib/active_support/core_ext/object/with_options.rb +101 -0
- data/lib/active_support/core_ext/object.rb +17 -0
- data/lib/active_support/core_ext/pathname/blank.rb +20 -0
- data/lib/active_support/core_ext/pathname/existence.rb +23 -0
- data/lib/active_support/core_ext/pathname.rb +4 -0
- data/lib/active_support/core_ext/range/compare_range.rb +57 -0
- data/lib/active_support/core_ext/range/conversions.rb +58 -17
- data/lib/active_support/core_ext/range/overlap.rb +40 -0
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +5 -4
- data/lib/active_support/core_ext/regexp.rb +14 -0
- data/lib/active_support/core_ext/securerandom.rb +57 -0
- data/lib/active_support/core_ext/string/access.rb +93 -56
- data/lib/active_support/core_ext/string/behavior.rb +8 -0
- data/lib/active_support/core_ext/string/conversions.rb +57 -16
- data/lib/active_support/core_ext/string/exclude.rb +13 -0
- data/lib/active_support/core_ext/string/filters.rb +151 -0
- data/lib/active_support/core_ext/string/indent.rb +45 -0
- data/lib/active_support/core_ext/string/inflections.rb +297 -54
- data/lib/active_support/core_ext/string/inquiry.rb +16 -0
- data/lib/active_support/core_ext/string/multibyte.rb +67 -0
- data/lib/active_support/core_ext/string/output_safety.rb +235 -0
- data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
- data/lib/active_support/core_ext/string/strip.rb +27 -0
- data/lib/active_support/core_ext/string/zones.rb +16 -0
- data/lib/active_support/core_ext/string.rb +14 -10
- data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
- data/lib/active_support/core_ext/symbol.rb +3 -0
- data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
- data/lib/active_support/core_ext/time/acts_like.rb +10 -0
- data/lib/active_support/core_ext/time/calculations.rb +358 -153
- data/lib/active_support/core_ext/time/compatibility.rb +15 -0
- data/lib/active_support/core_ext/time/conversions.rb +69 -30
- data/lib/active_support/core_ext/time/zones.rb +97 -0
- data/lib/active_support/core_ext/time.rb +6 -6
- data/lib/active_support/core_ext.rb +5 -1
- data/lib/active_support/current_attributes/test_helper.rb +13 -0
- data/lib/active_support/current_attributes.rb +243 -0
- data/lib/active_support/deep_mergeable.rb +53 -0
- data/lib/active_support/delegation.rb +183 -0
- data/lib/active_support/dependencies/autoload.rb +72 -0
- data/lib/active_support/dependencies/interlock.rb +55 -0
- data/lib/active_support/dependencies/require_dependency.rb +28 -0
- data/lib/active_support/dependencies.rb +84 -222
- data/lib/active_support/deprecation/behaviors.rb +148 -0
- data/lib/active_support/deprecation/constant_accessor.rb +74 -0
- data/lib/active_support/deprecation/deprecators.rb +104 -0
- data/lib/active_support/deprecation/disallowed.rb +54 -0
- data/lib/active_support/deprecation/method_wrappers.rb +68 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
- data/lib/active_support/deprecation/reporting.rb +162 -0
- data/lib/active_support/deprecation.rb +81 -0
- data/lib/active_support/deprecator.rb +7 -0
- data/lib/active_support/descendants_tracker.rb +112 -0
- data/lib/active_support/digest.rb +22 -0
- data/lib/active_support/duration/iso8601_parser.rb +123 -0
- data/lib/active_support/duration/iso8601_serializer.rb +64 -0
- data/lib/active_support/duration.rb +524 -0
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/encrypted_configuration.rb +126 -0
- data/lib/active_support/encrypted_file.rb +133 -0
- data/lib/active_support/environment_inquirer.rb +40 -0
- data/lib/active_support/error_reporter/test_helper.rb +15 -0
- data/lib/active_support/error_reporter.rb +318 -0
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +592 -0
- data/lib/active_support/evented_file_update_checker.rb +185 -0
- data/lib/active_support/execution_context/test_helper.rb +13 -0
- data/lib/active_support/execution_context.rb +110 -0
- data/lib/active_support/execution_wrapper.rb +150 -0
- data/lib/active_support/executor/test_helper.rb +7 -0
- data/lib/active_support/executor.rb +8 -0
- data/lib/active_support/file_update_checker.rb +166 -0
- data/lib/active_support/fork_tracker.rb +43 -0
- data/lib/active_support/gem_version.rb +17 -0
- data/lib/active_support/gzip.rb +41 -0
- data/lib/active_support/hash_with_indifferent_access.rb +464 -0
- data/lib/active_support/html_safe_translation.rb +56 -0
- data/lib/active_support/i18n.rb +17 -0
- data/lib/active_support/i18n_railtie.rb +140 -0
- data/lib/active_support/inflections.rb +68 -49
- data/lib/active_support/inflector/inflections.rb +290 -0
- data/lib/active_support/inflector/methods.rb +387 -0
- data/lib/active_support/inflector/transliterate.rb +147 -0
- data/lib/active_support/inflector.rb +7 -164
- data/lib/active_support/isolated_execution_state.rb +76 -0
- data/lib/active_support/json/decoding.rb +78 -0
- data/lib/active_support/json/encoding.rb +256 -0
- data/lib/active_support/json.rb +4 -0
- data/lib/active_support/key_generator.rb +66 -0
- data/lib/active_support/lazy_load_hooks.rb +107 -0
- data/lib/active_support/locale/en.rb +33 -0
- data/lib/active_support/locale/en.yml +141 -0
- data/lib/active_support/log_subscriber/test_helper.rb +106 -0
- data/lib/active_support/log_subscriber.rb +188 -0
- data/lib/active_support/logger.rb +55 -0
- data/lib/active_support/logger_silence.rb +21 -0
- data/lib/active_support/logger_thread_safe_level.rb +50 -0
- data/lib/active_support/message_encryptor.rb +374 -0
- data/lib/active_support/message_encryptors.rb +193 -0
- data/lib/active_support/message_pack/cache_serializer.rb +23 -0
- data/lib/active_support/message_pack/extensions.rb +310 -0
- data/lib/active_support/message_pack/serializer.rb +63 -0
- data/lib/active_support/message_pack.rb +50 -0
- data/lib/active_support/message_verifier.rb +377 -0
- data/lib/active_support/message_verifiers.rb +189 -0
- data/lib/active_support/messages/codec.rb +65 -0
- data/lib/active_support/messages/metadata.rb +146 -0
- data/lib/active_support/messages/rotation_configuration.rb +23 -0
- data/lib/active_support/messages/rotation_coordinator.rb +102 -0
- data/lib/active_support/messages/rotator.rb +69 -0
- data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
- data/lib/active_support/multibyte/chars.rb +188 -0
- data/lib/active_support/multibyte/unicode.rb +42 -0
- data/lib/active_support/multibyte.rb +27 -0
- data/lib/active_support/notifications/fanout.rb +467 -0
- data/lib/active_support/notifications/instrumenter.rb +240 -0
- data/lib/active_support/notifications.rb +281 -0
- data/lib/active_support/number_helper/number_converter.rb +190 -0
- data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
- data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
- data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
- data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
- data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
- data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
- data/lib/active_support/number_helper/rounding_helper.rb +46 -0
- data/lib/active_support/number_helper.rb +479 -0
- data/lib/active_support/option_merger.rb +38 -0
- data/lib/active_support/ordered_hash.rb +50 -0
- data/lib/active_support/ordered_options.rb +141 -25
- data/lib/active_support/parameter_filter.rb +157 -0
- data/lib/active_support/rails.rb +26 -0
- data/lib/active_support/railtie.rb +180 -0
- data/lib/active_support/reloader.rb +138 -0
- data/lib/active_support/rescuable.rb +176 -0
- data/lib/active_support/secure_compare_rotator.rb +58 -0
- data/lib/active_support/security_utils.rb +38 -0
- data/lib/active_support/string_inquirer.rb +35 -0
- data/lib/active_support/structured_event_subscriber.rb +99 -0
- data/lib/active_support/subscriber.rb +141 -0
- data/lib/active_support/syntax_error_proxy.rb +67 -0
- data/lib/active_support/tagged_logging.rb +157 -0
- data/lib/active_support/test_case.rb +365 -0
- data/lib/active_support/testing/assertions.rb +369 -0
- data/lib/active_support/testing/autorun.rb +10 -0
- data/lib/active_support/testing/constant_lookup.rb +51 -0
- data/lib/active_support/testing/constant_stubbing.rb +54 -0
- data/lib/active_support/testing/declarative.rb +28 -0
- data/lib/active_support/testing/deprecation.rb +82 -0
- data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
- data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
- data/lib/active_support/testing/file_fixtures.rb +38 -0
- data/lib/active_support/testing/isolation.rb +121 -0
- data/lib/active_support/testing/method_call_assertions.rb +69 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/server.rb +98 -0
- data/lib/active_support/testing/parallelization/worker.rb +107 -0
- data/lib/active_support/testing/parallelization.rb +79 -0
- data/lib/active_support/testing/parallelize_executor.rb +81 -0
- data/lib/active_support/testing/setup_and_teardown.rb +57 -0
- data/lib/active_support/testing/stream.rb +41 -0
- data/lib/active_support/testing/tagged_logging.rb +27 -0
- data/lib/active_support/testing/tests_without_assertions.rb +19 -0
- data/lib/active_support/testing/time_helpers.rb +273 -0
- data/lib/active_support/time.rb +20 -0
- data/lib/active_support/time_with_zone.rb +613 -0
- data/lib/active_support/values/time_zone.rb +599 -158
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +175 -0
- data/lib/active_support/xml_mini/libxml.rb +80 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
- data/lib/active_support/xml_mini/nokogiri.rb +83 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
- data/lib/active_support/xml_mini/rexml.rb +137 -0
- data/lib/active_support/xml_mini.rb +212 -0
- data/lib/active_support.rb +122 -10
- metadata +524 -93
- data/CHANGELOG +0 -283
- data/lib/active_support/binding_of_caller.rb +0 -84
- data/lib/active_support/breakpoint.rb +0 -523
- data/lib/active_support/class_attribute_accessors.rb +0 -57
- data/lib/active_support/class_inheritable_attributes.rb +0 -117
- data/lib/active_support/clean_logger.rb +0 -36
- data/lib/active_support/core_ext/blank.rb +0 -38
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
- data/lib/active_support/core_ext/cgi.rb +0 -5
- data/lib/active_support/core_ext/exception.rb +0 -29
- data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
- data/lib/active_support/core_ext/object_and_class.rb +0 -44
- data/lib/active_support/module_attribute_accessors.rb +0 -57
- data/lib/active_support/whiny_nil.rb +0 -38
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
module ErrorReporterAssertions
|
|
6
|
+
module ErrorCollector # :nodoc:
|
|
7
|
+
@subscribed = false
|
|
8
|
+
@mutex = Mutex.new
|
|
9
|
+
|
|
10
|
+
Report = Struct.new(:error, :handled, :severity, :context, :source, keyword_init: true)
|
|
11
|
+
class Report
|
|
12
|
+
alias_method :handled?, :handled
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def record
|
|
17
|
+
subscribe
|
|
18
|
+
recorders = ActiveSupport::IsolatedExecutionState[:active_support_error_reporter_assertions] ||= []
|
|
19
|
+
reports = []
|
|
20
|
+
recorders << reports
|
|
21
|
+
begin
|
|
22
|
+
yield
|
|
23
|
+
reports
|
|
24
|
+
ensure
|
|
25
|
+
recorders.delete_if { |r| reports.equal?(r) }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def report(error, **kwargs)
|
|
30
|
+
report = Report.new(error: error, **kwargs)
|
|
31
|
+
ActiveSupport::IsolatedExecutionState[:active_support_error_reporter_assertions]&.each do |reports|
|
|
32
|
+
reports << report
|
|
33
|
+
end
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def subscribe
|
|
39
|
+
return if @subscribed
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
return if @subscribed
|
|
42
|
+
|
|
43
|
+
if ActiveSupport.error_reporter
|
|
44
|
+
ActiveSupport.error_reporter.subscribe(self)
|
|
45
|
+
@subscribed = true
|
|
46
|
+
else
|
|
47
|
+
flunk("No error reporter is configured")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Assertion that the block should not cause an exception to be reported
|
|
55
|
+
# to +Rails.error+.
|
|
56
|
+
#
|
|
57
|
+
# Passes if evaluated code in the yielded block reports no exception.
|
|
58
|
+
#
|
|
59
|
+
# assert_no_error_reported do
|
|
60
|
+
# perform_service(param: 'no_exception')
|
|
61
|
+
# end
|
|
62
|
+
def assert_no_error_reported(&block)
|
|
63
|
+
reports = ErrorCollector.record do
|
|
64
|
+
_assert_nothing_raised_or_warn("assert_no_error_reported", &block)
|
|
65
|
+
end
|
|
66
|
+
assert_predicate(reports, :empty?)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Assertion that the block should cause at least one exception to be reported
|
|
70
|
+
# to +Rails.error+.
|
|
71
|
+
#
|
|
72
|
+
# Passes if the evaluated code in the yielded block reports a matching exception.
|
|
73
|
+
#
|
|
74
|
+
# assert_error_reported(IOError) do
|
|
75
|
+
# Rails.error.report(IOError.new("Oops"))
|
|
76
|
+
# end
|
|
77
|
+
#
|
|
78
|
+
# To test further details about the reported exception, you can use the return
|
|
79
|
+
# value.
|
|
80
|
+
#
|
|
81
|
+
# report = assert_error_reported(IOError) do
|
|
82
|
+
# # ...
|
|
83
|
+
# end
|
|
84
|
+
# assert_equal "Oops", report.error.message
|
|
85
|
+
# assert_equal "admin", report.context[:section]
|
|
86
|
+
# assert_equal :warning, report.severity
|
|
87
|
+
# assert_predicate report, :handled?
|
|
88
|
+
def assert_error_reported(error_class = StandardError, &block)
|
|
89
|
+
reports = ErrorCollector.record do
|
|
90
|
+
_assert_nothing_raised_or_warn("assert_error_reported", &block)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if reports.empty?
|
|
94
|
+
assert(false, "Expected a #{error_class.name} to be reported, but there were no errors reported.")
|
|
95
|
+
elsif (report = reports.find { |r| error_class === r.error })
|
|
96
|
+
self.assertions += 1
|
|
97
|
+
report
|
|
98
|
+
else
|
|
99
|
+
message = "Expected a #{error_class.name} to be reported, but none of the " \
|
|
100
|
+
"#{reports.size} reported errors matched: \n" \
|
|
101
|
+
"#{reports.map { |r| r.error.class.name }.join("\n ")}"
|
|
102
|
+
assert(false, message)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Captures reported errors from within the block that match the given
|
|
107
|
+
# error class.
|
|
108
|
+
#
|
|
109
|
+
# reports = capture_error_reports(IOError) do
|
|
110
|
+
# Rails.error.report(IOError.new("Oops"))
|
|
111
|
+
# Rails.error.report(IOError.new("Oh no"))
|
|
112
|
+
# Rails.error.report(StandardError.new)
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
# assert_equal 2, reports.size
|
|
116
|
+
# assert_equal "Oops", reports.first.error.message
|
|
117
|
+
# assert_equal "Oh no", reports.last.error.message
|
|
118
|
+
def capture_error_reports(error_class = StandardError, &block)
|
|
119
|
+
reports = ErrorCollector.record(&block)
|
|
120
|
+
reports.select { |r| error_class === r.error }
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
# Provides test helpers for asserting on ActiveSupport::EventReporter events.
|
|
6
|
+
module EventReporterAssertions
|
|
7
|
+
module EventCollector # :nodoc:
|
|
8
|
+
@subscribed = false
|
|
9
|
+
@mutex = Mutex.new
|
|
10
|
+
|
|
11
|
+
class Event # :nodoc:
|
|
12
|
+
attr_reader :event_data
|
|
13
|
+
|
|
14
|
+
def initialize(event_data)
|
|
15
|
+
@event_data = event_data
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def inspect
|
|
19
|
+
"#{event_data[:name]} (payload: #{event_data[:payload].inspect}, tags: #{event_data[:tags].inspect})"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def matches?(name, payload, tags)
|
|
23
|
+
return false unless name.to_s == event_data[:name]
|
|
24
|
+
|
|
25
|
+
if payload && payload.is_a?(Hash)
|
|
26
|
+
return false unless matches_hash?(payload, :payload)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
return false unless matches_hash?(tags, :tags)
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def matches_hash?(expected_hash, event_key)
|
|
35
|
+
expected_hash.all? do |k, v|
|
|
36
|
+
if v.is_a?(Regexp)
|
|
37
|
+
event_data.dig(event_key, k).to_s.match?(v)
|
|
38
|
+
else
|
|
39
|
+
event_data.dig(event_key, k) == v
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
def emit(event)
|
|
47
|
+
event_recorders&.each do |events|
|
|
48
|
+
events << Event.new(event)
|
|
49
|
+
end
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def record
|
|
54
|
+
subscribe
|
|
55
|
+
events = []
|
|
56
|
+
event_recorders << events
|
|
57
|
+
begin
|
|
58
|
+
yield
|
|
59
|
+
events
|
|
60
|
+
ensure
|
|
61
|
+
event_recorders.delete_if { |r| events.equal?(r) }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
def subscribe
|
|
67
|
+
return if @subscribed
|
|
68
|
+
|
|
69
|
+
@mutex.synchronize do
|
|
70
|
+
unless @subscribed
|
|
71
|
+
if ActiveSupport.event_reporter
|
|
72
|
+
ActiveSupport.event_reporter.subscribe(self)
|
|
73
|
+
@subscribed = true
|
|
74
|
+
else
|
|
75
|
+
raise Minitest::Assertion, "No event reporter is configured"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def event_recorders
|
|
82
|
+
ActiveSupport::IsolatedExecutionState[:active_support_event_reporter_assertions] ||= []
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Asserts that the block does not cause an event to be reported to +Rails.event+.
|
|
88
|
+
#
|
|
89
|
+
# If no name is provided, passes if evaluated code in the yielded block reports no events.
|
|
90
|
+
#
|
|
91
|
+
# assert_no_event_reported do
|
|
92
|
+
# service_that_does_not_report_events.perform
|
|
93
|
+
# end
|
|
94
|
+
#
|
|
95
|
+
# If a name is provided, passes if evaluated code in the yielded block reports no events
|
|
96
|
+
# with that name.
|
|
97
|
+
#
|
|
98
|
+
# assert_no_event_reported("user.created") do
|
|
99
|
+
# service_that_does_not_report_events.perform
|
|
100
|
+
# end
|
|
101
|
+
def assert_no_event_reported(name = nil, payload: {}, tags: {}, &block)
|
|
102
|
+
events = EventCollector.record(&block)
|
|
103
|
+
|
|
104
|
+
if name.nil?
|
|
105
|
+
assert_predicate(events, :empty?)
|
|
106
|
+
else
|
|
107
|
+
matching_event = events.find { |event| event.matches?(name, payload, tags) }
|
|
108
|
+
if matching_event
|
|
109
|
+
message = "Expected no '#{name}' event to be reported, but found:\n " \
|
|
110
|
+
"#{matching_event.inspect}"
|
|
111
|
+
flunk(message)
|
|
112
|
+
end
|
|
113
|
+
assert(true)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Asserts that the block causes an event with the given name to be reported
|
|
118
|
+
# to +Rails.event+.
|
|
119
|
+
#
|
|
120
|
+
# Passes if the evaluated code in the yielded block reports a matching event.
|
|
121
|
+
#
|
|
122
|
+
# assert_event_reported("user.created") do
|
|
123
|
+
# Rails.event.notify("user.created", { id: 123 })
|
|
124
|
+
# end
|
|
125
|
+
#
|
|
126
|
+
# To test further details about the reported event, you can specify payload and tag matchers.
|
|
127
|
+
#
|
|
128
|
+
# assert_event_reported("user.created",
|
|
129
|
+
# payload: { id: 123, name: "John Doe" },
|
|
130
|
+
# tags: { request_id: /[0-9]+/ }
|
|
131
|
+
# ) do
|
|
132
|
+
# Rails.event.tagged(request_id: "123") do
|
|
133
|
+
# Rails.event.notify("user.created", { id: 123, name: "John Doe" })
|
|
134
|
+
# end
|
|
135
|
+
# end
|
|
136
|
+
#
|
|
137
|
+
# The matchers support partial matching - only the specified keys need to match.
|
|
138
|
+
#
|
|
139
|
+
# assert_event_reported("user.created", payload: { id: 123 }) do
|
|
140
|
+
# Rails.event.notify("user.created", { id: 123, name: "John Doe" })
|
|
141
|
+
# end
|
|
142
|
+
def assert_event_reported(name, payload: nil, tags: {}, &block)
|
|
143
|
+
events = EventCollector.record(&block)
|
|
144
|
+
|
|
145
|
+
if events.empty?
|
|
146
|
+
flunk("Expected an event to be reported, but there were no events reported.")
|
|
147
|
+
elsif (event = events.find { |event| event.matches?(name, payload, tags) })
|
|
148
|
+
assert(true)
|
|
149
|
+
event.event_data
|
|
150
|
+
else
|
|
151
|
+
message = "Expected an event to be reported matching:\n " \
|
|
152
|
+
"name: #{name.inspect}\n " \
|
|
153
|
+
"payload: #{payload.inspect}\n " \
|
|
154
|
+
"tags: #{tags.inspect}\n" \
|
|
155
|
+
"but none of the #{events.size} reported events matched:\n " \
|
|
156
|
+
"#{events.map(&:inspect).join("\n ")}"
|
|
157
|
+
flunk(message)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Asserts that the provided events were reported, regardless of order.
|
|
162
|
+
#
|
|
163
|
+
# assert_events_reported([
|
|
164
|
+
# { name: "user.created", payload: { id: 123 } },
|
|
165
|
+
# { name: "email.sent", payload: { to: "user@example.com" } }
|
|
166
|
+
# ]) do
|
|
167
|
+
# create_user_and_send_welcome_email
|
|
168
|
+
# end
|
|
169
|
+
#
|
|
170
|
+
# Supports the same payload and tag matching as +assert_event_reported+.
|
|
171
|
+
#
|
|
172
|
+
# assert_events_reported([
|
|
173
|
+
# {
|
|
174
|
+
# name: "process.started",
|
|
175
|
+
# payload: { id: 123 },
|
|
176
|
+
# tags: { request_id: /[0-9]+/ }
|
|
177
|
+
# },
|
|
178
|
+
# { name: "process.completed" }
|
|
179
|
+
# ]) do
|
|
180
|
+
# Rails.event.tagged(request_id: "456") do
|
|
181
|
+
# start_and_complete_process(123)
|
|
182
|
+
# end
|
|
183
|
+
# end
|
|
184
|
+
def assert_events_reported(expected_events, &block)
|
|
185
|
+
events = EventCollector.record(&block)
|
|
186
|
+
|
|
187
|
+
if events.empty? && expected_events.size > 0
|
|
188
|
+
flunk("Expected #{expected_events.size} events to be reported, but there were no events reported.")
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
events_copy = events.dup
|
|
192
|
+
|
|
193
|
+
expected_events.each do |expected_event|
|
|
194
|
+
name = expected_event[:name]
|
|
195
|
+
payload = expected_event[:payload] || {}
|
|
196
|
+
tags = expected_event[:tags] || {}
|
|
197
|
+
|
|
198
|
+
matching_event_index = events_copy.find_index { |event| event.matches?(name, payload, tags) }
|
|
199
|
+
|
|
200
|
+
if matching_event_index
|
|
201
|
+
events_copy.delete_at(matching_event_index)
|
|
202
|
+
else
|
|
203
|
+
message = "Expected an event to be reported matching:\n " \
|
|
204
|
+
"name: #{name.inspect}\n " \
|
|
205
|
+
"payload: #{payload.inspect}\n " \
|
|
206
|
+
"tags: #{tags.inspect}\n" \
|
|
207
|
+
"but none of the #{events.size} reported events matched:\n " \
|
|
208
|
+
"#{events.map(&:inspect).join("\n ")}"
|
|
209
|
+
flunk(message)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
assert(true)
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Allows debug events to be reported to +Rails.event+ for the duration of a given block.
|
|
217
|
+
#
|
|
218
|
+
# with_debug_event_reporting do
|
|
219
|
+
# service_that_reports_debug_events.perform
|
|
220
|
+
# end
|
|
221
|
+
#
|
|
222
|
+
def with_debug_event_reporting(&block)
|
|
223
|
+
ActiveSupport.event_reporter.with_debug(&block)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
module Testing
|
|
7
|
+
# Adds simple access to sample files called file fixtures.
|
|
8
|
+
# File fixtures are normal files stored in
|
|
9
|
+
# <tt>ActiveSupport::TestCase.file_fixture_path</tt>.
|
|
10
|
+
#
|
|
11
|
+
# File fixtures are represented as +Pathname+ objects.
|
|
12
|
+
# This makes it easy to extract specific information:
|
|
13
|
+
#
|
|
14
|
+
# file_fixture("example.txt").read # get the file's content
|
|
15
|
+
# file_fixture("example.mp3").size # get the file size
|
|
16
|
+
module FileFixtures
|
|
17
|
+
extend ActiveSupport::Concern
|
|
18
|
+
|
|
19
|
+
included do
|
|
20
|
+
class_attribute :file_fixture_path, instance_writer: false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Returns a +Pathname+ to the fixture file named +fixture_name+.
|
|
24
|
+
#
|
|
25
|
+
# Raises +ArgumentError+ if +fixture_name+ can't be found.
|
|
26
|
+
def file_fixture(fixture_name)
|
|
27
|
+
path = Pathname.new(File.join(file_fixture_path, fixture_name))
|
|
28
|
+
|
|
29
|
+
if path.exist?
|
|
30
|
+
path
|
|
31
|
+
else
|
|
32
|
+
msg = "the directory '%s' does not contain a file named '%s'"
|
|
33
|
+
raise ArgumentError, msg % [file_fixture_path, fixture_name]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/testing/parallelize_executor"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
module Testing
|
|
7
|
+
module Isolation
|
|
8
|
+
SubprocessCrashed = Class.new(StandardError)
|
|
9
|
+
|
|
10
|
+
def self.included(klass) # :nodoc:
|
|
11
|
+
klass.class_eval do
|
|
12
|
+
parallelize_me! unless Minitest.parallel_executor.is_a?(ActiveSupport::Testing::ParallelizeExecutor)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.forking_env?
|
|
17
|
+
!ENV["NO_FORK"] && Process.respond_to?(:fork)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def run
|
|
21
|
+
status, serialized = run_in_isolation do
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
unless status&.success?
|
|
26
|
+
error = SubprocessCrashed.new("Subprocess exited with an error: #{status.inspect}\noutput: #{serialized.inspect}")
|
|
27
|
+
error.set_backtrace(caller)
|
|
28
|
+
self.failures << Minitest::UnexpectedError.new(error)
|
|
29
|
+
return defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Marshal.load(serialized)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module Forking
|
|
36
|
+
def run_in_isolation(&blk)
|
|
37
|
+
IO.pipe do |read, write|
|
|
38
|
+
read.binmode
|
|
39
|
+
write.binmode
|
|
40
|
+
|
|
41
|
+
pid = fork do
|
|
42
|
+
read.close
|
|
43
|
+
yield
|
|
44
|
+
begin
|
|
45
|
+
if error?
|
|
46
|
+
failures.map! { |e|
|
|
47
|
+
begin
|
|
48
|
+
Marshal.dump e
|
|
49
|
+
e
|
|
50
|
+
rescue TypeError
|
|
51
|
+
ex = Exception.new e.message
|
|
52
|
+
ex.set_backtrace e.backtrace
|
|
53
|
+
Minitest::UnexpectedError.new ex
|
|
54
|
+
end
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
|
58
|
+
result = Marshal.dump(test_result)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
write.puts [result].pack("m")
|
|
62
|
+
exit!(0)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
write.close
|
|
66
|
+
result = read.read
|
|
67
|
+
_, status = Process.wait2(pid)
|
|
68
|
+
return status, result.unpack1("m")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module Subprocess
|
|
74
|
+
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
|
|
75
|
+
|
|
76
|
+
# Complicated H4X to get this working in Windows / JRuby with
|
|
77
|
+
# no forking.
|
|
78
|
+
def run_in_isolation(&blk)
|
|
79
|
+
require "tempfile"
|
|
80
|
+
|
|
81
|
+
if ENV["ISOLATION_TEST"]
|
|
82
|
+
yield
|
|
83
|
+
test_result = defined?(Minitest::Result) ? Minitest::Result.from(self) : dup
|
|
84
|
+
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
|
|
85
|
+
file.puts [Marshal.dump(test_result)].pack("m")
|
|
86
|
+
end
|
|
87
|
+
exit!(0)
|
|
88
|
+
else
|
|
89
|
+
Tempfile.open("isolation") do |tmpfile|
|
|
90
|
+
env = {
|
|
91
|
+
"ISOLATION_TEST" => self.class.name,
|
|
92
|
+
"ISOLATION_OUTPUT" => tmpfile.path
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
test_opts = "-n#{self.class.name}##{name}"
|
|
96
|
+
|
|
97
|
+
load_path_args = []
|
|
98
|
+
$-I.each do |p|
|
|
99
|
+
load_path_args << "-I"
|
|
100
|
+
load_path_args << File.expand_path(p)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
child = IO.popen([env, Gem.ruby, *load_path_args, $0, *ORIG_ARGV, test_opts])
|
|
104
|
+
|
|
105
|
+
status = nil
|
|
106
|
+
begin
|
|
107
|
+
_, status = Process.wait2(child.pid)
|
|
108
|
+
rescue Errno::ECHILD # The child process may exit before we wait
|
|
109
|
+
nil
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
return status, tmpfile.read.unpack1("m")
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
include forking_env? ? Forking : Subprocess
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest/mock"
|
|
4
|
+
|
|
5
|
+
module ActiveSupport
|
|
6
|
+
module Testing
|
|
7
|
+
module MethodCallAssertions # :nodoc:
|
|
8
|
+
private
|
|
9
|
+
def assert_called(object, method_name, message = nil, times: 1, returns: nil, &block)
|
|
10
|
+
times_called = 0
|
|
11
|
+
|
|
12
|
+
object.stub(method_name, proc { times_called += 1; returns }, &block)
|
|
13
|
+
|
|
14
|
+
error = "Expected #{method_name} to be called #{times} times, " \
|
|
15
|
+
"but was called #{times_called} times"
|
|
16
|
+
error = "#{message}.\n#{error}" if message
|
|
17
|
+
assert_equal times, times_called, error
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def assert_called_with(object, method_name, args, returns: false, **kwargs, &block)
|
|
21
|
+
mock = Minitest::Mock.new
|
|
22
|
+
expect_called_with(mock, args, returns: returns, **kwargs)
|
|
23
|
+
|
|
24
|
+
object.stub(method_name, mock, &block)
|
|
25
|
+
|
|
26
|
+
assert_mock(mock)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def assert_not_called(object, method_name, message = nil, &block)
|
|
30
|
+
assert_called(object, method_name, message, times: 0, &block)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def expect_called_with(mock, args, returns: false, **kwargs)
|
|
34
|
+
mock.expect(:call, returns, args, **kwargs)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def assert_called_on_instance_of(klass, method_name, message = nil, times: 1, returns: nil)
|
|
38
|
+
times_called = 0
|
|
39
|
+
klass.define_method("stubbed_#{method_name}") do |*|
|
|
40
|
+
times_called += 1
|
|
41
|
+
|
|
42
|
+
returns
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
klass.alias_method "original_#{method_name}", method_name
|
|
46
|
+
klass.alias_method method_name, "stubbed_#{method_name}"
|
|
47
|
+
|
|
48
|
+
yield
|
|
49
|
+
|
|
50
|
+
error = "Expected #{method_name} to be called #{times} times, but was called #{times_called} times"
|
|
51
|
+
error = "#{message}.\n#{error}" if message
|
|
52
|
+
|
|
53
|
+
assert_equal times, times_called, error
|
|
54
|
+
ensure
|
|
55
|
+
klass.alias_method method_name, "original_#{method_name}"
|
|
56
|
+
klass.undef_method "original_#{method_name}"
|
|
57
|
+
klass.undef_method "stubbed_#{method_name}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def assert_not_called_on_instance_of(klass, method_name, message = nil, &block)
|
|
61
|
+
assert_called_on_instance_of(klass, method_name, message, times: 0, &block)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def stub_any_instance(klass, instance: klass.new)
|
|
65
|
+
klass.stub(:new, instance) { yield instance }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Testing
|
|
5
|
+
module NotificationAssertions
|
|
6
|
+
# Assert a notification was emitted with a given +pattern+ and optional +payload+.
|
|
7
|
+
#
|
|
8
|
+
# You can assert that a notification was emitted by passing a pattern, which accepts
|
|
9
|
+
# either a string or regexp, an optional payload, and a block. While the block
|
|
10
|
+
# is executed, if a matching notification is emitted, the assertion will pass
|
|
11
|
+
# and the notification will be returned.
|
|
12
|
+
#
|
|
13
|
+
# Note that the payload is matched as a subset, meaning that the notification must
|
|
14
|
+
# contain at least the specified keys and values, but may contain additional ones.
|
|
15
|
+
#
|
|
16
|
+
# assert_notification("post.submitted", title: "Cool Post") do
|
|
17
|
+
# post.submit(title: "Cool Post", body: "Cool Body") # => emits matching notification
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# Using the returned notification, you can make more customized assertions.
|
|
21
|
+
#
|
|
22
|
+
# notification = assert_notification("post.submitted", title: "Cool Post") do
|
|
23
|
+
# ActiveSupport::Notifications.instrument("post.submitted", title: "Cool Post", body: Body.new("Cool Body"))
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# assert_instance_of(Body, notification.payload[:body])
|
|
27
|
+
#
|
|
28
|
+
def assert_notification(pattern, payload = nil, &block)
|
|
29
|
+
notifications = capture_notifications(pattern, &block)
|
|
30
|
+
assert_not_empty(notifications, "No #{pattern} notifications were found")
|
|
31
|
+
|
|
32
|
+
return notifications.first if payload.nil?
|
|
33
|
+
|
|
34
|
+
notification = notifications.find { |notification| notification.payload.slice(*payload.keys) == payload }
|
|
35
|
+
assert_not_nil(notification, "No #{pattern} notification with payload #{payload} was found")
|
|
36
|
+
|
|
37
|
+
notification
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Assert the number of notifications emitted with a given +pattern+.
|
|
41
|
+
#
|
|
42
|
+
# You can assert the number of notifications emitted by passing a pattern, which accepts
|
|
43
|
+
# either a string or regexp, a count, and a block. While the block is executed,
|
|
44
|
+
# the number of matching notifications emitted will be counted. After the block's
|
|
45
|
+
# execution completes, the assertion will pass if the count matches.
|
|
46
|
+
#
|
|
47
|
+
# assert_notifications_count("post.submitted", 1) do
|
|
48
|
+
# post.submit(title: "Cool Post") # => emits matching notification
|
|
49
|
+
# end
|
|
50
|
+
#
|
|
51
|
+
def assert_notifications_count(pattern, count, &block)
|
|
52
|
+
actual_count = capture_notifications(pattern, &block).count
|
|
53
|
+
assert_equal(count, actual_count, "Expected #{count} instead of #{actual_count} notifications for #{pattern}")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Assert no notifications were emitted for a given +pattern+.
|
|
57
|
+
#
|
|
58
|
+
# You can assert no notifications were emitted by passing a pattern, which accepts
|
|
59
|
+
# either a string or regexp, and a block. While the block is executed, if no
|
|
60
|
+
# matching notifications are emitted, the assertion will pass.
|
|
61
|
+
#
|
|
62
|
+
# assert_no_notifications("post.submitted") do
|
|
63
|
+
# post.destroy # => emits non-matching notification
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
66
|
+
def assert_no_notifications(pattern = nil, &block)
|
|
67
|
+
notifications = capture_notifications(pattern, &block)
|
|
68
|
+
error_message = if pattern
|
|
69
|
+
"Expected no notifications for #{pattern} but found #{notifications.size}"
|
|
70
|
+
else
|
|
71
|
+
"Expected no notifications but found #{notifications.size}"
|
|
72
|
+
end
|
|
73
|
+
assert_empty(notifications, error_message)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Capture emitted notifications, optionally filtered by a +pattern+.
|
|
77
|
+
#
|
|
78
|
+
# You can capture emitted notifications, optionally filtered by a pattern,
|
|
79
|
+
# which accepts either a string or regexp, and a block.
|
|
80
|
+
#
|
|
81
|
+
# notifications = capture_notifications("post.submitted") do
|
|
82
|
+
# post.submit(title: "Cool Post") # => emits matching notification
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
def capture_notifications(pattern = nil, &block)
|
|
86
|
+
notifications = []
|
|
87
|
+
ActiveSupport::Notifications.subscribed(->(n) { notifications << n }, pattern, &block)
|
|
88
|
+
notifications
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|