chaotic_job 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c4da24d7fc4aa39030976d4f7249a14c87995ef5d5b05c523ad5e1e83724f37
4
- data.tar.gz: 0a7be90ecb5381a5c9e67c6673bb41bc3ad2997c904ca42bfedbb4c701ef6bf0
3
+ metadata.gz: 10db3cce1a0eedffe044477a348122d781e19cfbb6f561c9e389926d7de21153
4
+ data.tar.gz: a48f0d63526613593a25cc92d1c3eca794adb99466ef697a3c932e581a08434a
5
5
  SHA512:
6
- metadata.gz: 11a054f050a3e95aa40c392e281b69bf2c43dbba3b91bce3d17473d589f4e2057d26a18af5071d7a5f4cb2b055775bbdd293af6c712792fbe1d042c4c99cca85
7
- data.tar.gz: d6750f62adecc71334dd42cfb15e35e33fb36680be62a6c9d0d5f4e2ed29eb598f9f92fac6a09ec1a4636816789f13996bc0241faa5a1ef9eb4acac0577fc06f
6
+ metadata.gz: a1ea02d93adf652616f25478c8d7f628f30d56137485b10988bd2f08b9a62153327cd60c787555d7ba4286a7c26f94616ee45c92a58778e0689011461a39bb3b
7
+ data.tar.gz: 6d94662caef5559bca3de08c69674ad10dd4fa349fbd8434a42dc65c8baa8ba6da13f6cc8d13b9af815f4610148a874ba3f9fa0b9e5c8aa97e7b131afc5c3689
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2025-06-08
4
+
5
+ - `run_scenario` requires a Glitch instance [#5](https://github.com/fractaledmind/chaotic_job/pull/5)
6
+
3
7
  ## [0.5.0] - 2025-06-04
4
8
 
5
9
  - Add a Tracer class [#3](https://github.com/fractaledmind/chaotic_job/pull/3)
data/README.md CHANGED
@@ -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: [:before_call, "Job#step_3"])
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. A "glitch" is simply a tuple that describes precisely where you would like the failure to occur. The first element of the tuple is 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:
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
- |`:before_line`|`"#{file_path}:#{line_number}"`|`"/Users/you/path/to/file.rb:123"`|
131
- |`:before_call`|`"#{YourClass.name}(.|#)#{method_name}"`|`"YourClass.some_class_method"`|
132
- |`:before_return`|`"#{YourClass.name}(.|#)#{method_name}"`|`"YourClass#some_instance_method"`|
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 `:before_call` and `:before_return` keys are formatted the same, and can identify any instance (`#`) or class (`.`) method.
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 `:before_return`, like this:
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: [:before_return, "Job#step_3"])
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,21 @@ 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 `:before_line` key, like this:
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: [:before_line, "#{__FILE__}:6"])
167
+ run_scenario(Job.new, glitch: ChaoticJob::Glitch.before_line("#{__FILE__}:6"))
166
168
  ```
167
169
 
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:
170
+ If you want to simulate multiple glitches affecting a job run, you simply define additional failure points using the fluid interface of the `ChaoticJob::Glitch` class:
169
171
 
170
172
  ```ruby
171
- run_scenario(Job.new, glitches: [
172
- [:before_call, "Job#step_1"],
173
- [:before_return, "Job#step_1"]
174
- ])
173
+ run_scenario(
174
+ Job.new,
175
+ glitch: ChaoticJob::Glitch
176
+ .before_call("Job#step_1")
177
+ .before_return("Job#step_3")
178
+ )
175
179
  ```
176
180
 
177
181
  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.
@@ -1,12 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Glitch.new.before_line("job_crucible.rb:10") { do_anything }
4
- # Glitch.new.before_call("Model#method", String, name: "Joel") { do_anything }
5
- # Glitch.new.before_return("Model#method", String, name: "Joel") { do_anything }
6
- # Glitch.new.inject! { execute code to 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 self.before_line(key, &block)
11
+ new.before_line(key, &block)
12
+ end
13
+
14
+ def self.before_call(key, ...)
15
+ new.before_call(key, ...)
16
+ end
17
+
18
+ def self.before_return(key, return_type = nil, &block)
19
+ new.before_return(key, return_type, &block)
20
+ end
21
+
10
22
  def initialize
11
23
  @breakpoints = {}
12
24
  end
@@ -26,6 +38,15 @@ module ChaoticJob
26
38
  self
27
39
  end
28
40
 
41
+ def set_action(force: false, &block)
42
+ @breakpoints.each do |_key, handlers|
43
+ handlers.each do |_event, handler|
44
+ handler[:block] = block if handler[:block].nil? || force
45
+ end
46
+ end
47
+ self
48
+ end
49
+
29
50
  def inject!(&block)
30
51
  breakpoints = @breakpoints
31
52
 
@@ -45,17 +66,11 @@ module ChaoticJob
45
66
  end
46
67
 
47
68
  def all_executed?
48
- @breakpoints.all? do |_location, handlers|
49
- handlers.all? { |_position, handler| handler[:executed] }
69
+ @breakpoints.all? do |_key, handlers|
70
+ handlers.all? { |_event, handler| handler[:executed] }
50
71
  end
51
72
  end
52
73
 
53
- # def inspect
54
- # @breakpoints.flat_map do |location, configs|
55
- # configs.keys.map { |position| "#{position}-#{location}" }
56
- # end.join("|>")
57
- # end
58
-
59
74
  private
60
75
 
61
76
  def set_breakpoint(key, event, *args, retval: nil, **kwargs, &block)
@@ -7,20 +7,20 @@ module ChaoticJob
7
7
  class Scenario
8
8
  attr_reader :events
9
9
 
10
- def initialize(job, glitches:, raise: RetryableError, capture: /active_job/)
10
+ def initialize(job, glitch:, raise: RetryableError, capture: /active_job/)
11
11
  @job = job
12
- @glitches = glitches
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
- # TODO: assert that all glitches ran
34
- end
35
-
36
- def to_s
37
- @glitches.map { |position, location| "#{position}-#{location}" }.join("|>")
33
+ # TODO: assert that all glitch ran
38
34
  end
39
35
 
40
36
  def all_glitched?
41
37
  @glitch.all_executed?
42
38
  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
52
- end
53
39
  end
54
40
  end
@@ -45,7 +45,8 @@ module ChaoticJob
45
45
  def scenarios
46
46
  variants.map do |glitches|
47
47
  job = clone_job_template
48
- scenario = Scenario.new(job, glitches: glitches)
48
+ glitch = Glitch.new.tap { |g| glitches.each { |event, key| g.public_send(event, key) } }
49
+ scenario = Scenario.new(job, glitch: glitch)
49
50
  job.job_id = scenario.to_s
50
51
  scenario
51
52
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChaoticJob
4
- VERSION = "0.5.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/chaotic_job.rb CHANGED
@@ -76,10 +76,13 @@ module ChaoticJob
76
76
  Simulation.new(job, **kwargs).run(&block)
77
77
  end
78
78
 
79
- def run_scenario(job, glitch: nil, glitches: nil, raise: nil, capture: nil, &block)
80
- kwargs = {glitches: glitches || [glitch]}
79
+ def run_scenario(job, glitch:, raise: nil, capture: nil, &block)
80
+ kwargs = {}
81
+
82
+ kwargs[:glitch] = glitch
81
83
  kwargs[:raise] = binding.local_variable_get(:raise) if binding.local_variable_get(:raise)
82
84
  kwargs[:capture] = capture if capture
85
+
83
86
  if block
84
87
  Scenario.new(job, **kwargs).run(&block)
85
88
  else
@@ -87,6 +90,8 @@ module ChaoticJob
87
90
  end
88
91
  end
89
92
 
93
+ private
94
+
90
95
  def assert(test, msg = nil)
91
96
  return super unless @simulation_scenario
92
97
 
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.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-06-03 00:00:00.000000000 Z
10
+ date: 2025-06-08 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activejob