julewire-core 1.0.1 → 1.1.0

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.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/docs/configuration.md +14 -7
  4. data/docs/context-and-propagation.md +20 -0
  5. data/docs/contracts.md +23 -49
  6. data/docs/extensions-and-api.md +78 -82
  7. data/docs/health-schema.md +12 -2
  8. data/docs/internals.md +21 -20
  9. data/docs/outputs-and-lifecycle.md +10 -6
  10. data/docs/records-and-data-policy.md +36 -5
  11. data/lib/julewire/core/cli/doctor.rb +20 -16
  12. data/lib/julewire/core/cli/line_helpers.rb +23 -12
  13. data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
  14. data/lib/julewire/core/cli/log_formats.rb +9 -9
  15. data/lib/julewire/core/cli/tail.rb +19 -14
  16. data/lib/julewire/core/cli/transcode.rb +4 -5
  17. data/lib/julewire/core/cli.rb +1 -1
  18. data/lib/julewire/core/configuration.rb +1 -1
  19. data/lib/julewire/core/context_store.rb +51 -87
  20. data/lib/julewire/core/destinations/chaos_output.rb +2 -2
  21. data/lib/julewire/core/destinations/collection.rb +8 -16
  22. data/lib/julewire/core/destinations/definition.rb +1 -9
  23. data/lib/julewire/core/destinations/destination.rb +13 -63
  24. data/lib/julewire/core/destinations/processor_handling.rb +78 -0
  25. data/lib/julewire/core/destinations/registry.rb +1 -1
  26. data/lib/julewire/core/destinations/sink.rb +0 -1
  27. data/lib/julewire/core/destinations/synchronized_output.rb +14 -13
  28. data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
  29. data/lib/julewire/core/destinations/write_step.rb +5 -5
  30. data/lib/julewire/core/destinations.rb +0 -7
  31. data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
  32. data/lib/julewire/core/diagnostics/doctor.rb +16 -9
  33. data/lib/julewire/core/diagnostics/failure_snapshot.rb +6 -2
  34. data/lib/julewire/core/diagnostics/health.rb +41 -57
  35. data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
  36. data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
  37. data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
  38. data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
  39. data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
  40. data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
  41. data/lib/julewire/core/diagnostics/tail.rb +18 -22
  42. data/lib/julewire/core/execution/boundary.rb +2 -8
  43. data/lib/julewire/core/execution/handle.rb +12 -17
  44. data/lib/julewire/core/execution/lineage.rb +16 -56
  45. data/lib/julewire/core/execution/measurement_handle.rb +6 -10
  46. data/lib/julewire/core/execution/scope.rb +15 -20
  47. data/lib/julewire/core/execution/scope_fields.rb +3 -12
  48. data/lib/julewire/core/execution/scope_identity.rb +1 -9
  49. data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
  50. data/lib/julewire/core/execution/summary_state.rb +16 -19
  51. data/lib/julewire/core/execution/view.rb +8 -17
  52. data/lib/julewire/core/facade_methods.rb +67 -65
  53. data/lib/julewire/core/fields/attribute_keys.rb +0 -2
  54. data/lib/julewire/core/fields/bags.rb +23 -27
  55. data/lib/julewire/core/fields/field_set.rb +29 -19
  56. data/lib/julewire/core/fields/field_stack.rb +68 -134
  57. data/lib/julewire/core/fields/internal/deletion.rb +6 -9
  58. data/lib/julewire/core/fields/internal.rb +19 -38
  59. data/lib/julewire/core/fields/lookup.rb +9 -13
  60. data/lib/julewire/core/fields/section_proxy.rb +1 -9
  61. data/lib/julewire/core/fields/stack_set.rb +7 -12
  62. data/lib/julewire/core/fields/static_labels.rb +1 -1
  63. data/lib/julewire/core/integration/configurable.rb +4 -3
  64. data/lib/julewire/core/integration/destination_health.rb +17 -7
  65. data/lib/julewire/core/integration/event_subscriber.rb +1 -1
  66. data/lib/julewire/core/integration/facade.rb +12 -15
  67. data/lib/julewire/core/integration/fork_hooks.rb +12 -36
  68. data/lib/julewire/core/integration/health.rb +0 -3
  69. data/lib/julewire/core/integration/ivar_state.rb +0 -5
  70. data/lib/julewire/core/integration/protocol.rb +20 -0
  71. data/lib/julewire/core/integration/settings.rb +4 -4
  72. data/lib/julewire/core/integration/subscriber_install.rb +2 -2
  73. data/lib/julewire/core/integration/subscription.rb +3 -2
  74. data/lib/julewire/core/integration/values.rb +0 -17
  75. data/lib/julewire/core/local_storage.rb +3 -19
  76. data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
  77. data/lib/julewire/core/processing/level_threshold.rb +7 -14
  78. data/lib/julewire/core/processing/match.rb +6 -4
  79. data/lib/julewire/core/processing/pipeline.rb +46 -40
  80. data/lib/julewire/core/processing/processor_chain.rb +22 -11
  81. data/lib/julewire/core/processing/processor_registry.rb +3 -4
  82. data/lib/julewire/core/processing/record_field_transform.rb +0 -4
  83. data/lib/julewire/core/processing/sampling.rb +1 -3
  84. data/lib/julewire/core/processing.rb +2 -2
  85. data/lib/julewire/core/propagation/carrier.rb +14 -14
  86. data/lib/julewire/core/propagation.rb +15 -2
  87. data/lib/julewire/core/records/build_input.rb +75 -0
  88. data/lib/julewire/core/records/console_formatter.rb +6 -6
  89. data/lib/julewire/core/records/deconstruct.rb +1 -1
  90. data/lib/julewire/core/records/display_message.rb +2 -4
  91. data/lib/julewire/core/records/draft.rb +125 -262
  92. data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
  93. data/lib/julewire/core/records/metadata.rb +4 -3
  94. data/lib/julewire/core/records/public_projection.rb +2 -2
  95. data/lib/julewire/core/records/raw_input.rb +1 -1
  96. data/lib/julewire/core/records/record.rb +19 -37
  97. data/lib/julewire/core/records/severity.rb +7 -15
  98. data/lib/julewire/core/runtime.rb +67 -71
  99. data/lib/julewire/core/runtime_registry.rb +20 -15
  100. data/lib/julewire/core/runtime_state.rb +5 -3
  101. data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
  102. data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
  103. data/lib/julewire/core/serialization/bounded_transform.rb +2 -12
  104. data/lib/julewire/core/serialization/bounded_traversal.rb +49 -66
  105. data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
  106. data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
  107. data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
  108. data/lib/julewire/core/serialization/exception_shape.rb +7 -6
  109. data/lib/julewire/core/serialization/json_encoder.rb +10 -13
  110. data/lib/julewire/core/serialization/serializer.rb +23 -50
  111. data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
  112. data/lib/julewire/core/serialization/text_encoder.rb +5 -5
  113. data/lib/julewire/core/serialization/truncation_metadata.rb +63 -48
  114. data/lib/julewire/core/serialization/value_copy.rb +60 -227
  115. data/lib/julewire/core/serialization/value_traversal.rb +10 -127
  116. data/lib/julewire/core/testing.rb +6 -56
  117. data/lib/julewire/core/validation.rb +2 -3
  118. data/lib/julewire/core/version.rb +1 -1
  119. data/lib/julewire/core.rb +2 -7
  120. metadata +6 -19
  121. data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
  122. data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
  123. data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
  124. data/lib/julewire/core/testing/chaos/destination.rb +0 -55
  125. data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
  126. data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
  127. data/lib/julewire/core/testing/chaos.rb +0 -80
  128. data/lib/julewire/core/testing/contracts/component.rb +0 -162
  129. data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
  130. data/lib/julewire/core/testing/contracts/integration.rb +0 -166
  131. data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
  132. data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
  133. data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
  134. data/lib/julewire/core/testing/contracts/wire.rb +0 -60
  135. data/lib/julewire/core/testing/contracts.rb +0 -24
  136. data/lib/julewire/core/testing/coverage.rb +0 -58
  137. data/lib/julewire/core/testing/test_reports.rb +0 -78
@@ -7,9 +7,8 @@ module Julewire
7
7
  class << self
8
8
  def validate_byte_limit!(value, name:)
9
9
  return if value.nil?
10
+ return value if value.instance_of?(Integer) && value.positive?
10
11
 
11
- validate_integer_limit!(value, name: name, positive: true)
12
- rescue ArgumentError
13
12
  raise ArgumentError, "#{name} must be nil or a positive Integer"
14
13
  end
15
14
 
@@ -18,7 +17,7 @@ module Julewire
18
17
  end
19
18
 
20
19
  def validate_integer_limit!(value, name:, positive: false)
21
- return value if value.is_a?(Integer) && valid_integer_limit?(value, positive: positive)
20
+ return value if value.instance_of?(Integer) && valid_integer_limit?(value, positive: positive)
22
21
 
23
22
  qualifier = positive ? "positive" : "non-negative"
24
23
  raise ArgumentError, "#{name} must be a #{qualifier} Integer"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Julewire
4
4
  module Core
5
- VERSION = "1.0.1"
5
+ VERSION = "1.1.0"
6
6
  end
7
7
  end
data/lib/julewire/core.rb CHANGED
@@ -6,7 +6,7 @@ module Julewire
6
6
  # Each gem extends the shared Julewire namespace.
7
7
  loader = Zeitwerk::Loader.for_gem_extension(self)
8
8
  loader.inflector.inflect("cli" => "CLI")
9
- loader.ignore("#{__dir__}/core/testing.rb", "#{__dir__}/core/testing")
9
+ loader.ignore("#{__dir__}/core/testing.rb")
10
10
  loader.setup
11
11
 
12
12
  module Core
@@ -25,7 +25,7 @@ module Julewire
25
25
 
26
26
  value.to_sym
27
27
  when Symbol
28
- raise ArgumentError, "#{name} must not be empty" if value.name.empty?
28
+ raise ArgumentError, "#{name} must not be empty" if value.empty?
29
29
 
30
30
  value
31
31
  else
@@ -54,11 +54,6 @@ module Julewire
54
54
  extend Core::FacadeMethods
55
55
 
56
56
  Core::RuntimeLocator.current = Core::Runtime.new
57
- Core.singleton_class.class_eval do
58
- define_method(:loader) { loader }
59
- private :loader
60
- end
61
-
62
57
  ConsoleFormatter = Core::Records::ConsoleFormatter
63
58
  JsonEncoder = Core::Serialization::JsonEncoder
64
59
  Match = Core::Processing::Match
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: julewire-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Grebennik
@@ -87,6 +87,7 @@ files:
87
87
  - lib/julewire/core/destinations/collection.rb
88
88
  - lib/julewire/core/destinations/definition.rb
89
89
  - lib/julewire/core/destinations/destination.rb
90
+ - lib/julewire/core/destinations/processor_handling.rb
90
91
  - lib/julewire/core/destinations/registry.rb
91
92
  - lib/julewire/core/destinations/sink.rb
92
93
  - lib/julewire/core/destinations/synchronized_output.rb
@@ -139,6 +140,7 @@ files:
139
140
  - lib/julewire/core/integration/health.rb
140
141
  - lib/julewire/core/integration/ivar_state.rb
141
142
  - lib/julewire/core/integration/lifecycle.rb
143
+ - lib/julewire/core/integration/protocol.rb
142
144
  - lib/julewire/core/integration/scoped.rb
143
145
  - lib/julewire/core/integration/settings.rb
144
146
  - lib/julewire/core/integration/subscriber_install.rb
@@ -147,6 +149,7 @@ files:
147
149
  - lib/julewire/core/lifecycle_error.rb
148
150
  - lib/julewire/core/local_storage.rb
149
151
  - lib/julewire/core/processing.rb
152
+ - lib/julewire/core/processing/invalid_result_failure.rb
150
153
  - lib/julewire/core/processing/level_threshold.rb
151
154
  - lib/julewire/core/processing/match.rb
152
155
  - lib/julewire/core/processing/pipeline.rb
@@ -157,6 +160,7 @@ files:
157
160
  - lib/julewire/core/processing/sampling.rb
158
161
  - lib/julewire/core/propagation.rb
159
162
  - lib/julewire/core/propagation/carrier.rb
163
+ - lib/julewire/core/records/build_input.rb
160
164
  - lib/julewire/core/records/console_formatter.rb
161
165
  - lib/julewire/core/records/deconstruct.rb
162
166
  - lib/julewire/core/records/display_message.rb
@@ -173,7 +177,6 @@ files:
173
177
  - lib/julewire/core/runtime_registry.rb
174
178
  - lib/julewire/core/runtime_state.rb
175
179
  - lib/julewire/core/scheduling/deadline.rb
176
- - lib/julewire/core/scheduling/deadline_scheduler.rb
177
180
  - lib/julewire/core/scheduling/shared_scheduler.rb
178
181
  - lib/julewire/core/sentinel.rb
179
182
  - lib/julewire/core/serialization/backtrace_limiter.rb
@@ -191,22 +194,6 @@ files:
191
194
  - lib/julewire/core/serialization/value_copy.rb
192
195
  - lib/julewire/core/serialization/value_traversal.rb
193
196
  - lib/julewire/core/testing.rb
194
- - lib/julewire/core/testing/chaos.rb
195
- - lib/julewire/core/testing/chaos/catalog.rb
196
- - lib/julewire/core/testing/chaos/core_runtime.rb
197
- - lib/julewire/core/testing/chaos/destination.rb
198
- - lib/julewire/core/testing/chaos/emitter.rb
199
- - lib/julewire/core/testing/chaos/raising_output.rb
200
- - lib/julewire/core/testing/contracts.rb
201
- - lib/julewire/core/testing/contracts/component.rb
202
- - lib/julewire/core/testing/contracts/deadline_scheduler.rb
203
- - lib/julewire/core/testing/contracts/integration.rb
204
- - lib/julewire/core/testing/contracts/integration_fields.rb
205
- - lib/julewire/core/testing/contracts/record_draft.rb
206
- - lib/julewire/core/testing/contracts/runtime.rb
207
- - lib/julewire/core/testing/contracts/wire.rb
208
- - lib/julewire/core/testing/coverage.rb
209
- - lib/julewire/core/testing/test_reports.rb
210
197
  - lib/julewire/core/validation.rb
211
198
  - lib/julewire/core/version.rb
212
199
  - lib/julewire/error.rb
@@ -232,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
219
  - !ruby/object:Gem::Version
233
220
  version: '0'
234
221
  requirements: []
235
- rubygems_version: 4.0.14
222
+ rubygems_version: 4.0.16
236
223
  specification_version: 4
237
224
  summary: Execution-scoped structured logging core for Ruby applications.
238
225
  test_files: []
@@ -1,207 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Scheduling
6
- # @api integration_spi
7
- class DeadlineScheduler
8
- CLOCK = Process::CLOCK_MONOTONIC
9
- Entry = Data.define(:deadline, :token, :callback)
10
-
11
- def initialize(thread_name:, idle: :keep_alive)
12
- @thread_name = thread_name
13
- @idle = idle
14
- @mutex = Mutex.new
15
- @condition = ConditionVariable.new
16
- @entries = {}
17
- # A heap keeps timeout scheduling cheap without non-shareable scheduler dependencies.
18
- @heap = []
19
- @next_token = 0
20
- @generation = 0
21
- @pid = Process.pid
22
- @thread = nil
23
- end
24
-
25
- def schedule(timeout, &block)
26
- raise ArgumentError, "block required" unless block
27
-
28
- timeout = Float(timeout)
29
- if timeout <= 0
30
- yield
31
- return
32
- end
33
-
34
- @mutex.synchronize do
35
- token = next_token
36
- entry = Entry.new(monotonic_time + timeout, token, block)
37
- @entries[token] = entry
38
- heap_push(entry)
39
- ensure_thread
40
- @condition.signal
41
- token
42
- end
43
- end
44
-
45
- def cancel(token)
46
- return unless token
47
-
48
- @mutex.synchronize do
49
- @entries.delete(token)
50
- @condition.signal
51
- end
52
- end
53
-
54
- def after_fork!
55
- if @pid == Process.pid
56
- reset_same_process
57
- else
58
- reset_after_fork
59
- end
60
-
61
- self
62
- end
63
-
64
- private
65
-
66
- def reset_same_process
67
- @mutex.synchronize do
68
- @generation += 1
69
- @entries = {}
70
- @heap = []
71
- @next_token = 0
72
- @thread = nil
73
- @condition.broadcast
74
- end
75
- end
76
-
77
- def reset_after_fork
78
- @mutex = Mutex.new
79
- @condition = ConditionVariable.new
80
- @entries = {}
81
- @heap = []
82
- @next_token = 0
83
- @generation += 1
84
- @pid = Process.pid
85
- @thread = nil
86
- end
87
-
88
- def next_token
89
- @next_token += 1
90
- end
91
-
92
- def ensure_thread
93
- return if @thread&.alive?
94
-
95
- generation = @generation
96
- @thread = Thread.new { run(generation) }
97
- @thread.name = @thread_name
98
- @thread.report_on_exception = false
99
- end
100
-
101
- def run(generation)
102
- loop do
103
- callback = next_expired_callback(generation)
104
- return unless callback
105
-
106
- safe_call(callback)
107
- end
108
- end
109
-
110
- def next_expired_callback(generation)
111
- @mutex.synchronize do
112
- loop do
113
- return unless generation == @generation
114
-
115
- discard_cancelled_head
116
- if @heap.empty?
117
- return clear_thread if exit_when_idle?
118
-
119
- @condition.wait(@mutex)
120
- next
121
- end
122
-
123
- entry = @heap.fetch(0)
124
- remaining = entry.deadline - monotonic_time
125
- if remaining.positive?
126
- @condition.wait(@mutex, remaining)
127
- else
128
- heap_pop
129
- @entries.delete(entry.token)
130
- return entry.callback
131
- end
132
- end
133
- end
134
- end
135
-
136
- def discard_cancelled_head
137
- heap_pop while @heap.any? && !@entries.key?(@heap.fetch(0).token)
138
- end
139
-
140
- def clear_thread
141
- @thread = nil
142
- nil
143
- end
144
-
145
- def exit_when_idle?
146
- @idle == :exit
147
- end
148
-
149
- def safe_call(callback)
150
- callback.call
151
- rescue StandardError
152
- nil
153
- end
154
-
155
- def monotonic_time
156
- Process.clock_gettime(CLOCK)
157
- end
158
-
159
- def heap_push(entry)
160
- @heap << entry
161
- sift_up(@heap.length - 1)
162
- end
163
-
164
- def heap_pop
165
- return @heap.pop if @heap.one?
166
-
167
- top = @heap.fetch(0)
168
- @heap[0] = @heap.pop
169
- sift_down(0)
170
- top
171
- end
172
-
173
- def sift_up(index)
174
- while index.positive?
175
- parent = (index - 1) / 2
176
- break if earlier_or_equal?(@heap.fetch(parent), @heap.fetch(index))
177
-
178
- swap_heap(index, parent)
179
- index = parent
180
- end
181
- end
182
-
183
- def sift_down(index)
184
- loop do
185
- left = (index * 2) + 1
186
- right = left + 1
187
- smallest = index
188
- smallest = left if left < @heap.length && earlier_or_equal?(@heap.fetch(left), @heap.fetch(smallest))
189
- smallest = right if right < @heap.length && earlier_or_equal?(@heap.fetch(right), @heap.fetch(smallest))
190
- break if smallest == index
191
-
192
- swap_heap(index, smallest)
193
- index = smallest
194
- end
195
- end
196
-
197
- def earlier_or_equal?(left, right)
198
- left.deadline < right.deadline || (left.deadline == right.deadline && left.token <= right.token)
199
- end
200
-
201
- def swap_heap(left, right)
202
- @heap[left], @heap[right] = @heap[right], @heap[left]
203
- end
204
- end
205
- end
206
- end
207
- end
@@ -1,72 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Testing
6
- module Chaos
7
- class Catalog
8
- Entry = Data.define(:kind, :name, :exercise)
9
- KINDS = %i[processor formatter encoder destination subscriber listener].freeze
10
-
11
- attr_reader :entries
12
-
13
- class << self
14
- def build
15
- catalog = new
16
- yield catalog if block_given?
17
- catalog
18
- end
19
-
20
- def assert_contract(test_context, catalog:, errors:)
21
- entries = catalog.entries
22
- raise ArgumentError, "chaos catalog must have entries" if entries.empty?
23
-
24
- entries.each do |entry|
25
- assert_entry(test_context, entry, errors)
26
- end
27
- nil
28
- end
29
-
30
- private
31
-
32
- def assert_entry(test_context, entry, errors)
33
- Chaos.assert_contained(
34
- test_context,
35
- errors: errors,
36
- description: "#{entry.kind} #{entry.name}"
37
- ) do |error|
38
- entry.exercise.call(error)
39
- end
40
- end
41
- end
42
-
43
- def initialize
44
- @entries = []
45
- end
46
-
47
- def processor(name, &) = register(:processor, name, &)
48
-
49
- def formatter(name, &) = register(:formatter, name, &)
50
-
51
- def encoder(name, &) = register(:encoder, name, &)
52
-
53
- def destination(name, &) = register(:destination, name, &)
54
-
55
- def subscriber(name, &) = register(:subscriber, name, &)
56
-
57
- def listener(name, &) = register(:listener, name, &)
58
-
59
- private
60
-
61
- def register(kind, name, &exercise)
62
- raise ArgumentError, "unknown chaos component kind #{kind.inspect}" unless KINDS.include?(kind)
63
- raise ArgumentError, "chaos component exercise block required" unless exercise
64
-
65
- @entries << Entry.new(kind, Core.normalize_name(name), exercise)
66
- self
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Testing
6
- module Chaos
7
- module CoreRuntime
8
- SCENARIOS = %i[
9
- destination_processor
10
- drop_callback
11
- encoder
12
- failure_callback
13
- formatter
14
- lifecycle_after_fork
15
- lifecycle_flush
16
- output
17
- pipeline_processor
18
- ].freeze
19
-
20
- private_constant :SCENARIOS
21
-
22
- class << self
23
- def assert_contract(test_context, runtime:, reset:, errors:)
24
- SCENARIOS.each do |scenario|
25
- errors.each do |error|
26
- assert_scenario(test_context, runtime, reset, scenario, error)
27
- end
28
- end
29
- nil
30
- end
31
-
32
- private
33
-
34
- def assert_scenario(test_context, runtime, reset, scenario, error)
35
- reset.call
36
- send(:"exercise_#{scenario}", runtime, error)
37
- rescue StandardError => e
38
- test_context.flunk(
39
- "expected core #{scenario} failure to be contained, " \
40
- "#{error.class} leaked #{e.class}: #{e.message}"
41
- )
42
- ensure
43
- reset.call
44
- end
45
-
46
- def exercise_destination_processor(runtime, error)
47
- configure_destination(runtime, processors: [Chaos.raiser(error)])
48
- runtime.emit("chaos")
49
- end
50
-
51
- def exercise_drop_callback(runtime, error)
52
- trigger = RuntimeError.new("julewire chaos formatter trigger")
53
- configure_destination(runtime, formatter: Chaos.raiser(trigger), on_drop: Chaos.raiser(error))
54
- runtime.emit("chaos")
55
- end
56
-
57
- def exercise_encoder(runtime, error)
58
- configure_destination(runtime, encoder: Chaos.raiser(error))
59
- runtime.emit("chaos")
60
- end
61
-
62
- def exercise_failure_callback(runtime, error)
63
- trigger = RuntimeError.new("julewire chaos output trigger")
64
- configure_destination(
65
- runtime,
66
- output: RaisingOutput.new(trigger, failures: %i[write]),
67
- runtime_on_failure: Chaos.raiser(error)
68
- )
69
- runtime.emit("chaos")
70
- end
71
-
72
- def exercise_formatter(runtime, error)
73
- configure_destination(runtime, formatter: Chaos.raiser(error))
74
- runtime.emit("chaos")
75
- end
76
-
77
- def exercise_lifecycle_after_fork(runtime, error)
78
- configure_destination(runtime, output: RaisingOutput.new(error, failures: %i[after_fork]))
79
- runtime.after_fork!
80
- end
81
-
82
- def exercise_lifecycle_flush(runtime, error)
83
- configure_destination(runtime, output: RaisingOutput.new(error, failures: %i[flush]))
84
- runtime.flush
85
- end
86
-
87
- def exercise_output(runtime, error)
88
- configure_destination(runtime, output: RaisingOutput.new(error, failures: %i[write]))
89
- runtime.emit("chaos")
90
- end
91
-
92
- def exercise_pipeline_processor(runtime, error)
93
- runtime.configure do |config|
94
- config.destinations.use(:default, output: NullOutput.new)
95
- config.processors.use(Chaos.raiser(error))
96
- end
97
- runtime.emit("chaos")
98
- end
99
-
100
- def configure_destination(runtime, output: NullOutput.new, formatter: nil, encoder: nil,
101
- on_drop: nil, processors: nil, runtime_on_failure: nil)
102
- runtime.configure do |config|
103
- config.on_failure = runtime_on_failure if runtime_on_failure
104
- config.destinations.clear
105
- config.destinations.use(
106
- :default,
107
- encoder: encoder || Julewire::Core::Serialization::JsonEncoder.new,
108
- formatter: formatter || Julewire::Core::Records::Formatter.new,
109
- on_drop: on_drop,
110
- output: output,
111
- processors: processors || []
112
- )
113
- end
114
- end
115
- end
116
- end
117
- end
118
- end
119
- end
120
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Testing
6
- module Chaos
7
- module Destination
8
- class << self
9
- def assert_contract(test_context, record:, formatter:, encoder:, output:, callbacks:, errors:)
10
- {
11
- formatter: formatter,
12
- encoder: encoder,
13
- output: output,
14
- callbacks: callbacks
15
- }.compact.each do |scenario, builder|
16
- assert_scenario(test_context, scenario, builder, record, errors)
17
- end
18
- nil
19
- end
20
-
21
- private
22
-
23
- def assert_scenario(test_context, scenario, builder, record, errors)
24
- errors.each do |error|
25
- assert_error_contained(test_context, scenario, builder, record, error)
26
- end
27
- nil
28
- end
29
-
30
- def assert_error_contained(test_context, scenario, builder, record, error)
31
- Chaos.assert_contained(
32
- test_context,
33
- errors: [error],
34
- description: "destination #{scenario}"
35
- ) do |build_error|
36
- destination = builder.call(build_error)
37
- destination.emit(record)
38
- ensure
39
- close_destination(destination)
40
- end
41
- end
42
-
43
- def close_destination(destination)
44
- return unless destination.respond_to?(:close)
45
-
46
- destination.close(timeout: 0)
47
- rescue StandardError
48
- nil
49
- end
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Testing
6
- module Chaos
7
- module Emitter
8
- class << self
9
- def assert_contract(test_context, component:, build:, exercise:, errors:)
10
- Chaos.assert_contained(test_context, errors: errors, description: component) do |error|
11
- emitter = build.call(error)
12
- exercise.call(emitter, error)
13
- end
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Testing
6
- module Chaos
7
- class RaisingOutput
8
- def initialize(error, failures:)
9
- @error = error
10
- @failures = failures
11
- end
12
-
13
- def write(value)
14
- raise @error if @failures.include?(:write)
15
-
16
- value.bytesize
17
- end
18
-
19
- def flush
20
- raise @error if @failures.include?(:flush)
21
-
22
- self
23
- end
24
-
25
- def close
26
- raise @error if @failures.include?(:close)
27
-
28
- self
29
- end
30
-
31
- def after_fork!
32
- raise @error if @failures.include?(:after_fork)
33
-
34
- self
35
- end
36
- end
37
-
38
- private_constant :RaisingOutput
39
- end
40
- end
41
- end
42
- end