chaotic_job 0.5.0 โ 0.7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +28 -37
- data/lib/chaotic_job/glitch.rb +38 -48
- data/lib/chaotic_job/performer.rb +4 -1
- data/lib/chaotic_job/scenario.rb +8 -22
- data/lib/chaotic_job/simulation.rb +16 -21
- data/lib/chaotic_job/version.rb +1 -1
- data/lib/chaotic_job.rb +20 -5
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94de9ce766a9042a925882e70f29a8022fbd3b22fbd175ed3254260e04962cba
|
4
|
+
data.tar.gz: e05d2546038d72cd9181fef3eff223d7adec84225b0fa1267d89d0b3880351ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29fedcda5ca8ec4c98fa68d4f1e0661194c558a72a7a70b6b19a445de0e4bb0741e873c1365911315a8be73461248ccbca3005dd604e0a1f8229cf75aa4f7f69
|
7
|
+
data.tar.gz: 93423ffd252f3faf4b3c0e0c0e7097fc9e14468d884935fed12421ca653cc99a47819ab80833d4068e0c8ebf23752e45385923384c56e4072e08554d7fe872f2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.7.0] - 2025-06-09
|
4
|
+
|
5
|
+
- Glitch only works with singular event + key definition [#6](https://github.com/fractaledmind/chaotic_job/pull/6)
|
6
|
+
- Scenarios assert the glitch was executed [#7](https://github.com/fractaledmind/chaotic_job/pull/7)
|
7
|
+
- Add helper methods to create a Glitch of the various kinds [#8](https://github.com/fractaledmind/chaotic_job/pull/8)
|
8
|
+
- Improve test coverage [#9](https://github.com/fractaledmind/chaotic_job/pull/9)
|
9
|
+
|
10
|
+
## [0.6.0] - 2025-06-08
|
11
|
+
|
12
|
+
- `run_scenario` requires a Glitch instance [#5](https://github.com/fractaledmind/chaotic_job/pull/5)
|
13
|
+
|
3
14
|
## [0.5.0] - 2025-06-04
|
4
15
|
|
5
16
|
- Add a Tracer class [#3](https://github.com/fractaledmind/chaotic_job/pull/3)
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[](https://rubygems.org/gems/chaotic_job)
|
4
4
|
[](https://rubygems.org/gems/chaotic_job)
|
5
5
|

|
6
|
-

|
7
7
|
[](https://github.com/sponsors/fractaledmind)
|
8
8
|
[](https://twitter.com/fractaledmind)
|
9
9
|
|
@@ -107,7 +107,7 @@ test "scenario of a simple job" do
|
|
107
107
|
def step_3; ChaoticJob::Journal.log; end
|
108
108
|
end
|
109
109
|
|
110
|
-
run_scenario(Job.new, glitch:
|
110
|
+
run_scenario(Job.new, glitch: ChaoticJob::Glitch.before_call("Job#step_3"))
|
111
111
|
|
112
112
|
assert_equal 5, ChaoticJob::Journal.total
|
113
113
|
end
|
@@ -124,14 +124,16 @@ end
|
|
124
124
|
> | `Journal.entries` | get all of the logged values under the default scope |
|
125
125
|
> | `Journal.entries(scope: :special)` | get all of the logged values under a particular scope |
|
126
126
|
|
127
|
-
In this example, the job being tested is defined within the test case. You can, of course, also test jobs defined in your application. The key detail is the `glitch` keyword argument.
|
127
|
+
In this example, the job being tested is defined within the test case. You can, of course, also test jobs defined in your application. The key detail is the `glitch` keyword argument.
|
128
|
+
|
129
|
+
A "glitch" is describes precisely where you would like the failure to occur. The description is composed first of the _kind_ of glitch, which can be either `before_line`, `before_call`, or `before_return`. These refer to the three kinds of `TracePoint` events that the gem hooks into. The second element is the _key_ for the code that will be affected by the glitch. This _key_ is a specially formatted string that defines the specific bit of code that the glitch should be inserted before. The different kinds of glitches are identified by different kinds of keys:
|
128
130
|
|kind|key format|key example|
|
129
131
|
|---|---|---|
|
130
|
-
|
131
|
-
|
132
|
-
|
132
|
+
|`before_line`|`"#{file_path}:#{line_number}"`|`"/Users/you/path/to/file.rb:123"`|
|
133
|
+
|`before_call`|`"#{YourClass.name}(.|#)#{method_name}"`|`"YourClass.some_class_method"`|
|
134
|
+
|`before_return`|`"#{YourClass.name}(.|#)#{method_name}"`|`"YourClass#some_instance_method"`|
|
133
135
|
|
134
|
-
As you can see, the
|
136
|
+
As you can see, the `before_call` and `before_return` keys are formatted the same, and can identify any instance (`#`) or class (`.`) method.
|
135
137
|
|
136
138
|
What the example scenario above does is inject a glitch before the `step_3` method is called, here:
|
137
139
|
|
@@ -144,10 +146,10 @@ def perform
|
|
144
146
|
end
|
145
147
|
```
|
146
148
|
|
147
|
-
If we wanted to inject a glitch right before the `step_3` method finishes, we could define the glitch as a
|
149
|
+
If we wanted to inject a glitch right before the `step_3` method finishes, we could define the glitch as a `before_return`, like this:
|
148
150
|
|
149
151
|
```ruby
|
150
|
-
run_scenario(Job.new, glitch:
|
152
|
+
run_scenario(Job.new, glitch: ChaoticJob::Glitch.before_return("Job#step_3"))
|
151
153
|
```
|
152
154
|
|
153
155
|
and it would inject the transient error right here:
|
@@ -159,19 +161,10 @@ def step_3
|
|
159
161
|
end
|
160
162
|
```
|
161
163
|
|
162
|
-
Finally, if you need to inject a glitch right before a particular line of code is executed that is neither a method call nor a method return, you can use the
|
164
|
+
Finally, if you need to inject a glitch right before a particular line of code is executed that is neither a method call nor a method return, you can use the `before_line` key, like this:
|
163
165
|
|
164
166
|
```ruby
|
165
|
-
run_scenario(Job.new, glitch:
|
166
|
-
```
|
167
|
-
|
168
|
-
If you want to simulate multiple glitches affecting a job run, you can use the plural `glitches` keyword argument instead and pass an array of tuples:
|
169
|
-
|
170
|
-
```ruby
|
171
|
-
run_scenario(Job.new, glitches: [
|
172
|
-
[:before_call, "Job#step_1"],
|
173
|
-
[:before_return, "Job#step_1"]
|
174
|
-
])
|
167
|
+
run_scenario(Job.new, glitch: ChaoticJob::Glitch.before_line("#{__FILE__}:6"))
|
175
168
|
```
|
176
169
|
|
177
170
|
Scenario testing is useful to test the behavior of a job under a specific set of conditions. But, if you want to test the behavior of a job under a variety of conditions, you can use the `run_simulation` method. Instead of running a single scenario, a simulation will run the full set of possible error scenarios for your job.
|
@@ -199,23 +192,21 @@ end
|
|
199
192
|
More specifically, it will create a scenario injecting a glitch before every line of code executed in your job. So, in this example, the simulation will run 12 scenarios:
|
200
193
|
|
201
194
|
```ruby
|
202
|
-
|
203
|
-
|
204
|
-
[
|
205
|
-
[
|
206
|
-
[
|
207
|
-
[
|
208
|
-
[
|
209
|
-
[
|
210
|
-
[
|
211
|
-
[
|
212
|
-
[
|
213
|
-
[
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
It generates all possible glitch scenarios by performing your job once with a [`TracePoint`](https://docs.ruby-lang.org/en/master/TracePoint.html) that captures each line executed in your job. It then computes all possible glitch locations to produce a set of scenarios that will be run. The block that you pass to `run_simulation` will be called for each scenario, allowing you to make assertions about the behavior of your job under all scenarios.
|
195
|
+
#<Set:
|
196
|
+
{[:call, "Job#perform"],
|
197
|
+
[:line, "file.rb:3"],
|
198
|
+
[:call, "Job#step_1"],
|
199
|
+
[:return, "Job#step_1"],
|
200
|
+
[:line, "file.rb:4"],
|
201
|
+
[:call, "Job#step_2"],
|
202
|
+
[:return, "Job#step_2"],
|
203
|
+
[:line, "file.rb:5"],
|
204
|
+
[:call, "Job#step_3"],
|
205
|
+
[:return, "Job#step_3"],
|
206
|
+
[:return, "Job#perform"]}>
|
207
|
+
```
|
208
|
+
|
209
|
+
It generates all possible glitch scenarios by performing your job once with a [`TracePoint`](https://docs.ruby-lang.org/en/master/TracePoint.html) that captures every event executed as a part of your job running. The block that you pass to `run_simulation` will be called for each scenario, allowing you to make assertions about the behavior of your job under all scenarios.
|
219
210
|
|
220
211
|
If you want to have the simulation run against a larger collection of scenarios, you can capture a custom callstack using the `ChaoticJob::Tracer` class and pass it to the `run_simulation` method as the `callstack` parameter. A `Tracer` is initialized with a block that determines which `TracePoint` events to collect. You then call `capture` with a block that defines the code to be traced. The default `Simulation` tracer collects all events for the passed job and then traces the job execution, essentially like this:
|
221
212
|
|
data/lib/chaotic_job/glitch.rb
CHANGED
@@ -1,73 +1,64 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Glitch.
|
4
|
-
# Glitch.
|
5
|
-
# Glitch.
|
6
|
-
# Glitch.
|
3
|
+
# Glitch.before_line("job_crucible.rb:10") { do_anything }
|
4
|
+
# Glitch.before_call("Model#method", String, name: "Joel") { do_anything }
|
5
|
+
# Glitch.before_return("Model#method", String, name: "Joel") { do_anything }
|
6
|
+
# Glitch.inject! { execute code to glitch }
|
7
7
|
|
8
8
|
module ChaoticJob
|
9
9
|
class Glitch
|
10
|
-
def
|
11
|
-
|
10
|
+
def self.before_line(key, &block)
|
11
|
+
new(key, :line, &block)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
|
16
|
-
self
|
14
|
+
def self.before_call(key, ...)
|
15
|
+
new(key, :call, ...)
|
17
16
|
end
|
18
17
|
|
19
|
-
def
|
20
|
-
|
21
|
-
self
|
18
|
+
def self.before_return(key, return_type = nil, &block)
|
19
|
+
new(key, :return, retval: return_type, &block)
|
22
20
|
end
|
23
21
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
22
|
+
def initialize(key, event, *args, retval: nil, **kwargs, &block)
|
23
|
+
@event = event
|
24
|
+
@key = key
|
25
|
+
@args = args
|
26
|
+
@retval = retval
|
27
|
+
@kwargs = kwargs
|
28
|
+
@block = block
|
29
|
+
@executed = false
|
27
30
|
end
|
28
31
|
|
29
|
-
def
|
30
|
-
|
32
|
+
def set_action(force: false, &block)
|
33
|
+
@block = block if @block.nil? || force
|
34
|
+
end
|
31
35
|
|
32
|
-
|
36
|
+
def inject!(&block)
|
37
|
+
trace = TracePoint.new(@event) do |tp|
|
33
38
|
# :nocov: SimpleCov cannot track code executed _within_ a TracePoint
|
34
39
|
key = derive_key(tp)
|
35
|
-
|
40
|
+
next unless @key == key
|
36
41
|
|
37
|
-
|
38
|
-
next unless matches?(
|
42
|
+
matchers = derive_matchers(tp)
|
43
|
+
next unless matches?(matchers)
|
39
44
|
|
40
|
-
execute_block
|
45
|
+
execute_block
|
41
46
|
# :nocov:
|
42
47
|
end
|
43
48
|
|
44
49
|
trace.enable(&block)
|
45
50
|
end
|
46
51
|
|
47
|
-
def
|
48
|
-
@
|
49
|
-
handlers.all? { |_position, handler| handler[:executed] }
|
50
|
-
end
|
52
|
+
def executed?
|
53
|
+
@executed
|
51
54
|
end
|
52
55
|
|
53
|
-
# def inspect
|
54
|
-
# @breakpoints.flat_map do |location, configs|
|
55
|
-
# configs.keys.map { |position| "#{position}-#{location}" }
|
56
|
-
# end.join("|>")
|
57
|
-
# end
|
58
|
-
|
59
56
|
private
|
60
57
|
|
61
|
-
def set_breakpoint(key, event, *args, retval: nil, **kwargs, &block)
|
62
|
-
@breakpoints[key] ||= {}
|
63
|
-
@breakpoints[key][event] = {args: args, kwargs: kwargs, retval: retval, block: block, executed: false}
|
64
|
-
end
|
65
|
-
|
66
58
|
# :nocov: SimpleCov cannot track code executed _within_ a TracePoint
|
67
|
-
def matches?(
|
68
|
-
return true if defn.nil?
|
59
|
+
def matches?(matchers)
|
69
60
|
return true if matchers.nil?
|
70
|
-
return true if
|
61
|
+
return true if @args.empty? && @kwargs.empty? && @retval.nil?
|
71
62
|
|
72
63
|
args = []
|
73
64
|
kwargs = {}
|
@@ -92,25 +83,24 @@ module ChaoticJob
|
|
92
83
|
end
|
93
84
|
end
|
94
85
|
|
95
|
-
|
86
|
+
@args.each_with_index do |type, index|
|
96
87
|
return false unless type === args[index]
|
97
88
|
end
|
98
89
|
|
99
|
-
|
90
|
+
@kwargs.each do |key, type|
|
100
91
|
return false unless type === kwargs[key]
|
101
92
|
end
|
102
93
|
|
103
|
-
return false unless
|
94
|
+
return false unless @retval === retval
|
104
95
|
|
105
96
|
true
|
106
97
|
end
|
107
98
|
|
108
|
-
def execute_block
|
109
|
-
return
|
110
|
-
return if handler[:executed]
|
99
|
+
def execute_block
|
100
|
+
return if @executed
|
111
101
|
|
112
|
-
|
113
|
-
|
102
|
+
@executed = true
|
103
|
+
@block.call
|
114
104
|
end
|
115
105
|
|
116
106
|
def derive_key(trace)
|
@@ -76,8 +76,11 @@ module ChaoticJob
|
|
76
76
|
cutoff.from_now
|
77
77
|
in Time
|
78
78
|
cutoff
|
79
|
+
else
|
80
|
+
raise Error.new("cutoff must be Time or ActiveSupport::Duration, but got #{cutoff.inspect}")
|
79
81
|
end
|
80
82
|
delta = (Time.now - time).abs.floor
|
83
|
+
|
81
84
|
changeset = case delta
|
82
85
|
when 0..59 # seconds
|
83
86
|
{usec: 0}
|
@@ -85,7 +88,7 @@ module ChaoticJob
|
|
85
88
|
{sec: 0, usec: 0}
|
86
89
|
when 3600..86_399 # hours
|
87
90
|
{min: 0, sec: 0, usec: 0}
|
88
|
-
|
91
|
+
else # days+
|
89
92
|
{hour: 0, min: 0, sec: 0, usec: 0}
|
90
93
|
end
|
91
94
|
time.change(**changeset)
|
data/lib/chaotic_job/scenario.rb
CHANGED
@@ -5,22 +5,22 @@
|
|
5
5
|
|
6
6
|
module ChaoticJob
|
7
7
|
class Scenario
|
8
|
-
attr_reader :events
|
8
|
+
attr_reader :events, :glitch, :job
|
9
9
|
|
10
|
-
def initialize(job,
|
10
|
+
def initialize(job, glitch:, raise: RetryableError, capture: /active_job/)
|
11
11
|
@job = job
|
12
|
-
@
|
12
|
+
@glitch = (Glitch === glitch) ? glitch : (raise Error.new("glitch: must be a Glitch instance, but got #{glitch.inspect}"))
|
13
13
|
@raise = binding.local_variable_get(:raise)
|
14
14
|
@capture = capture
|
15
|
-
@glitch = nil
|
16
15
|
@events = []
|
17
16
|
end
|
18
17
|
|
19
18
|
def run(&block)
|
20
19
|
@job.class.retry_on RetryableError, attempts: 10, wait: 1, jitter: 0
|
20
|
+
@glitch.set_action { raise @raise }
|
21
21
|
|
22
22
|
ActiveSupport::Notifications.subscribed(->(event) { @events << event.dup }, @capture) do
|
23
|
-
glitch.inject! do
|
23
|
+
@glitch.inject! do
|
24
24
|
@job.enqueue
|
25
25
|
if block
|
26
26
|
block.call
|
@@ -30,25 +30,11 @@ module ChaoticJob
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
self
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
@
|
38
|
-
end
|
39
|
-
|
40
|
-
def all_glitched?
|
41
|
-
@glitch.all_executed?
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def glitch
|
47
|
-
@glitch ||= Glitch.new.tap do |glitch|
|
48
|
-
@glitches.each do |kind, location, _description|
|
49
|
-
glitch.public_send(kind, location) { raise @raise }
|
50
|
-
end
|
51
|
-
end
|
36
|
+
def glitched?
|
37
|
+
@glitch.executed?
|
52
38
|
end
|
53
39
|
end
|
54
40
|
end
|
@@ -1,53 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Simulation.new(job).run { |scenario| ... }
|
4
|
-
# Simulation.new(job).permutations
|
5
4
|
# Simulation.new(job).variants
|
6
5
|
# Simulation.new(job).scenarios
|
7
6
|
module ChaoticJob
|
8
7
|
class Simulation
|
9
|
-
def initialize(job, callstack: nil,
|
8
|
+
def initialize(job, callstack: nil, variations: nil, test: nil, seed: nil)
|
10
9
|
@template = job
|
11
10
|
@callstack = callstack || capture_callstack
|
12
|
-
@depth = depth
|
13
11
|
@variations = variations
|
14
12
|
@test = test
|
15
13
|
@seed = seed || Random.new_seed
|
16
14
|
@random = Random.new(@seed)
|
17
15
|
|
18
|
-
raise Error.new("callstack must be a generated via
|
16
|
+
raise Error.new("callstack must be a generated via ChaoticJob::Tracer") unless @callstack.is_a?(Stack)
|
19
17
|
end
|
20
18
|
|
21
|
-
def run(&
|
22
|
-
@template.class.retry_on RetryableError, attempts:
|
19
|
+
def run(&assertions)
|
20
|
+
@template.class.retry_on RetryableError, attempts: 3, wait: 1, jitter: 0
|
23
21
|
|
24
|
-
debug "๐พ Running #{
|
22
|
+
debug "๐พ Running #{@variations || "all"} simulations of the total #{variants.size} possibilities..."
|
25
23
|
|
26
24
|
scenarios.map do |scenario|
|
27
|
-
run_scenario(scenario, &
|
25
|
+
run_scenario(scenario, &assertions)
|
28
26
|
print "ยท"
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
32
|
-
def
|
30
|
+
def variants
|
33
31
|
error_locations = @callstack.map do |event, key|
|
34
32
|
["before_#{event}", key]
|
35
33
|
end
|
36
|
-
error_locations.permutation(@depth)
|
37
|
-
end
|
38
34
|
|
39
|
-
|
40
|
-
return permutations if @variations.nil?
|
35
|
+
return error_locations if @variations.nil?
|
41
36
|
|
42
|
-
|
37
|
+
error_locations.sample(@variations, random: @random)
|
43
38
|
end
|
44
39
|
|
45
40
|
def scenarios
|
46
|
-
variants.map do |
|
41
|
+
variants.map do |(event, key)|
|
47
42
|
job = clone_job_template
|
48
|
-
|
49
|
-
job
|
50
|
-
scenario
|
43
|
+
glitch = Glitch.public_send(event, key)
|
44
|
+
Scenario.new(job, glitch: glitch)
|
51
45
|
end
|
52
46
|
end
|
53
47
|
|
@@ -63,13 +57,14 @@ module ChaoticJob
|
|
63
57
|
callstack
|
64
58
|
end
|
65
59
|
|
66
|
-
def run_scenario(scenario, &
|
60
|
+
def run_scenario(scenario, &assertions)
|
67
61
|
debug "๐พ Running simulation with scenario: #{scenario}"
|
68
62
|
@test.before_setup
|
69
|
-
@test.simulation_scenario = scenario
|
63
|
+
@test.simulation_scenario = scenario
|
70
64
|
scenario.run
|
71
65
|
@test.after_teardown
|
72
|
-
|
66
|
+
@test.assert scenario.glitched?, "Scenario did not execute glitch: #{scenario.glitch}"
|
67
|
+
assertions.call(scenario)
|
73
68
|
ensure
|
74
69
|
@test.simulation_scenario = nil
|
75
70
|
end
|
data/lib/chaotic_job/version.rb
CHANGED
data/lib/chaotic_job.rb
CHANGED
@@ -66,20 +66,22 @@ module ChaoticJob
|
|
66
66
|
Performer.perform_all_after(time)
|
67
67
|
end
|
68
68
|
|
69
|
-
def run_simulation(job,
|
69
|
+
def run_simulation(job, variations: nil, callstack: nil, &block)
|
70
70
|
seed = defined?(RSpec) ? RSpec.configuration.seed : Minitest.seed
|
71
71
|
kwargs = {test: self, seed: seed}
|
72
|
-
kwargs[:depth] = depth if depth
|
73
72
|
kwargs[:variations] = variations if variations
|
74
73
|
kwargs[:callstack] = callstack if callstack
|
75
74
|
self.simulation_scenario = nil
|
76
75
|
Simulation.new(job, **kwargs).run(&block)
|
77
76
|
end
|
78
77
|
|
79
|
-
def run_scenario(job, glitch
|
80
|
-
kwargs = {
|
78
|
+
def run_scenario(job, glitch:, raise: nil, capture: nil, &block)
|
79
|
+
kwargs = {}
|
80
|
+
|
81
|
+
kwargs[:glitch] = glitch
|
81
82
|
kwargs[:raise] = binding.local_variable_get(:raise) if binding.local_variable_get(:raise)
|
82
83
|
kwargs[:capture] = capture if capture
|
84
|
+
|
83
85
|
if block
|
84
86
|
Scenario.new(job, **kwargs).run(&block)
|
85
87
|
else
|
@@ -87,6 +89,18 @@ module ChaoticJob
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
92
|
+
def glitch_before_line(key, &block)
|
93
|
+
Glitch.before_line(key, &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
def glitch_before_call(key, ...)
|
97
|
+
Glitch.before_call(key, ...)
|
98
|
+
end
|
99
|
+
|
100
|
+
def glitch_before_return(key, return_type = nil, &block)
|
101
|
+
Glitch.before_return(key, return_type, &block)
|
102
|
+
end
|
103
|
+
|
90
104
|
def assert(test, msg = nil)
|
91
105
|
return super unless @simulation_scenario
|
92
106
|
|
@@ -95,7 +109,8 @@ module ChaoticJob
|
|
95
109
|
default_msg = "Expected #{mu_pp test} to be truthy."
|
96
110
|
custom_msg = msg.is_a?(Proc) ? msg.call : msg
|
97
111
|
full_msg = custom_msg || default_msg
|
98
|
-
|
112
|
+
indented_scenario = @simulation_scenario.to_s.split("\n").join("\n ")
|
113
|
+
" #{indented_scenario}\n#{full_msg}"
|
99
114
|
end
|
100
115
|
|
101
116
|
super(test, contextual_msg)
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chaotic_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Margheim
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-06-
|
10
|
+
date: 2025-06-09 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activejob
|