chaotic_job 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d7a3e97903b960aad5ff163a8ea80d3d1c94d8427e86d0d06cb6c44ceffd7ad4
4
- data.tar.gz: b4c2200a5436d1575f0f66d8647834514bd6563ac3fbf62a74014557d198ed90
3
+ metadata.gz: 8241f1235f37ed46c1da18d53e18c6fce43fb5758273bb51dba8db332dddb5b6
4
+ data.tar.gz: fc86ee73cf18b16d6142f7111e5d1cfa446b46bfea2e1959eb7aa222a5bf42dd
5
5
  SHA512:
6
- metadata.gz: fa8f9e58bf1fa3eab4e4b6441e91824322e02043daea6fccb7f4d21844c739b34d376880ef03924fed43a7f7d49cf72fa536400698216ca58247cb287c49abd6
7
- data.tar.gz: f8e83e868e13ed287f1ae9ff3b2560257b5c6fe1e6ba715bf400a93d2148c5a36d9372ac70aaadb5a498db198b3a0f0d9cd4bb3c00d138aa0ac4a8961c1e463a
6
+ metadata.gz: 7d32f1ab9748ea544d13c15692934634a6c3bcc01d602903e5e554abd229b7623a6a9aff111357031f474ec701fcd96131994604a121f9e55672eaed2616107b
7
+ data.tar.gz: c8ef73d8d07a091f59ebd1347ecc3f11fbd6ba6abc3eee218e251600ff156f748ec2a911d8108834cad96f9ce7ef59d1b3989606a27a827f1bb7bd93bfd9b062
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2024-11-06
4
+
5
+ - Update the `perform_all` helper method to `perform_all_jobs`
6
+ - Update the `perform_all_before` helper method to `perform_all_jobs_before`
7
+ - Update the `perform_all_after` helper method to `perform_all_jobs_after`
8
+ - Update the `perform_all_within` helper method to `perform_all_jobs_within`
9
+
10
+ ## [0.1.1] - 2024-11-06
11
+
12
+ - Update `Journal` interface
13
+ - Add top-level `ChaoticJob` methods to work with the journal
14
+ - Fix bug with job sorting in the `Performer`
15
+ - Fix bug with resolving time cutoffs in the `Performer`
16
+ - Fix bug with using the `run_scenario` helper with a block
17
+
3
18
  ## [0.1.0] - 2024-11-06
4
19
 
5
20
  - Added `Journal` to log activity for tests
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  > [!TIP]
11
11
  > This gem helps you test that your Active Jobs are reliable and resilient to failures. If you want to more easily *build* reliable and resilient Active Jobs, check out the companion [Acidic Job](https://github.com/fractaledmind/acidic_job/tree/alpha-1.0) gem.
12
12
 
13
- `ChaoticJob` provides a set of tools to help you test the reliability and resilience of your Active Jobs. It does this by allowing you to simulate various types of failures and glitches that can occur in a production environment.
13
+ `ChaoticJob` provides a set of tools to help you test the reliability and resilience of your Active Jobs. It does this by allowing you to simulate various types of failures and glitches that can occur in a production environment, inspired by the principles of [chaos testing](https://principlesofchaos.org) and [deterministic simulation testing](https://blog.resonatehq.io/deterministic-simulation-testing)
14
14
 
15
15
  ## Installation
16
16
 
@@ -59,29 +59,29 @@ end
59
59
 
60
60
  But, this method does not behave as you would expect. Functionally, it overwrites the `enqueue` method to immediately perform the job, which means that instead of your job being performed in waves, the retry is performed _within_ the execution of the original job. This both confuses the logs and means the behavior in your tests are not representative of the behavior in production.
61
61
 
62
- In order to properly test job retries, you should use the `perform_all` method provided by `ChaoticJob::Helpers`:
62
+ In order to properly test job retries, you should use the `perform_all_jobs` method provided by `ChaoticJob::Helpers`:
63
63
 
64
64
  ```ruby
65
65
  Job.perform_later
66
- perform_all
66
+ perform_all_jobs
67
67
  ```
68
68
 
69
69
  This helper will perform the job and all of its retries in the proper way, in waves, just like it would in production.
70
70
 
71
- If you need more control over which batches of jobs are performed, you can use the `perform_all_before` and `perform_all_after` methods. These are particularly useful if you need to test the behavior of a job that schedules another job. You can use these methods to perform only the original job and its retries, assert the state of the system, and then perform the scheduled job and its retries.
71
+ If you need more control over which batches of jobs are performed, you can use the `perform_all_jobs_before` and `perform_all_jobs_after` methods. These are particularly useful if you need to test the behavior of a job that schedules another job. You can use these methods to perform only the original job and its retries, assert the state of the system, and then perform the scheduled job and its retries.
72
72
 
73
73
  ```ruby
74
74
  JobThatSchedules.perform_later
75
- perform_all_before(4.seconds)
75
+ perform_all_jobs_before(4.seconds)
76
76
  assert_equal 1, enqueued_jobs.size
77
77
  assert_equal 2, performed_jobs.size
78
78
 
79
- perform_all_after(1.day)
79
+ perform_all_jobs_after(1.day)
80
80
  assert_equal 0, enqueued_jobs.size
81
81
  assert_equal 3, performed_jobs.size
82
82
  ```
83
83
 
84
- You can pass either a `Time` object or an `ActiveSupport::Duration` object to these methods. And, to make the code as readable as possible, the `perform_all_before` is also aliased as the `perform_all_within` method. This allows you to write the example above as `perform_all_within(4.seconds)`.
84
+ You can pass either a `Time` object or an `ActiveSupport::Duration` object to these methods. And, to make the code as readable as possible, the `perform_all_jobs_before` is also aliased as the `perform_all_jobs_within` method. This allows you to write the example above as `perform_all_jobs_within(4.seconds)`.
85
85
 
86
86
  ### Simulating Failures
87
87
 
@@ -115,10 +115,10 @@ end
115
115
  > |---|---|
116
116
  > | `Journal.log` | log simply that something happened within the default scope |
117
117
  > | `Journal.log(thing, scope: :special)` | log a particular value within a particular scope |
118
- > | `Journal.total` | get the total number of logs under the default scope |
119
- > | `Journal.total(scope: :special)` | get the total number of logs under a particular scope |
120
- > | `Journal.all` | get all of the logged values under the default scope |
121
- > | `Journal.all(scope: :special)` | get all of the logged values under a particular scope |
118
+ > | `Journal.size` | get the total number of logs under the default scope |
119
+ > | `Journal.size(scope: :special)` | get the total number of logs under a particular scope |
120
+ > | `Journal.entries` | get all of the logged values under the default scope |
121
+ > | `Journal.entries(scope: :special)` | get all of the logged values under a particular scope |
122
122
 
123
123
  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 location of the glitch, which can be either *before* or *after*. The second element is the location of the code that will be affected by the glitch, defined by its file path and line number. What this example scenario does is inject a glitch before the `step_3` method is called, here:
124
124
 
@@ -20,12 +20,16 @@ module ChaoticJob
20
20
  @logs[scope] << item
21
21
  end
22
22
 
23
- def total(scope: :default)
23
+ def size(scope: :default)
24
24
  @logs[scope]&.size || 0
25
25
  end
26
26
 
27
- def all(scope: :default)
27
+ def entries(scope: :default)
28
28
  @logs[scope]
29
29
  end
30
+
31
+ def top(scope: :default)
32
+ entries&.first
33
+ end
30
34
  end
31
35
  end
@@ -40,7 +40,17 @@ module ChaoticJob
40
40
 
41
41
  def enqueued_jobs_where(before: nil, after: nil)
42
42
  enqueued_jobs
43
- .sort_by { |job| job["scheduled_at"] }
43
+ .sort do |ljob, rjob|
44
+ lat = ljob[:at]
45
+ rat = rjob[:at]
46
+
47
+ # sort by scheduled time, with nil values first
48
+ if lat && rat
49
+ lat <=> rat
50
+ else
51
+ lat ? 1 : -1
52
+ end
53
+ end
44
54
  .select do |job|
45
55
  scheduled_at = job[:at]
46
56
 
@@ -67,7 +77,7 @@ module ChaoticJob
67
77
  in Time
68
78
  cutoff
69
79
  end
70
- delta = (Time.now - time).abs
80
+ delta = (Time.now - time).abs.floor
71
81
  changeset = case delta
72
82
  when 0..59 # seconds
73
83
  {usec: 0}
@@ -16,15 +16,15 @@ module ChaoticJob
16
16
  @events = []
17
17
  end
18
18
 
19
- def run
19
+ def run(&block)
20
20
  @job.class.retry_on RetryableError, attempts: 10, wait: 1, jitter: 0
21
21
 
22
22
  ActiveSupport::Notifications.subscribed(->(event) { @events << event.dup }, @capture) do
23
23
  glitch.inject! do
24
- if block_given?
25
- yield
24
+ @job.enqueue
25
+ if block
26
+ block.call
26
27
  else
27
- @job.enqueue
28
28
  Performer.perform_all
29
29
  end
30
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ChaoticJob
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/chaotic_job.rb CHANGED
@@ -11,20 +11,45 @@ module ChaoticJob
11
11
  class RetryableError < StandardError
12
12
  end
13
13
 
14
- module Helpers
15
- def perform_all
16
- Performer.perform_all
14
+ def self.log_to_journal!(item = nil, scope: nil)
15
+ if item && scope
16
+ Journal.log(item, scope: scope)
17
+ elsif item
18
+ Journal.log(item)
19
+ elsif scope
20
+ Journal.log(scope: scope)
21
+ else
22
+ Journal.log
23
+ end
24
+ end
25
+
26
+ def self.journal_size(scope: nil)
27
+ if scope
28
+ Journal.size(scope: scope)
29
+ else
30
+ Journal.size
17
31
  end
32
+ end
18
33
 
19
- def perform_all_within(time)
20
- Performer.perform_all_within(time)
34
+ def self.top_journal_entry(scope: nil)
35
+ if scope
36
+ Journal.top(scope: scope)
37
+ else
38
+ Journal.top
39
+ end
40
+ end
41
+
42
+ module Helpers
43
+ def perform_all_jobs
44
+ Performer.perform_all
21
45
  end
22
46
 
23
- def perform_all_before(time)
47
+ def perform_all_jobs_before(time)
24
48
  Performer.perform_all_before(time)
25
49
  end
50
+ alias_method :perform_all_jobs_within, :perform_all_jobs_before
26
51
 
27
- def perform_all_after(time)
52
+ def perform_all_jobs_after(time)
28
53
  Performer.perform_all_after(time)
29
54
  end
30
55
 
@@ -36,11 +61,15 @@ module ChaoticJob
36
61
  Simulation.new(job, **kwargs).run(&block)
37
62
  end
38
63
 
39
- def run_scenario(job, glitch: nil, glitches: nil, raise: nil, capture: nil)
64
+ def run_scenario(job, glitch: nil, glitches: nil, raise: nil, capture: nil, &block)
40
65
  kwargs = {glitches: glitches || [glitch]}
41
66
  kwargs[:raise] = raise if raise
42
67
  kwargs[:capture] = capture if capture
43
- Scenario.new(job, **kwargs).run
68
+ if block
69
+ Scenario.new(job, **kwargs).run(&block)
70
+ else
71
+ Scenario.new(job, **kwargs).run
72
+ end
44
73
  end
45
74
  end
46
75
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chaotic_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim