wayfarer 0.4.6 → 0.4.8

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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/.env +17 -0
  3. data/.github/workflows/lint.yaml +27 -0
  4. data/.github/workflows/release.yaml +30 -0
  5. data/.github/workflows/tests.yaml +21 -0
  6. data/.gitignore +5 -1
  7. data/.rubocop.yml +36 -0
  8. data/.vale.ini +8 -0
  9. data/.yardopts +1 -3
  10. data/Dockerfile +6 -4
  11. data/Gemfile +24 -0
  12. data/Gemfile.lock +274 -164
  13. data/Rakefile +7 -51
  14. data/bin/wayfarer +1 -1
  15. data/docker-compose.yml +23 -13
  16. data/docs/cookbook/consent_screen.md +2 -2
  17. data/docs/cookbook/executing_javascript.md +3 -3
  18. data/docs/cookbook/navigation.md +12 -12
  19. data/docs/cookbook/querying_html.md +3 -3
  20. data/docs/cookbook/screenshots.md +2 -2
  21. data/docs/guides/callbacks.md +25 -125
  22. data/docs/guides/cli.md +71 -0
  23. data/docs/guides/configuration.md +10 -35
  24. data/docs/guides/development.md +67 -0
  25. data/docs/guides/handlers.md +60 -0
  26. data/docs/guides/index.md +1 -0
  27. data/docs/guides/jobs.md +142 -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 +103 -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 +78 -10
  35. data/docs/guides/redis.md +10 -0
  36. data/docs/guides/routing.md +156 -0
  37. data/docs/guides/tasks.md +53 -9
  38. data/docs/guides/tutorial.md +66 -0
  39. data/docs/guides/user_agents.md +115 -0
  40. data/docs/index.md +17 -40
  41. data/lib/wayfarer/base.rb +125 -46
  42. data/lib/wayfarer/batch_completion.rb +60 -0
  43. data/lib/wayfarer/callbacks.rb +22 -48
  44. data/lib/wayfarer/cli/route_printer.rb +85 -89
  45. data/lib/wayfarer/cli.rb +103 -0
  46. data/lib/wayfarer/gc.rb +18 -6
  47. data/lib/wayfarer/handler.rb +15 -7
  48. data/lib/wayfarer/kv.rb +28 -0
  49. data/lib/wayfarer/logging.rb +38 -0
  50. data/lib/wayfarer/middleware/base.rb +2 -0
  51. data/lib/wayfarer/middleware/batch_completion.rb +19 -0
  52. data/lib/wayfarer/middleware/chain.rb +7 -1
  53. data/lib/wayfarer/middleware/content_type.rb +59 -0
  54. data/lib/wayfarer/middleware/controller.rb +19 -15
  55. data/lib/wayfarer/middleware/dedup.rb +22 -13
  56. data/lib/wayfarer/middleware/dispatch.rb +17 -4
  57. data/lib/wayfarer/middleware/normalize.rb +7 -14
  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 +31 -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 +14 -3
  65. data/lib/wayfarer/networking/ferrum.rb +1 -4
  66. data/lib/wayfarer/networking/follow.rb +14 -7
  67. data/lib/wayfarer/networking/http.rb +1 -1
  68. data/lib/wayfarer/networking/pool.rb +23 -13
  69. data/lib/wayfarer/networking/selenium.rb +15 -7
  70. data/lib/wayfarer/networking/strategy.rb +2 -2
  71. data/lib/wayfarer/page.rb +34 -14
  72. data/lib/wayfarer/parsing/xml.rb +6 -6
  73. data/lib/wayfarer/parsing.rb +21 -0
  74. data/lib/wayfarer/redis/barrier.rb +26 -21
  75. data/lib/wayfarer/redis/counter.rb +18 -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 +166 -30
  79. data/lib/wayfarer/routing/hash_stack.rb +33 -0
  80. data/lib/wayfarer/routing/matchers/custom.rb +8 -5
  81. data/lib/wayfarer/routing/matchers/{suffix.rb → empty_params.rb} +2 -6
  82. data/lib/wayfarer/routing/matchers/host.rb +15 -9
  83. data/lib/wayfarer/routing/matchers/path.rb +11 -31
  84. data/lib/wayfarer/routing/matchers/query.rb +41 -17
  85. data/lib/wayfarer/routing/matchers/result.rb +12 -0
  86. data/lib/wayfarer/routing/matchers/scheme.rb +13 -5
  87. data/lib/wayfarer/routing/matchers/url.rb +13 -5
  88. data/lib/wayfarer/routing/path_consumer.rb +130 -0
  89. data/lib/wayfarer/routing/path_finder.rb +151 -23
  90. data/lib/wayfarer/routing/result.rb +1 -1
  91. data/lib/wayfarer/routing/root_route.rb +17 -1
  92. data/lib/wayfarer/routing/route.rb +66 -19
  93. data/lib/wayfarer/routing/serializable.rb +28 -0
  94. data/lib/wayfarer/routing/sub_route.rb +53 -0
  95. data/lib/wayfarer/routing/target_route.rb +17 -1
  96. data/lib/wayfarer/stringify.rb +21 -30
  97. data/lib/wayfarer/task.rb +9 -17
  98. data/lib/wayfarer/uri/normalization.rb +120 -0
  99. data/lib/wayfarer.rb +72 -5
  100. data/mise.toml +2 -0
  101. data/mkdocs.yml +44 -8
  102. data/rake/docs.rake +26 -0
  103. data/rake/lint.rake +9 -0
  104. data/rake/release.rake +23 -0
  105. data/rake/tests.rake +32 -0
  106. data/requirements.txt +1 -1
  107. data/spec/factories/job.rb +8 -0
  108. data/spec/factories/middleware.rb +2 -2
  109. data/spec/factories/path_finder.rb +11 -0
  110. data/spec/factories/redis.rb +19 -0
  111. data/spec/factories/task.rb +46 -2
  112. data/spec/spec_helpers.rb +55 -51
  113. data/spec/support/active_job_helpers.rb +8 -0
  114. data/spec/support/integration_helpers.rb +21 -0
  115. data/spec/support/redis_helpers.rb +9 -0
  116. data/spec/support/test_app.rb +66 -37
  117. data/spec/wayfarer/base_spec.rb +200 -0
  118. data/spec/wayfarer/batch_completion_spec.rb +142 -0
  119. data/spec/wayfarer/cli/job_spec.rb +88 -0
  120. data/spec/wayfarer/cli/routing_spec.rb +322 -0
  121. data/spec/{cli → wayfarer/cli}/version_spec.rb +1 -1
  122. data/spec/wayfarer/gc_spec.rb +29 -0
  123. data/spec/wayfarer/handler_spec.rb +9 -0
  124. data/spec/wayfarer/integration/callbacks_spec.rb +200 -0
  125. data/spec/wayfarer/integration/content_type_spec.rb +37 -0
  126. data/spec/wayfarer/integration/custom_routing_spec.rb +51 -0
  127. data/spec/wayfarer/integration/gc_spec.rb +40 -0
  128. data/spec/wayfarer/integration/handler_spec.rb +65 -0
  129. data/spec/wayfarer/integration/page_spec.rb +79 -0
  130. data/spec/wayfarer/integration/params_spec.rb +64 -0
  131. data/spec/wayfarer/integration/parsing_spec.rb +99 -0
  132. data/spec/wayfarer/integration/retry_spec.rb +112 -0
  133. data/spec/wayfarer/integration/stage_spec.rb +58 -0
  134. data/spec/wayfarer/middleware/batch_completion_spec.rb +33 -0
  135. data/spec/{middleware → wayfarer/middleware}/chain_spec.rb +24 -19
  136. data/spec/wayfarer/middleware/content_type_spec.rb +83 -0
  137. data/spec/{middleware → wayfarer/middleware}/controller_spec.rb +24 -22
  138. data/spec/wayfarer/middleware/dedup_spec.rb +66 -0
  139. data/spec/wayfarer/middleware/normalize_spec.rb +32 -0
  140. data/spec/wayfarer/middleware/router_spec.rb +102 -0
  141. data/spec/wayfarer/middleware/stage_spec.rb +63 -0
  142. data/spec/wayfarer/middleware/uri_parser_spec.rb +63 -0
  143. data/spec/wayfarer/middleware/user_agent_spec.rb +158 -0
  144. data/spec/wayfarer/networking/capybara_spec.rb +13 -0
  145. data/spec/{networking → wayfarer/networking}/context_spec.rb +46 -38
  146. data/spec/wayfarer/networking/ferrum_spec.rb +13 -0
  147. data/spec/{networking → wayfarer/networking}/follow_spec.rb +11 -6
  148. data/spec/wayfarer/networking/http_spec.rb +12 -0
  149. data/spec/{networking → wayfarer/networking}/pool_spec.rb +16 -14
  150. data/spec/wayfarer/networking/selenium_spec.rb +12 -0
  151. data/spec/{networking → wayfarer/networking}/strategy.rb +33 -54
  152. data/spec/wayfarer/page_spec.rb +69 -0
  153. data/spec/{parsing → wayfarer/parsing}/json_spec.rb +1 -1
  154. data/spec/wayfarer/parsing/xml_parse_spec.rb +25 -0
  155. data/spec/wayfarer/redis/barrier_spec.rb +39 -0
  156. data/spec/wayfarer/redis/counter_spec.rb +34 -0
  157. data/spec/{redis → wayfarer/redis}/pool_spec.rb +4 -3
  158. data/spec/{routing → wayfarer/routing}/dsl_spec.rb +12 -22
  159. data/spec/wayfarer/routing/hash_stack_spec.rb +63 -0
  160. data/spec/wayfarer/routing/integration_spec.rb +101 -0
  161. data/spec/wayfarer/routing/matchers/custom_spec.rb +39 -0
  162. data/spec/wayfarer/routing/matchers/host_spec.rb +56 -0
  163. data/spec/wayfarer/routing/matchers/matcher.rb +17 -0
  164. data/spec/wayfarer/routing/matchers/path_spec.rb +43 -0
  165. data/spec/wayfarer/routing/matchers/query_spec.rb +123 -0
  166. data/spec/wayfarer/routing/matchers/scheme_spec.rb +45 -0
  167. data/spec/wayfarer/routing/matchers/url_spec.rb +33 -0
  168. data/spec/wayfarer/routing/path_consumer_spec.rb +123 -0
  169. data/spec/wayfarer/routing/path_finder_spec.rb +409 -0
  170. data/spec/wayfarer/routing/root_route_spec.rb +51 -0
  171. data/spec/wayfarer/routing/route_spec.rb +74 -0
  172. data/spec/wayfarer/routing/sub_route_spec.rb +103 -0
  173. data/spec/wayfarer/task_spec.rb +13 -0
  174. data/spec/wayfarer/uri/normalization_spec.rb +98 -0
  175. data/spec/wayfarer_spec.rb +2 -2
  176. data/wayfarer.gemspec +18 -28
  177. metadata +797 -265
  178. data/.github/workflows/ci.yaml +0 -32
  179. data/.rbenv-gemsets +0 -1
  180. data/.ruby-version +0 -1
  181. data/RELEASING.md +0 -17
  182. data/docs/cookbook/user_agent.md +0 -7
  183. data/docs/guides/error_handling.md +0 -53
  184. data/docs/guides/networking.md +0 -94
  185. data/docs/guides/performance.md +0 -130
  186. data/docs/guides/reliability.md +0 -41
  187. data/docs/guides/routing/steering.md +0 -30
  188. data/docs/reference/api/base.md +0 -48
  189. data/docs/reference/cli.md +0 -61
  190. data/docs/reference/configuration_keys.md +0 -43
  191. data/docs/reference/environment_variables.md +0 -83
  192. data/lib/wayfarer/cli/base.rb +0 -45
  193. data/lib/wayfarer/cli/generate.rb +0 -17
  194. data/lib/wayfarer/cli/job.rb +0 -56
  195. data/lib/wayfarer/cli/route.rb +0 -29
  196. data/lib/wayfarer/cli/runner.rb +0 -34
  197. data/lib/wayfarer/cli/templates/Gemfile.tt +0 -5
  198. data/lib/wayfarer/cli/templates/job.rb.tt +0 -10
  199. data/lib/wayfarer/config/capybara.rb +0 -10
  200. data/lib/wayfarer/config/ferrum.rb +0 -11
  201. data/lib/wayfarer/config/networking.rb +0 -29
  202. data/lib/wayfarer/config/redis.rb +0 -14
  203. data/lib/wayfarer/config/root.rb +0 -11
  204. data/lib/wayfarer/config/selenium.rb +0 -21
  205. data/lib/wayfarer/config/strconv.rb +0 -45
  206. data/lib/wayfarer/config/struct.rb +0 -72
  207. data/lib/wayfarer/middleware/fetch.rb +0 -56
  208. data/lib/wayfarer/redis/connection.rb +0 -13
  209. data/lib/wayfarer/redis/version.rb +0 -19
  210. data/lib/wayfarer/routing/router.rb +0 -28
  211. data/spec/base_spec.rb +0 -224
  212. data/spec/callbacks_spec.rb +0 -102
  213. data/spec/cli/generate_spec.rb +0 -39
  214. data/spec/cli/job_spec.rb +0 -78
  215. data/spec/config/capybara_spec.rb +0 -18
  216. data/spec/config/ferrum_spec.rb +0 -24
  217. data/spec/config/networking_spec.rb +0 -73
  218. data/spec/config/redis_spec.rb +0 -32
  219. data/spec/config/root_spec.rb +0 -31
  220. data/spec/config/selenium_spec.rb +0 -56
  221. data/spec/config/strconv_spec.rb +0 -58
  222. data/spec/config/struct_spec.rb +0 -66
  223. data/spec/fixtures/dummy_job.rb +0 -7
  224. data/spec/gc_spec.rb +0 -59
  225. data/spec/handler_spec.rb +0 -11
  226. data/spec/integration/callbacks_spec.rb +0 -85
  227. data/spec/integration/page_spec.rb +0 -62
  228. data/spec/integration/params_spec.rb +0 -56
  229. data/spec/integration/stage_spec.rb +0 -51
  230. data/spec/integration/steering_spec.rb +0 -57
  231. data/spec/middleware/dedup_spec.rb +0 -88
  232. data/spec/middleware/dispatch_spec.rb +0 -43
  233. data/spec/middleware/fetch_spec.rb +0 -155
  234. data/spec/middleware/normalize_spec.rb +0 -29
  235. data/spec/middleware/router_spec.rb +0 -105
  236. data/spec/middleware/stage_spec.rb +0 -62
  237. data/spec/networking/capybara_spec.rb +0 -12
  238. data/spec/networking/ferrum_spec.rb +0 -12
  239. data/spec/networking/http_spec.rb +0 -12
  240. data/spec/networking/selenium_spec.rb +0 -12
  241. data/spec/page_spec.rb +0 -47
  242. data/spec/parsing/xml_spec.rb +0 -25
  243. data/spec/redis/barrier_spec.rb +0 -78
  244. data/spec/redis/counter_spec.rb +0 -32
  245. data/spec/redis/version_spec.rb +0 -13
  246. data/spec/routing/integration_spec.rb +0 -110
  247. data/spec/routing/matchers/custom_spec.rb +0 -31
  248. data/spec/routing/matchers/host_spec.rb +0 -49
  249. data/spec/routing/matchers/path_spec.rb +0 -43
  250. data/spec/routing/matchers/query_spec.rb +0 -137
  251. data/spec/routing/matchers/scheme_spec.rb +0 -25
  252. data/spec/routing/matchers/suffix_spec.rb +0 -41
  253. data/spec/routing/matchers/uri_spec.rb +0 -27
  254. data/spec/routing/path_finder_spec.rb +0 -33
  255. data/spec/routing/root_route_spec.rb +0 -29
  256. data/spec/routing/route_spec.rb +0 -43
  257. data/spec/routing/router_spec.rb +0 -24
  258. data/spec/task_spec.rb +0 -34
  259. data/spec/{stringify_spec.rb → wayfarer/stringify_spec.rb} +2 -2
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ describe Wayfarer::Base, :integration, :redis do
6
+ let(:task) { build(:task, :redis_pool) }
7
+
8
+ before do
9
+ mock_job! :dummy_job
10
+ end
11
+
12
+ describe "::crawl" do
13
+ it "enqueues a task" do
14
+ allow(DummyJob).to receive(:perform_later)
15
+ DummyJob.crawl(task.url, batch: task.batch)
16
+ expect(DummyJob).to have_received(:perform_later).with(task)
17
+ end
18
+
19
+ it "returns a task" do
20
+ expect(DummyJob.crawl(task.url)).to be_a(Wayfarer::Task)
21
+ end
22
+ end
23
+
24
+ describe "#perform" do
25
+ let(:counter) { build(:counter, task: task) }
26
+ let!(:initial) { counter.increment }
27
+
28
+ before do
29
+ allow(Wayfarer::Redis::Counter).to receive(:new).and_return(counter)
30
+ allow(counter).to receive(:increment).and_call_original
31
+
32
+ DummyJob.class_eval do
33
+ route.to :index
34
+ end
35
+ end
36
+
37
+ describe "succeeding job" do
38
+ before do
39
+ DummyJob.class_eval do
40
+ def index; end
41
+ end
42
+ end
43
+
44
+ specify do
45
+ expect {
46
+ DummyJob.perform_later(task)
47
+ perform_enqueued_jobs
48
+ assert_performed_jobs 1
49
+ expect(enqueued_jobs).to be_empty
50
+ }.not_to change(counter, :value).from(initial)
51
+
52
+ expect(counter).to have_received(:increment).at_least(:once)
53
+ end
54
+ end
55
+
56
+ describe "failing job" do
57
+ before do
58
+ DummyJob.class_eval do
59
+ def index
60
+ raise
61
+ end
62
+ end
63
+ end
64
+
65
+ specify do
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)
72
+
73
+ expect(counter).to have_received(:increment).at_least(:once)
74
+ end
75
+ end
76
+
77
+ describe "failing job with retries" do
78
+ before do
79
+ DummyJob.class_eval do
80
+ retry_on StandardError, attempts: 3
81
+
82
+ def index
83
+ raise
84
+ end
85
+ end
86
+ end
87
+
88
+ specify do
89
+ expect {
90
+ DummyJob.perform_later(task)
91
+ 2.times { perform_enqueued_jobs }
92
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
93
+ assert_performed_jobs 3
94
+ expect(enqueued_jobs).to be_empty
95
+ }.not_to change(counter, :value).from(initial)
96
+
97
+ expect(counter).to have_received(:increment).at_least(:once)
98
+ end
99
+ end
100
+
101
+ describe "discarded after retries" do
102
+ before do
103
+ stub_const("ErrorA", Class.new(StandardError))
104
+ stub_const("ErrorB", Class.new(StandardError))
105
+
106
+ DummyJob.class_eval do
107
+ retry_on ErrorA, attempts: 5
108
+ discard_on ErrorB
109
+
110
+ def index
111
+ raise ErrorB if executions == 5
112
+
113
+ raise ErrorA
114
+ end
115
+ end
116
+ end
117
+
118
+ specify do
119
+ expect {
120
+ DummyJob.perform_later(task)
121
+ 4.times { perform_enqueued_jobs }
122
+ perform_enqueued_jobs
123
+ assert_performed_jobs 5
124
+ expect(enqueued_jobs).to be_empty
125
+ }.not_to change(counter, :value).from(initial)
126
+
127
+ expect(counter).to have_received(:increment).at_least(:once)
128
+ end
129
+ end
130
+
131
+ describe "discarded job" do
132
+ before do
133
+ DummyJob.class_eval do
134
+ discard_on RuntimeError
135
+
136
+ def index
137
+ raise "boom"
138
+ end
139
+ end
140
+ end
141
+
142
+ specify do
143
+ expect {
144
+ DummyJob.perform_later(task)
145
+ perform_enqueued_jobs
146
+ assert_performed_jobs 1
147
+ expect(enqueued_jobs).to be_empty
148
+ }.not_to change(counter, :value).from(initial)
149
+
150
+ expect(counter).to have_received(:increment).at_least(:once)
151
+ end
152
+ end
153
+
154
+ describe "after_batch callback" do
155
+ let(:initial) { counter.value }
156
+
157
+ describe "when after_perform callback fails" do
158
+ before do
159
+ DummyJob.class_eval do
160
+ after_perform { raise }
161
+
162
+ def index; end
163
+ end
164
+ end
165
+
166
+ specify do
167
+ expect {
168
+ DummyJob.perform_later(task)
169
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
170
+ assert_performed_jobs 1
171
+ expect(enqueued_jobs).to be_empty
172
+ }.not_to change(counter, :value).from(initial)
173
+
174
+ expect(counter).to have_received(:increment).at_least(:once)
175
+ end
176
+ end
177
+
178
+ describe "when after_batch callback fails" do
179
+ before do
180
+ DummyJob.class_eval do
181
+ after_batch { raise }
182
+
183
+ def index; end
184
+ end
185
+ end
186
+
187
+ specify do
188
+ expect {
189
+ DummyJob.perform_later(task)
190
+ expect { perform_enqueued_jobs }.to raise_error(RuntimeError)
191
+ assert_performed_jobs 1
192
+ expect(enqueued_jobs).to be_empty
193
+ }.not_to change(counter, :value).from(initial)
194
+
195
+ expect(counter).to have_received(:increment).at_least(:once)
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ # The RSpec/SpecFilePathFormat offense was not addressed as it requires renaming
6
+ # the file, not changing its content.
7
+ describe Wayfarer::BatchCompletion, :redis do
8
+ let(:task) { build(:task, :redis_pool) }
9
+ let(:job) { instance_spy(ActiveJob::Base, arguments: [task]) }
10
+
11
+ describe "::call" do
12
+ subject(:call_batch_completion) { described_class.call(name, nil, nil, nil, { job: job }) }
13
+
14
+ let(:name) { "foo" }
15
+
16
+ context "with Wayfarer job" do
17
+ before { job.extend(Wayfarer::Base) }
18
+
19
+ specify do
20
+ allow(described_class).to receive(:handle)
21
+
22
+ call_batch_completion
23
+
24
+ expect(described_class).to have_received(:handle).with(name, job, task)
25
+ end
26
+
27
+ it "does not reassign Redis pool" do
28
+ expect { call_batch_completion }.not_to change { task[:redis_pool] }.from(Wayfarer::Redis::Pool.instance)
29
+ end
30
+
31
+ context "without Redis pool" do
32
+ before do
33
+ task[:redis_pool] = nil
34
+ end
35
+
36
+ it "assigns Redis pool" do
37
+ expect { call_batch_completion }.to change { task[:redis_pool] }.from(nil).to(Wayfarer::Redis::Pool.instance)
38
+ end
39
+ end
40
+ end
41
+
42
+ context "with other job" do
43
+ specify do
44
+ allow(described_class).to receive(:handle)
45
+
46
+ call_batch_completion
47
+
48
+ expect(described_class).not_to have_received(:handle)
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "::handle" do
54
+ subject(:handle) { described_class.handle(event, job, task) }
55
+
56
+ let(:job) do
57
+ instance_spy(ActiveJob::Base,
58
+ arguments: [task],
59
+ executions: executions,
60
+ exception_executions: exception_executions)
61
+ end
62
+
63
+ let(:initial_exception_executions) { {} }
64
+ let(:exception_executions) { initial_exception_executions }
65
+ let(:executions) { 0 }
66
+ let(:counter) { build(:counter, task: task) }
67
+
68
+ before do
69
+ task[:job] = job
70
+ task[:initial_exception_executions] = initial_exception_executions
71
+ end
72
+
73
+ context "with an 'enqueue.active_job' event" do
74
+ let(:event) { "enqueue.active_job" }
75
+
76
+ specify do
77
+ expect { handle }.to change(counter, :value).by(1)
78
+ end
79
+
80
+ context "with retry" do
81
+ let(:executions) { 1 }
82
+
83
+ specify do
84
+ expect { handle }.not_to(change(counter, :value))
85
+ end
86
+ end
87
+ end
88
+
89
+ context "with a 'perform.active_job' event" do
90
+ let(:event) { "perform.active_job" }
91
+
92
+ context "when job succeeds" do
93
+ specify do
94
+ expect { handle }.to change(counter, :value).by(-1)
95
+ end
96
+
97
+ context "when batch completes" do
98
+ before do
99
+ counter.increment
100
+ allow(Wayfarer::GC).to receive(:run).and_call_original
101
+ end
102
+
103
+ specify do
104
+ handle
105
+
106
+ expect(job).to have_received(:run_callbacks)
107
+ expect(Wayfarer::GC).to have_received(:run).with(task)
108
+ end
109
+ end
110
+
111
+ context "when batch does not complete" do
112
+ before do
113
+ allow(Wayfarer::GC).to receive(:run)
114
+ end
115
+
116
+ specify do
117
+ handle
118
+
119
+ expect(job).not_to have_received(:run_callbacks)
120
+ expect(Wayfarer::GC).not_to have_received(:run)
121
+ end
122
+ end
123
+ end
124
+
125
+ context "when an exception occurrs" do
126
+ let(:exception_executions) { { "[RuntimeError]" => 1 } }
127
+
128
+ specify do
129
+ expect { handle }.not_to(change(counter, :value))
130
+ end
131
+ end
132
+ end
133
+
134
+ context "with a 'retry_stopped.active_job' event" do
135
+ let(:event) { "retry_stopped.active_job" }
136
+
137
+ specify do
138
+ expect { handle }.to change(counter, :value).by(-1)
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helpers"
4
+
5
+ describe "Jobs", :cli, :integration, :redis do
6
+ include Wayfarer::Redis
7
+
8
+ subject(:cli) { Wayfarer::CLI }
9
+
10
+ let(:url) { test_app_path("/hello_world") }
11
+ let(:batch) { "my-batch" }
12
+
13
+ before do
14
+ mock_job! :dummy_job
15
+
16
+ write_file "dummy_job.rb", <<~FILE
17
+ Wayfarer::CLI.new.send(:load_environment, "dummy_job.rb")
18
+ class DummyJob < ActiveJob::Base
19
+ include Wayfarer::Base
20
+ end
21
+ FILE
22
+ end
23
+
24
+ describe "perform" do
25
+ it "performs the worker" do
26
+ # rubocop:disable RSpec/AnyInstance
27
+ # The CLI reloads DummyJob from file. #expect_any_instance_of alters the
28
+ # object initialization in a way that survives this class reloading.
29
+ expect_any_instance_of(DummyJob).to receive(:perform).with(kind_of(Wayfarer::Task)) do |job|
30
+ # rubocop:enable RSpec/AnyInstance
31
+ task = job.arguments.first
32
+ task[:job] = job
33
+ end
34
+
35
+ cli.start(["perform", "-r", "dummy_job.rb", "DummyJob", url])
36
+ end
37
+
38
+ context "when using MockRedis" do
39
+ it "performs the worker using MockRedis" do
40
+ cli.start(["perform", "--mock-redis", "DummyJob", url])
41
+ expect(Wayfarer.config[:redis][:factory].call(nil)).to be_a(MockRedis)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe "enqueue" do
47
+ before do
48
+ allow(DummyJob).to receive(:crawl)
49
+ end
50
+
51
+ it "enqueues the job" do
52
+ cli.start(["enqueue", "DummyJob", url])
53
+ expect(DummyJob).to have_received(:crawl).with(Addressable::URI.parse(url), batch: kind_of(String))
54
+ end
55
+
56
+ context "with a batch provided" do
57
+ it "enqueues the job" do
58
+ cli.start(["enqueue", "--batch", batch, "DummyJob", url])
59
+ expect(DummyJob).to have_received(:crawl).with(Addressable::URI.parse(url), batch: batch)
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "execute" do
65
+ before do
66
+ allow(DummyJob).to receive(:crawl)
67
+ end
68
+
69
+ it "executes the job" do
70
+ cli.start(["execute", "--retain-pool", "DummyJob", url])
71
+ expect(DummyJob).to have_received(:crawl).with(Addressable::URI.parse(url), batch: kind_of(String))
72
+ end
73
+
74
+ context "with a batch provided" do
75
+ it "executes the job" do
76
+ cli.start(["execute", "--retain-pool", "--batch", batch, "DummyJob", url])
77
+ expect(DummyJob).to have_received(:crawl).with(Addressable::URI.parse(url), batch: batch)
78
+ end
79
+ end
80
+
81
+ context "with mock Redis" do
82
+ it "executes the worker using MockRedis" do
83
+ cli.start(["execute", "--retain-pool", "--mock-redis", "DummyJob", url])
84
+ expect(Wayfarer.config[:redis][:factory].call(nil)).to be_a(MockRedis)
85
+ end
86
+ end
87
+ end
88
+ end