julewire-core 1.0.0 → 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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/docs/advanced-configuration.md +2 -1
  4. data/docs/configuration.md +14 -7
  5. data/docs/context-and-propagation.md +26 -4
  6. data/docs/contracts.md +25 -50
  7. data/docs/extensions-and-api.md +87 -88
  8. data/docs/health-schema.md +12 -2
  9. data/docs/internals.md +24 -23
  10. data/docs/outputs-and-lifecycle.md +15 -8
  11. data/docs/records-and-data-policy.md +38 -7
  12. data/lib/julewire/core/cli/doctor.rb +20 -16
  13. data/lib/julewire/core/cli/line_helpers.rb +23 -12
  14. data/lib/julewire/core/cli/log_formats/core_json_decoder.rb +3 -3
  15. data/lib/julewire/core/cli/log_formats/record_decoder.rb +1 -1
  16. data/lib/julewire/core/cli/log_formats.rb +9 -9
  17. data/lib/julewire/core/cli/tail.rb +19 -14
  18. data/lib/julewire/core/cli/transcode.rb +4 -5
  19. data/lib/julewire/core/cli.rb +1 -1
  20. data/lib/julewire/core/configuration.rb +1 -1
  21. data/lib/julewire/core/context_store.rb +56 -87
  22. data/lib/julewire/core/destinations/chaos_output.rb +2 -2
  23. data/lib/julewire/core/destinations/collection.rb +8 -16
  24. data/lib/julewire/core/destinations/definition.rb +1 -9
  25. data/lib/julewire/core/destinations/destination.rb +16 -66
  26. data/lib/julewire/core/destinations/processor_handling.rb +78 -0
  27. data/lib/julewire/core/destinations/registry.rb +1 -1
  28. data/lib/julewire/core/destinations/sink.rb +0 -1
  29. data/lib/julewire/core/destinations/synchronized_output.rb +54 -17
  30. data/lib/julewire/core/destinations/tail_sampling.rb +18 -27
  31. data/lib/julewire/core/destinations/write_step.rb +5 -5
  32. data/lib/julewire/core/destinations.rb +0 -7
  33. data/lib/julewire/core/diagnostics/callback_notifier.rb +4 -2
  34. data/lib/julewire/core/diagnostics/doctor.rb +22 -15
  35. data/lib/julewire/core/diagnostics/failure_snapshot.rb +9 -3
  36. data/lib/julewire/core/diagnostics/health.rb +41 -57
  37. data/lib/julewire/core/diagnostics/integration_health_store.rb +11 -24
  38. data/lib/julewire/core/diagnostics/internal_records.rb +8 -12
  39. data/lib/julewire/core/diagnostics/invalid_severity_reporter.rb +16 -55
  40. data/lib/julewire/core/diagnostics/meta_observer.rb +37 -44
  41. data/lib/julewire/core/diagnostics/process_integration_health.rb +0 -5
  42. data/lib/julewire/core/diagnostics/tail/renderer.rb +1 -1
  43. data/lib/julewire/core/diagnostics/tail.rb +18 -22
  44. data/lib/julewire/core/execution/boundary.rb +2 -8
  45. data/lib/julewire/core/execution/handle.rb +14 -17
  46. data/lib/julewire/core/execution/lineage.rb +26 -62
  47. data/lib/julewire/core/execution/measurement_handle.rb +6 -10
  48. data/lib/julewire/core/execution/scope.rb +17 -22
  49. data/lib/julewire/core/execution/scope_fields.rb +4 -13
  50. data/lib/julewire/core/execution/scope_identity.rb +1 -9
  51. data/lib/julewire/core/execution/scope_snapshot.rb +18 -39
  52. data/lib/julewire/core/execution/summary_state.rb +16 -19
  53. data/lib/julewire/core/execution/view.rb +8 -17
  54. data/lib/julewire/core/facade_methods.rb +67 -65
  55. data/lib/julewire/core/fields/attribute_keys.rb +0 -2
  56. data/lib/julewire/core/fields/bags.rb +23 -27
  57. data/lib/julewire/core/fields/field_set.rb +52 -16
  58. data/lib/julewire/core/fields/field_stack.rb +69 -109
  59. data/lib/julewire/core/fields/internal/deletion.rb +6 -9
  60. data/lib/julewire/core/fields/internal.rb +35 -26
  61. data/lib/julewire/core/fields/lookup.rb +9 -13
  62. data/lib/julewire/core/fields/section_proxy.rb +1 -9
  63. data/lib/julewire/core/fields/stack_set.rb +9 -14
  64. data/lib/julewire/core/fields/static_labels.rb +1 -1
  65. data/lib/julewire/core/integration/configurable.rb +4 -3
  66. data/lib/julewire/core/integration/destination_health.rb +28 -7
  67. data/lib/julewire/core/integration/event_subscriber.rb +1 -1
  68. data/lib/julewire/core/integration/facade.rb +12 -15
  69. data/lib/julewire/core/integration/fork_hooks.rb +12 -36
  70. data/lib/julewire/core/integration/health.rb +0 -3
  71. data/lib/julewire/core/integration/ivar_state.rb +0 -5
  72. data/lib/julewire/core/integration/protocol.rb +20 -0
  73. data/lib/julewire/core/integration/settings.rb +4 -4
  74. data/lib/julewire/core/integration/subscriber_install.rb +2 -2
  75. data/lib/julewire/core/integration/subscription.rb +3 -2
  76. data/lib/julewire/core/integration/values.rb +0 -17
  77. data/lib/julewire/core/local_storage.rb +3 -19
  78. data/lib/julewire/core/processing/invalid_result_failure.rb +23 -0
  79. data/lib/julewire/core/processing/level_threshold.rb +7 -14
  80. data/lib/julewire/core/processing/match.rb +6 -4
  81. data/lib/julewire/core/processing/pipeline.rb +46 -40
  82. data/lib/julewire/core/processing/processor_chain.rb +22 -11
  83. data/lib/julewire/core/processing/processor_registry.rb +3 -4
  84. data/lib/julewire/core/processing/record_field_transform.rb +0 -4
  85. data/lib/julewire/core/processing/sampling.rb +1 -3
  86. data/lib/julewire/core/processing.rb +2 -2
  87. data/lib/julewire/core/propagation/carrier.rb +73 -19
  88. data/lib/julewire/core/propagation.rb +18 -4
  89. data/lib/julewire/core/records/build_input.rb +75 -0
  90. data/lib/julewire/core/records/console_formatter.rb +6 -6
  91. data/lib/julewire/core/records/deconstruct.rb +1 -1
  92. data/lib/julewire/core/records/display_message.rb +2 -4
  93. data/lib/julewire/core/records/draft.rb +127 -258
  94. data/lib/julewire/core/records/lazy_emit_input.rb +2 -12
  95. data/lib/julewire/core/records/metadata.rb +4 -3
  96. data/lib/julewire/core/records/public_projection.rb +2 -2
  97. data/lib/julewire/core/records/raw_input.rb +1 -1
  98. data/lib/julewire/core/records/record.rb +30 -18
  99. data/lib/julewire/core/records/severity.rb +7 -15
  100. data/lib/julewire/core/runtime.rb +87 -72
  101. data/lib/julewire/core/runtime_registry.rb +20 -15
  102. data/lib/julewire/core/runtime_state.rb +5 -3
  103. data/lib/julewire/core/scheduling/shared_scheduler.rb +28 -22
  104. data/lib/julewire/core/serialization/backtrace_limiter.rb +13 -9
  105. data/lib/julewire/core/serialization/bounded_transform.rb +3 -2
  106. data/lib/julewire/core/serialization/bounded_traversal.rb +72 -83
  107. data/lib/julewire/core/serialization/deep_compact_empty.rb +9 -11
  108. data/lib/julewire/core/serialization/deep_freeze.rb +45 -6
  109. data/lib/julewire/core/serialization/encoding_sanitizer.rb +2 -3
  110. data/lib/julewire/core/serialization/exception_shape.rb +7 -6
  111. data/lib/julewire/core/serialization/json_encoder.rb +10 -13
  112. data/lib/julewire/core/serialization/serializer.rb +22 -49
  113. data/lib/julewire/core/serialization/serializer_pool.rb +9 -0
  114. data/lib/julewire/core/serialization/text_encoder.rb +5 -5
  115. data/lib/julewire/core/serialization/truncation_metadata.rb +157 -0
  116. data/lib/julewire/core/serialization/value_copy.rb +183 -109
  117. data/lib/julewire/core/serialization/value_traversal.rb +10 -127
  118. data/lib/julewire/core/testing.rb +6 -56
  119. data/lib/julewire/core/validation.rb +2 -3
  120. data/lib/julewire/core/version.rb +1 -1
  121. data/lib/julewire/core.rb +2 -7
  122. metadata +7 -19
  123. data/lib/julewire/core/scheduling/deadline_scheduler.rb +0 -207
  124. data/lib/julewire/core/testing/chaos/catalog.rb +0 -72
  125. data/lib/julewire/core/testing/chaos/core_runtime.rb +0 -120
  126. data/lib/julewire/core/testing/chaos/destination.rb +0 -55
  127. data/lib/julewire/core/testing/chaos/emitter.rb +0 -20
  128. data/lib/julewire/core/testing/chaos/raising_output.rb +0 -42
  129. data/lib/julewire/core/testing/chaos.rb +0 -80
  130. data/lib/julewire/core/testing/contracts/component.rb +0 -162
  131. data/lib/julewire/core/testing/contracts/deadline_scheduler.rb +0 -59
  132. data/lib/julewire/core/testing/contracts/integration.rb +0 -166
  133. data/lib/julewire/core/testing/contracts/integration_fields.rb +0 -36
  134. data/lib/julewire/core/testing/contracts/record_draft.rb +0 -37
  135. data/lib/julewire/core/testing/contracts/runtime.rb +0 -178
  136. data/lib/julewire/core/testing/contracts/wire.rb +0 -60
  137. data/lib/julewire/core/testing/contracts.rb +0 -24
  138. data/lib/julewire/core/testing/coverage.rb +0 -58
  139. data/lib/julewire/core/testing/test_reports.rb +0 -78
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.0
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
@@ -187,25 +190,10 @@ files:
187
190
  - lib/julewire/core/serialization/serializer.rb
188
191
  - lib/julewire/core/serialization/serializer_pool.rb
189
192
  - lib/julewire/core/serialization/text_encoder.rb
193
+ - lib/julewire/core/serialization/truncation_metadata.rb
190
194
  - lib/julewire/core/serialization/value_copy.rb
191
195
  - lib/julewire/core/serialization/value_traversal.rb
192
196
  - lib/julewire/core/testing.rb
193
- - lib/julewire/core/testing/chaos.rb
194
- - lib/julewire/core/testing/chaos/catalog.rb
195
- - lib/julewire/core/testing/chaos/core_runtime.rb
196
- - lib/julewire/core/testing/chaos/destination.rb
197
- - lib/julewire/core/testing/chaos/emitter.rb
198
- - lib/julewire/core/testing/chaos/raising_output.rb
199
- - lib/julewire/core/testing/contracts.rb
200
- - lib/julewire/core/testing/contracts/component.rb
201
- - lib/julewire/core/testing/contracts/deadline_scheduler.rb
202
- - lib/julewire/core/testing/contracts/integration.rb
203
- - lib/julewire/core/testing/contracts/integration_fields.rb
204
- - lib/julewire/core/testing/contracts/record_draft.rb
205
- - lib/julewire/core/testing/contracts/runtime.rb
206
- - lib/julewire/core/testing/contracts/wire.rb
207
- - lib/julewire/core/testing/coverage.rb
208
- - lib/julewire/core/testing/test_reports.rb
209
197
  - lib/julewire/core/validation.rb
210
198
  - lib/julewire/core/version.rb
211
199
  - lib/julewire/error.rb
@@ -231,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
231
219
  - !ruby/object:Gem::Version
232
220
  version: '0'
233
221
  requirements: []
234
- rubygems_version: 4.0.14
222
+ rubygems_version: 4.0.16
235
223
  specification_version: 4
236
224
  summary: Execution-scoped structured logging core for Ruby applications.
237
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
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Julewire
4
- module Core
5
- module Testing
6
- # @api extension
7
- module Chaos
8
- DEFAULT_ERRORS = [
9
- RuntimeError.new("julewire chaos runtime"),
10
- ArgumentError.new("julewire chaos argument"),
11
- TypeError.new("julewire chaos type")
12
- ].freeze
13
- class << self
14
- def assert_contained(test_context, errors: DEFAULT_ERRORS, description: nil)
15
- raise ArgumentError, "block required" unless block_given?
16
-
17
- errors.each do |error|
18
- yield error
19
- rescue StandardError => e
20
- test_context.flunk(containment_message(description, error, e))
21
- end
22
- nil
23
- end
24
-
25
- def assert_core_runtime_containment(test_context, runtime: Julewire, reset: nil, errors: DEFAULT_ERRORS)
26
- reset ||= -> { runtime.reset! }
27
- raise ArgumentError, "reset must respond to call" unless reset.respond_to?(:call)
28
-
29
- CoreRuntime.assert_contract(test_context, runtime: runtime, reset: reset, errors: errors)
30
- end
31
-
32
- def catalog(&)
33
- Catalog.build(&)
34
- end
35
-
36
- def assert_discovered_chaos_contracts(test_context, catalog:, errors: DEFAULT_ERRORS)
37
- Catalog.assert_contract(test_context, catalog: catalog, errors: errors)
38
- end
39
-
40
- def assert_destination_chaos_contract(test_context, record:, formatter:, encoder:, output:,
41
- callbacks: nil, errors: DEFAULT_ERRORS)
42
- Destination.assert_contract(
43
- test_context,
44
- record: record,
45
- formatter: formatter,
46
- encoder: encoder,
47
- output: output,
48
- callbacks: callbacks,
49
- errors: errors
50
- )
51
- end
52
-
53
- def assert_emitter_chaos_contract(test_context, component:, build:, exercise:, errors: DEFAULT_ERRORS)
54
- Emitter.assert_contract(
55
- test_context,
56
- component: component,
57
- build: build,
58
- exercise: exercise,
59
- errors: errors
60
- )
61
- end
62
-
63
- def raiser(error = RuntimeError.new("julewire chaos"))
64
- ->(*) { raise error }
65
- end
66
-
67
- private
68
-
69
- def containment_message(description, expected_error, leaked_error)
70
- unless description
71
- return "expected #{expected_error.class} to be contained, leaked #{leaked_error.class}: #{leaked_error}"
72
- end
73
-
74
- "expected #{description} chaos to be contained, leaked #{leaked_error.class}: #{leaked_error}"
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end