wayfarer 0.4.5 → 0.4.7

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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint.yaml +25 -0
  3. data/.github/workflows/release.yaml +29 -0
  4. data/.github/workflows/tests.yaml +30 -0
  5. data/.gitignore +4 -0
  6. data/.rubocop.yml +5 -0
  7. data/.vale.ini +5 -0
  8. data/.yardopts +1 -3
  9. data/Dockerfile +5 -4
  10. data/Gemfile +3 -0
  11. data/Gemfile.lock +107 -102
  12. data/Rakefile +5 -56
  13. data/bin/wayfarer +1 -1
  14. data/docker-compose.yml +20 -9
  15. data/docs/cookbook/consent_screen.md +2 -2
  16. data/docs/cookbook/executing_javascript.md +3 -3
  17. data/docs/cookbook/navigation.md +12 -12
  18. data/docs/cookbook/querying_html.md +3 -3
  19. data/docs/cookbook/screenshots.md +2 -2
  20. data/docs/cookbook/user_agent.md +1 -1
  21. data/docs/design.md +36 -0
  22. data/docs/guides/callbacks.md +24 -126
  23. data/docs/guides/configuration.md +8 -8
  24. data/docs/guides/handlers.md +60 -0
  25. data/docs/guides/index.md +1 -0
  26. data/docs/guides/jobs/error_handling.md +40 -0
  27. data/docs/guides/jobs.md +99 -31
  28. data/docs/guides/navigation.md +1 -1
  29. data/docs/guides/networking/capybara.md +13 -22
  30. data/docs/guides/networking/custom_adapters.md +82 -41
  31. data/docs/guides/networking/ferrum.md +4 -4
  32. data/docs/guides/networking/http.md +9 -13
  33. data/docs/guides/networking/selenium.md +10 -11
  34. data/docs/guides/pages.md +76 -10
  35. data/docs/guides/redis.md +10 -0
  36. data/docs/guides/routing.md +74 -0
  37. data/docs/guides/tasks.md +33 -9
  38. data/docs/guides/tutorial.md +60 -0
  39. data/docs/guides/user_agents.md +113 -0
  40. data/docs/index.md +17 -40
  41. data/docs/reference/cli.md +35 -25
  42. data/docs/reference/configuration.md +36 -0
  43. data/lib/wayfarer/base.rb +124 -46
  44. data/lib/wayfarer/batch_completion.rb +56 -0
  45. data/lib/wayfarer/callbacks.rb +22 -48
  46. data/lib/wayfarer/cli/route_printer.rb +71 -57
  47. data/lib/wayfarer/cli.rb +121 -0
  48. data/lib/wayfarer/gc.rb +13 -6
  49. data/lib/wayfarer/handler.rb +15 -7
  50. data/lib/wayfarer/logging.rb +38 -0
  51. data/lib/wayfarer/middleware/base.rb +2 -0
  52. data/lib/wayfarer/middleware/batch_completion.rb +19 -0
  53. data/lib/wayfarer/middleware/content_type.rb +54 -0
  54. data/lib/wayfarer/middleware/controller.rb +19 -15
  55. data/lib/wayfarer/middleware/dedup.rb +16 -13
  56. data/lib/wayfarer/middleware/dispatch.rb +12 -4
  57. data/lib/wayfarer/middleware/normalize.rb +12 -11
  58. data/lib/wayfarer/middleware/redis.rb +15 -0
  59. data/lib/wayfarer/middleware/router.rb +33 -35
  60. data/lib/wayfarer/middleware/stage.rb +5 -5
  61. data/lib/wayfarer/middleware/uri_parser.rb +30 -0
  62. data/lib/wayfarer/middleware/user_agent.rb +49 -0
  63. data/lib/wayfarer/networking/capybara.rb +1 -1
  64. data/lib/wayfarer/networking/context.rb +2 -2
  65. data/lib/wayfarer/networking/ferrum.rb +2 -2
  66. data/lib/wayfarer/networking/follow.rb +12 -6
  67. data/lib/wayfarer/networking/http.rb +1 -1
  68. data/lib/wayfarer/networking/pool.rb +17 -12
  69. data/lib/wayfarer/networking/selenium.rb +3 -3
  70. data/lib/wayfarer/networking/strategy.rb +2 -2
  71. data/lib/wayfarer/page.rb +36 -14
  72. data/lib/wayfarer/parsing/xml.rb +6 -6
  73. data/lib/wayfarer/parsing.rb +24 -0
  74. data/lib/wayfarer/redis/barrier.rb +13 -21
  75. data/lib/wayfarer/redis/counter.rb +19 -9
  76. data/lib/wayfarer/redis/pool.rb +1 -1
  77. data/lib/wayfarer/redis/resettable.rb +19 -0
  78. data/lib/wayfarer/routing/dsl.rb +1 -0
  79. data/lib/wayfarer/routing/matchers/path.rb +4 -2
  80. data/lib/wayfarer/routing/root_route.rb +5 -1
  81. data/lib/wayfarer/routing/route.rb +4 -14
  82. data/lib/wayfarer/stringify.rb +22 -30
  83. data/lib/wayfarer/task.rb +12 -18
  84. data/lib/wayfarer.rb +29 -2
  85. data/mkdocs.yml +52 -7
  86. data/rake/docs.rake +26 -0
  87. data/rake/lint.rake +105 -0
  88. data/rake/release.rake +29 -0
  89. data/rake/tests.rake +28 -0
  90. data/requirements.txt +1 -1
  91. data/spec/base_spec.rb +140 -160
  92. data/spec/batch_completion_spec.rb +104 -0
  93. data/spec/cli/job_spec.rb +19 -23
  94. data/spec/cli/routing_spec.rb +101 -0
  95. data/spec/cli/version_spec.rb +1 -1
  96. data/spec/factories/task.rb +7 -1
  97. data/spec/fixtures/dummy_job.rb +5 -3
  98. data/spec/gc_spec.rb +8 -50
  99. data/spec/handler_spec.rb +1 -1
  100. data/spec/integration/callbacks_spec.rb +157 -45
  101. data/spec/integration/content_type_spec.rb +145 -0
  102. data/spec/integration/gc_spec.rb +44 -0
  103. data/spec/integration/handler_spec.rb +66 -0
  104. data/spec/integration/page_spec.rb +44 -29
  105. data/spec/integration/params_spec.rb +33 -25
  106. data/spec/integration/parsing_spec.rb +125 -0
  107. data/spec/integration/routing_spec.rb +18 -0
  108. data/spec/integration/stage_spec.rb +27 -20
  109. data/spec/middleware/batch_completion_spec.rb +34 -0
  110. data/spec/middleware/chain_spec.rb +8 -8
  111. data/spec/middleware/content_type_spec.rb +86 -0
  112. data/spec/middleware/controller_spec.rb +5 -5
  113. data/spec/middleware/dedup_spec.rb +38 -55
  114. data/spec/middleware/dispatch_spec.rb +23 -7
  115. data/spec/middleware/normalize_spec.rb +44 -13
  116. data/spec/middleware/router_spec.rb +29 -30
  117. data/spec/middleware/stage_spec.rb +8 -8
  118. data/spec/middleware/uri_parser_spec.rb +53 -0
  119. data/spec/middleware/{fetch_spec.rb → user_agent_spec.rb} +28 -27
  120. data/spec/networking/context_spec.rb +17 -0
  121. data/spec/networking/follow_spec.rb +2 -2
  122. data/spec/networking/pool_spec.rb +5 -5
  123. data/spec/networking/strategy.rb +2 -2
  124. data/spec/page_spec.rb +42 -20
  125. data/spec/parsing/xml_spec.rb +11 -12
  126. data/spec/redis/barrier_spec.rb +8 -48
  127. data/spec/redis/counter_spec.rb +13 -1
  128. data/spec/redis/pool_spec.rb +1 -1
  129. data/spec/spec_helpers.rb +27 -16
  130. data/spec/support/test_app.rb +8 -0
  131. data/spec/task_spec.rb +3 -24
  132. data/spec/wayfarer_spec.rb +1 -1
  133. data/wayfarer.gemspec +4 -3
  134. metadata +61 -51
  135. data/.github/workflows/ci.yaml +0 -32
  136. data/docs/guides/error_handling.md +0 -31
  137. data/docs/guides/networking.md +0 -94
  138. data/docs/guides/performance.md +0 -130
  139. data/docs/guides/reliability.md +0 -41
  140. data/docs/guides/routing/steering.md +0 -30
  141. data/docs/reference/api/base.md +0 -48
  142. data/docs/reference/configuration_keys.md +0 -42
  143. data/docs/reference/environment_variables.md +0 -83
  144. data/lib/wayfarer/cli/base.rb +0 -45
  145. data/lib/wayfarer/cli/generate.rb +0 -17
  146. data/lib/wayfarer/cli/job.rb +0 -56
  147. data/lib/wayfarer/cli/route.rb +0 -29
  148. data/lib/wayfarer/cli/runner.rb +0 -34
  149. data/lib/wayfarer/cli/templates/Gemfile.tt +0 -5
  150. data/lib/wayfarer/cli/templates/job.rb.tt +0 -10
  151. data/lib/wayfarer/config/capybara.rb +0 -10
  152. data/lib/wayfarer/config/ferrum.rb +0 -11
  153. data/lib/wayfarer/config/networking.rb +0 -26
  154. data/lib/wayfarer/config/redis.rb +0 -14
  155. data/lib/wayfarer/config/root.rb +0 -11
  156. data/lib/wayfarer/config/selenium.rb +0 -21
  157. data/lib/wayfarer/config/strconv.rb +0 -45
  158. data/lib/wayfarer/config/struct.rb +0 -72
  159. data/lib/wayfarer/middleware/fetch.rb +0 -56
  160. data/lib/wayfarer/redis/connection.rb +0 -13
  161. data/lib/wayfarer/redis/version.rb +0 -19
  162. data/lib/wayfarer/routing/router.rb +0 -28
  163. data/spec/callbacks_spec.rb +0 -102
  164. data/spec/cli/generate_spec.rb +0 -39
  165. data/spec/config/capybara_spec.rb +0 -18
  166. data/spec/config/ferrum_spec.rb +0 -24
  167. data/spec/config/networking_spec.rb +0 -73
  168. data/spec/config/redis_spec.rb +0 -32
  169. data/spec/config/root_spec.rb +0 -31
  170. data/spec/config/selenium_spec.rb +0 -56
  171. data/spec/config/strconv_spec.rb +0 -58
  172. data/spec/config/struct_spec.rb +0 -66
  173. data/spec/integration/steering_spec.rb +0 -57
  174. data/spec/redis/version_spec.rb +0 -13
  175. data/spec/routing/router_spec.rb +0 -24
data/spec/base_spec.rb CHANGED
@@ -3,222 +3,202 @@
3
3
  require "spec_helpers"
4
4
 
5
5
  describe Wayfarer::Base, redis: true do
6
- include Wayfarer::Redis::Connection
6
+ let(:task) { build(:task, :redis_pool) }
7
7
 
8
- let(:url) { "https://example.com" }
9
- let(:batch) { "batch" }
10
- let(:task) { build(:task, batch: batch, url: url) }
11
- let(:klass) { Class.new(Wayfarer::Base) }
12
-
13
- before { stub_const("DummyJob", klass) }
8
+ before do
9
+ stub_const("DummyJob", Class.new(ActiveJob::Base).include(Wayfarer::Base))
10
+ end
14
11
 
15
12
  describe "::crawl" do
16
13
  it "enqueues a task" do
17
14
  expect(DummyJob).to receive(:perform_later).with(task)
18
- DummyJob.crawl(url, batch: batch)
15
+ DummyJob.crawl(task.url, batch: task.batch)
19
16
  end
20
17
 
21
18
  it "returns a task" do
22
- expect(DummyJob.crawl(url)).to be_a(Wayfarer::Task)
19
+ expect(DummyJob.crawl(task.url)).to be_a(Wayfarer::Task)
23
20
  end
24
21
  end
25
22
 
26
- describe "Callbacks" do
27
- let(:counter) { task.counter }
23
+ describe "#perform" do
24
+ subject(:counter) { Wayfarer::Redis::Counter.new(task) }
25
+ let!(:initial) { counter.increment }
28
26
 
29
- describe "after enqueue" do
30
- it "increments the counter" do
31
- expect {
32
- DummyJob.crawl(url, batch: batch)
33
- }.to change { counter.value }.by(1)
27
+ before do
28
+ DummyJob.class_eval do
29
+ route.to :index
34
30
  end
35
31
  end
36
32
 
37
- describe "after perform" do
38
- it "decrements the counter" do
39
- DummyJob.crawl(url, batch: batch)
40
- task.counter.increment
41
- expect { perform_enqueued_jobs }.to change { task.counter.value }.by(-1)
33
+ describe "succeeding job" do
34
+ before do
35
+ DummyJob.class_eval do
36
+ def index; end
37
+ end
42
38
  end
43
39
 
44
- context "when counter reaches 0" do
45
- it "resets the barrier" do
46
- DummyJob.crawl(url, batch: batch)
47
- perform_enqueued_jobs
48
- redis do |conn|
49
- expect(conn.exists?(task.barrier.redis_key)).to be(false)
50
- end
51
- end
40
+ specify do
41
+ expect_any_instance_of(Wayfarer::Redis::Counter)
42
+ .to receive(:increment).at_least(:once).and_call_original
52
43
 
53
- it "resets the counter" do
54
- DummyJob.crawl(url, batch: batch)
44
+ expect {
45
+ DummyJob.perform_later(task)
55
46
  perform_enqueued_jobs
56
- redis do |conn|
57
- expect(conn.exists?(task.counter.redis_key)).to be(false)
58
- end
59
- end
60
-
61
- it "runs after batch callbacks" do
62
- expect { |spy|
63
- klass.after_batch(&spy)
64
- DummyJob.crawl(url, batch: batch)
65
- perform_enqueued_jobs
66
- }.to yield_control
67
- end
47
+ assert_performed_jobs 1
48
+ expect(enqueued_jobs).to be_empty
49
+ }.not_to change { counter.value }.from(initial)
68
50
  end
69
51
  end
70
- end
71
-
72
- describe "Unhandled exceptions" do
73
- let(:klass) { Class.new(Wayfarer::Base) }
74
-
75
- before do
76
- allow_any_instance_of(DummyJob).to receive(:perform).and_raise(RuntimeError.new)
77
- end
78
52
 
79
- it "does not retry the job" do
80
- DummyJob.crawl(url, batch: batch)
81
-
82
- expect {
83
- begin
84
- perform_enqueued_jobs
85
- rescue StandardError
86
- nil
53
+ describe "failing job" do
54
+ before do
55
+ DummyJob.class_eval do
56
+ def index
57
+ raise
58
+ end
87
59
  end
88
- }.to change { enqueued_jobs.size }.by(-1)
89
- end
60
+ end
90
61
 
91
- it "decrements the counter" do
92
- 3.times { task.counter.increment }
62
+ specify do
63
+ expect_any_instance_of(Wayfarer::Redis::Counter)
64
+ .to receive(:increment).at_least(:once).and_call_original
93
65
 
94
- DummyJob.crawl(url, batch: batch)
95
- begin
96
- perform_enqueued_jobs
97
- rescue StandardError
98
- nil
66
+ expect {
67
+ DummyJob.perform_later(task)
68
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
69
+ assert_performed_jobs 1
70
+ expect(enqueued_jobs).to be_empty
71
+ }.not_to change { counter.value }.from(initial)
99
72
  end
100
-
101
- expect(task.counter.value).to be(3)
102
73
  end
103
- end
104
74
 
105
- describe "Retries" do
106
- let(:klass) do
107
- Class.new(Wayfarer::Base) do
108
- retry_on RuntimeError, attempts: 3 do |job, error|
109
- Spy.call(job, error)
75
+ describe "failing job with retries" do
76
+ before do
77
+ DummyJob.class_eval do
78
+ retry_on StandardError, attempts: 3
79
+
80
+ def index
81
+ raise
82
+ end
110
83
  end
111
84
  end
112
- end
113
85
 
114
- before do
115
- allow_any_instance_of(DummyJob).to receive(:perform) do |job|
116
- task = job.arguments.first
117
- task.metadata.job = job
86
+ specify do
87
+ expect_any_instance_of(Wayfarer::Redis::Counter)
88
+ .to receive(:increment).at_least(:once).and_call_original
118
89
 
119
- raise RuntimeError
90
+ expect {
91
+ DummyJob.perform_later(task)
92
+ 2.times { perform_enqueued_jobs }
93
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
94
+ assert_performed_jobs 3
95
+ expect(enqueued_jobs).to be_empty
96
+ }.not_to change { counter.value }.from(initial)
120
97
  end
121
-
122
- stub_const("Spy", spy)
123
98
  end
124
99
 
125
- it "retries the job" do
126
- expect(Spy).to receive(:call).exactly(:once)
127
- .with(kind_of(DummyJob),
128
- kind_of(RuntimeError))
100
+ describe "discarded after retries" do
101
+ before do
102
+ DummyJob.class_eval do
103
+ ErrorA = Class.new(StandardError)
104
+ ErrorB = Class.new(StandardError)
129
105
 
130
- DummyJob.crawl(url, batch: batch)
106
+ retry_on ErrorA, attempts: 5
131
107
 
132
- expect {
133
- perform_enqueued_jobs
134
- }.to change { enqueued_jobs.last["executions"] }.by(1)
108
+ discard_on ErrorB
135
109
 
136
- expect {
137
- perform_enqueued_jobs
138
- }.to change { enqueued_jobs.last["executions"] }.by(1)
110
+ def index
111
+ raise ErrorB if executions == 5
139
112
 
140
- expect {
141
- perform_enqueued_jobs
142
- }.to change { enqueued_jobs.size }.by(-1)
143
- end
144
-
145
- it "marks the URL seen" do
146
- task.counter.increment # otherwise barrier gets reset
147
- DummyJob.crawl(url, batch: batch)
148
- 3.times { perform_enqueued_jobs }
149
- expect(task.barrier.seen?(task.url)).to be(true)
150
- end
151
-
152
- it "decrements the counter" do
153
- 3.times { task.counter.increment }
113
+ raise ErrorA
114
+ end
115
+ end
116
+ end
154
117
 
155
- DummyJob.crawl(url, batch: batch)
156
- 3.times { perform_enqueued_jobs }
118
+ specify do
119
+ expect_any_instance_of(Wayfarer::Redis::Counter)
120
+ .to receive(:increment).at_least(:once).and_call_original
157
121
 
158
- expect(task.counter.value).to be(3)
122
+ expect {
123
+ DummyJob.perform_later(task)
124
+ 4.times { perform_enqueued_jobs }
125
+ perform_enqueued_jobs
126
+ assert_performed_jobs 5
127
+ expect(enqueued_jobs).to be_empty
128
+ }.not_to change { counter.value }.from(initial)
129
+ end
159
130
  end
160
131
 
161
- it "runs after batch callbacks" do
162
- expect { |spy|
163
- klass.after_batch(&spy)
164
- DummyJob.crawl(url, batch: batch)
165
- 3.times { perform_enqueued_jobs }
166
- }.to yield_control
167
- end
168
- end
132
+ describe "discarded job" do
133
+ before do
134
+ DummyJob.class_eval do
135
+ discard_on RuntimeError
169
136
 
170
- describe "Discarding" do
171
- let(:klass) do
172
- Class.new(Wayfarer::Base) do
173
- discard_on RuntimeError do |job, error|
174
- Spy.call(job, error)
137
+ def index
138
+ raise "boom"
139
+ end
175
140
  end
176
141
  end
177
- end
178
142
 
179
- before do
180
- allow_any_instance_of(DummyJob).to receive(:perform) do |job|
181
- task = job.arguments.first
182
- task.metadata.job = job
143
+ specify do
144
+ expect_any_instance_of(Wayfarer::Redis::Counter)
145
+ .to receive(:increment).at_least(:once).and_call_original
183
146
 
184
- raise RuntimeError
147
+ expect {
148
+ DummyJob.perform_later(task)
149
+ perform_enqueued_jobs
150
+ assert_performed_jobs 1
151
+ expect(enqueued_jobs).to be_empty
152
+ }.not_to change { counter.value }.from(initial)
185
153
  end
186
-
187
- stub_const("Spy", spy)
188
154
  end
189
155
 
190
- it "discards the job" do
191
- expect(Spy).to receive(:call).exactly(:once)
192
- .with(kind_of(DummyJob),
193
- kind_of(RuntimeError))
156
+ describe "after_batch callback" do
157
+ let(:initial) { counter.value }
194
158
 
195
- DummyJob.crawl(url, batch: batch)
159
+ describe "when after_perform callback fails" do
160
+ before do
161
+ DummyJob.class_eval do
162
+ after_perform { raise }
196
163
 
197
- expect {
198
- perform_enqueued_jobs
199
- }.to change { enqueued_jobs.size }.by(-1)
200
- end
164
+ def index; end
165
+ end
166
+ end
201
167
 
202
- it "marks the URL seen" do
203
- task.counter.increment # otherwise barrier gets reset
204
- DummyJob.crawl(url, batch: batch)
205
- perform_enqueued_jobs
206
- expect(task.barrier.seen?(task.url)).to be(true)
207
- end
168
+ specify do
169
+ expect_any_instance_of(Wayfarer::Redis::Counter)
170
+ .to receive(:increment).at_least(:once).and_call_original
208
171
 
209
- it "decrements the counter" do
210
- 3.times { task.counter.increment }
211
- DummyJob.crawl(url, batch: batch)
212
- perform_enqueued_jobs
213
- expect(task.counter.value).to be(3)
214
- end
172
+ expect {
173
+ DummyJob.perform_later(task)
174
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
175
+ assert_performed_jobs 1
176
+ expect(enqueued_jobs).to be_empty
177
+ }.not_to change { counter.value }.from(initial)
178
+ end
179
+ end
180
+
181
+ describe "when after_batch callback fails" do
182
+ before do
183
+ DummyJob.class_eval do
184
+ after_batch { raise }
185
+
186
+ def index; end
187
+ end
188
+ end
189
+
190
+ specify do
191
+ expect_any_instance_of(Wayfarer::Redis::Counter)
192
+ .to receive(:increment).at_least(:once).and_call_original
215
193
 
216
- it "runs after batch callbacks" do
217
- expect { |spy|
218
- klass.after_batch(&spy)
219
- DummyJob.crawl(url, batch: batch)
220
- perform_enqueued_jobs
221
- }.to yield_control
194
+ expect {
195
+ DummyJob.perform_later(task)
196
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
197
+ assert_performed_jobs 1
198
+ expect(enqueued_jobs).to be_empty
199
+ }.not_to change { counter.value }.from(initial)
200
+ end
201
+ end
222
202
  end
223
203
  end
224
204
  end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ describe Wayfarer::BatchCompletion, redis: true do
6
+ let(:task) { build(:task, :redis_pool) }
7
+ let(:job) { double(arguments: [task]) }
8
+
9
+ describe "::call" do
10
+ let(:name) { "foo" }
11
+
12
+ subject { described_class.call(name, nil, nil, nil, { job: job }) }
13
+
14
+ context "with Wayfarer job" do
15
+ before { job.extend(Wayfarer::Base) }
16
+
17
+ specify do
18
+ expect(Wayfarer::BatchCompletion)
19
+ .to receive(:handle).with(name, job, task, instance_of(Wayfarer::Redis::Counter))
20
+
21
+ subject
22
+ end
23
+
24
+ it "does not reassign Redis pool" do
25
+ expect { subject }.not_to change { task[:redis_pool] }.from(Wayfarer::Redis::Pool.instance)
26
+ end
27
+
28
+ context "without Redis pool" do
29
+ before do
30
+ task[:redis_pool] = nil
31
+ end
32
+
33
+ it "assigns Redis pool" do
34
+ expect { subject }.to change { task[:redis_pool] }.from(nil).to(Wayfarer::Redis::Pool.instance)
35
+ end
36
+ end
37
+ end
38
+
39
+ context "with other job" do
40
+ specify do
41
+ expect(Wayfarer::BatchCompletion).not_to receive(:handle)
42
+
43
+ subject
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "::handle" do
49
+ let(:job) do
50
+ double(arguments: [task],
51
+ executions: executions,
52
+ exception_executions: exception_executions)
53
+ end
54
+
55
+ let(:initial_exception_executions) { {} }
56
+ let(:exception_executions) { initial_exception_executions }
57
+ let(:executions) { 0 }
58
+ let(:counter) { Wayfarer::Redis::Counter.new(task) }
59
+
60
+ subject(:handle) { described_class.handle(event, job, task, counter) }
61
+
62
+ before { task[:initial_exception_executions] = initial_exception_executions }
63
+
64
+ context "enqueue.active_job" do
65
+ let(:event) { "enqueue.active_job" }
66
+
67
+ specify do
68
+ expect { handle }.to change { counter.value }.by(1)
69
+ end
70
+
71
+ context "with retry" do
72
+ let(:executions) { 1 }
73
+
74
+ specify do
75
+ expect { handle }.not_to(change { counter.value })
76
+ end
77
+ end
78
+ end
79
+
80
+ context "perform.active_job" do
81
+ let(:event) { "perform.active_job" }
82
+
83
+ specify do
84
+ expect { handle }.to change { counter.value }.by(-1)
85
+ end
86
+
87
+ context "with exception occurred" do
88
+ let(:exception_executions) { { "[RuntimeError]" => 1 } }
89
+
90
+ specify do
91
+ expect { handle }.not_to(change { counter.value })
92
+ end
93
+ end
94
+ end
95
+
96
+ context "retry_stopped.active_job" do
97
+ let(:event) { "retry_stopped.active_job" }
98
+
99
+ specify do
100
+ expect { handle }.to change { counter.value }.by(-1)
101
+ end
102
+ end
103
+ end
104
+ end
data/spec/cli/job_spec.rb CHANGED
@@ -2,76 +2,72 @@
2
2
 
3
3
  require "spec_helpers"
4
4
 
5
- describe Wayfarer::CLI::Job, cli: true, redis: true do
5
+ describe Wayfarer::CLI, cli: true, redis: true do
6
6
  include Wayfarer::Redis
7
7
 
8
8
  let(:url) { test_app_path("/hello_world") }
9
9
  let(:batch) { "my-batch" }
10
- subject(:cli) { Wayfarer::CLI::Runner }
10
+ subject(:cli) { Wayfarer::CLI }
11
11
 
12
12
  before do
13
- write_file "app/jobs/dummy_job.rb", <<~FILE
14
- class DummyJob < Wayfarer::Base
13
+ write_file "dummy_job.rb", <<~FILE
14
+ class DummyJob < ActiveJob::Base
15
+ include Wayfarer::Base
15
16
  end
16
17
  FILE
17
18
  end
18
19
 
19
- before { Wayfarer::CLI::Base.new.send(:load_environment) }
20
+ before { Wayfarer::CLI.new.send(:load_environment, "dummy_job.rb") }
20
21
 
21
- describe "job perform" do
22
+ describe "perform" do
22
23
  it "performs the worker" do
23
24
  expect_any_instance_of(DummyJob).to receive(:perform).with(kind_of(Wayfarer::Task)) do |job|
24
25
  task = job.arguments.first
25
- task.metadata.job = job
26
+ task[:job] = job
26
27
  end
27
28
 
28
- cli.start(["job", "perform", "DummyJob", url])
29
- end
30
-
31
- it "collects garbage" do
32
- expect_any_instance_of(Wayfarer::GC).to receive(:run).exactly(:once)
33
- cli.start(["job", "perform", "DummyJob", url])
29
+ cli.start(["perform", "-r", "dummy_job.rb", "DummyJob", url])
34
30
  end
35
31
 
36
32
  context "using MockRedis" do
37
33
  it "performs the worker using MockRedis" do
38
- cli.start(["job", "perform", "--mock-redis", "DummyJob", url])
39
- expect(Wayfarer.config.redis.factory.call(nil)).to be_a(MockRedis)
34
+ cli.start(["perform", "--mock-redis", "DummyJob", url])
35
+ expect(Wayfarer.config[:redis][:factory].call(nil)).to be_a(MockRedis)
40
36
  end
41
37
  end
42
38
  end
43
39
 
44
- describe "job enqueue" do
40
+ describe "enqueue" do
45
41
  it "enqueues the job" do
46
42
  expect(DummyJob).to receive(:crawl).with(Addressable::URI.parse(url), batch: kind_of(String))
47
- cli.start(["job", "enqueue", "DummyJob", url])
43
+ cli.start(["enqueue", "DummyJob", url])
48
44
  end
49
45
 
50
46
  context "with batch provided" do
51
47
  it "enqueues the job" do
52
48
  expect(DummyJob).to receive(:crawl).with(Addressable::URI.parse(url), batch: batch)
53
- cli.start(["job", "enqueue", "--batch", batch, "DummyJob", url])
49
+ cli.start(["enqueue", "--batch", batch, "DummyJob", url])
54
50
  end
55
51
  end
56
52
  end
57
53
 
58
- describe "job execute" do
54
+ describe "execute" do
59
55
  it "executes the job" do
60
56
  expect(DummyJob).to receive(:crawl).with(Addressable::URI.parse(url), batch: kind_of(String))
61
- cli.start(["job", "execute", "DummyJob", url])
57
+ cli.start(["execute", "DummyJob", url])
62
58
  end
63
59
 
64
60
  context "with batch provided" do
65
61
  it "enqueues the job" do
66
62
  expect(DummyJob).to receive(:crawl).with(Addressable::URI.parse(url), batch: batch)
67
- cli.start(["job", "execute", "--batch", batch, "DummyJob", url])
63
+ cli.start(["execute", "--batch", batch, "DummyJob", url])
68
64
  end
69
65
  end
70
66
 
71
67
  context "using MockRedis" do
72
68
  it "performs the worker using MockRedis" do
73
- cli.start(["job", "execute", "--mock-redis", "DummyJob", url])
74
- expect(Wayfarer.config.redis.factory.call(nil)).to be_a(MockRedis)
69
+ cli.start(["execute", "--mock-redis", "DummyJob", url])
70
+ expect(Wayfarer.config[:redis][:factory].call(nil)).to be_a(MockRedis)
75
71
  end
76
72
  end
77
73
  end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ describe Wayfarer::CLI, cli: true do
6
+ before { write_file("dummy_job.rb", contents) }
7
+
8
+ let(:contents) do
9
+ <<~RUBY
10
+ class DummyJob < ActiveJob::Base
11
+ include Wayfarer::Base
12
+
13
+ #{routes}
14
+ end
15
+ RUBY
16
+ end
17
+
18
+ describe "route" do
19
+ let(:routes) do
20
+ <<~RUBY
21
+ route.host #{test_app_host.inspect}, to: :index
22
+ RUBY
23
+ end
24
+
25
+ subject(:route) { described_class.start(["route", "-r", "dummy_job.rb", "DummyJob", url]) }
26
+
27
+ context "with matching URL" do
28
+ let(:url) { "http://#{test_app_host}" }
29
+
30
+ specify do
31
+ expect { route }.to output("Match => :index\n").to_stdout
32
+ end
33
+ end
34
+
35
+ context "with mismatching URL" do
36
+ let(:url) { "http://example.com" }
37
+
38
+ specify do
39
+ expect { route }.to output("Mismatch\n").to_stdout
40
+ end
41
+ end
42
+ end
43
+
44
+ describe "tree" do
45
+ subject(:tree) { described_class.start(["tree", "-r", "dummy_job.rb", "DummyJob", url]) }
46
+
47
+ context "without child routes" do
48
+ let(:url) { "http://#{test_app_host}" }
49
+ let(:routes) { "" }
50
+
51
+ specify do
52
+ expect { tree }.to output("Mismatch\n").to_stdout
53
+ end
54
+ end
55
+
56
+ context "symbol target" do
57
+ let(:url) { "http://#{test_app_host}" }
58
+ let(:routes) do
59
+ <<~RUBY
60
+ route.host #{test_app_host.inspect}, to: :index
61
+ RUBY
62
+ end
63
+
64
+ let(:expected_output) do
65
+ <<~OUTPUT
66
+ Match(:index)
67
+ └──Host("#{test_app_host}", match: true)
68
+ └──Target(match: true)
69
+ OUTPUT
70
+ end
71
+
72
+ specify do
73
+ expect { tree }.to output(expected_output).to_stdout
74
+ end
75
+ end
76
+
77
+ context "symbol target with params" do
78
+ let(:url) { "http://#{test_app_host}/barqux" }
79
+ let(:routes) do
80
+ <<~RUBY
81
+ route.host #{test_app_host.inspect}, to: :index do
82
+ path ":foobar"
83
+ end
84
+ RUBY
85
+ end
86
+
87
+ let(:expected_output) do
88
+ <<~OUTPUT
89
+ Match(:index, params: {:foobar=>"barqux"})
90
+ └──Host("#{test_app_host}", match: true)
91
+ └──Target(match: true)
92
+ └──Path("/:foobar", match: true, params: {:foobar=>"barqux"})
93
+ OUTPUT
94
+ end
95
+
96
+ specify do
97
+ expect { tree }.to output(expected_output).to_stdout
98
+ end
99
+ end
100
+ end
101
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  require "spec_helpers"
4
4
 
5
- describe Wayfarer::CLI::Runner, cli: true do
5
+ describe Wayfarer::CLI, cli: true do
6
6
  describe "version" do
7
7
  it "prints the version" do
8
8
  expect {