wayfarer 0.4.7 → 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.
- checksums.yaml +4 -4
- data/.env +17 -0
- data/.github/workflows/lint.yaml +8 -6
- data/.github/workflows/release.yaml +4 -3
- data/.github/workflows/tests.yaml +5 -14
- data/.gitignore +2 -2
- data/.rubocop.yml +31 -0
- data/.vale.ini +6 -3
- data/Dockerfile +3 -2
- data/Gemfile +21 -0
- data/Gemfile.lock +233 -128
- data/Rakefile +7 -0
- data/docker-compose.yml +13 -14
- data/docs/guides/callbacks.md +3 -1
- data/docs/guides/configuration.md +10 -35
- data/docs/guides/development.md +67 -0
- data/docs/guides/handlers.md +7 -7
- data/docs/guides/jobs.md +54 -11
- data/docs/guides/networking/custom_adapters.md +31 -10
- data/docs/guides/pages.md +24 -22
- data/docs/guides/routing.md +116 -34
- data/docs/guides/tasks.md +30 -10
- data/docs/guides/tutorial.md +23 -17
- data/docs/guides/user_agents.md +11 -9
- data/lib/wayfarer/base.rb +9 -8
- data/lib/wayfarer/batch_completion.rb +18 -14
- data/lib/wayfarer/callbacks.rb +14 -14
- data/lib/wayfarer/cli/route_printer.rb +78 -96
- data/lib/wayfarer/cli.rb +12 -30
- data/lib/wayfarer/gc.rb +6 -1
- data/lib/wayfarer/kv.rb +28 -0
- data/lib/wayfarer/middleware/chain.rb +7 -1
- data/lib/wayfarer/middleware/content_type.rb +20 -15
- data/lib/wayfarer/middleware/dedup.rb +9 -3
- data/lib/wayfarer/middleware/dispatch.rb +7 -2
- data/lib/wayfarer/middleware/normalize.rb +4 -12
- data/lib/wayfarer/middleware/router.rb +1 -1
- data/lib/wayfarer/middleware/uri_parser.rb +4 -3
- data/lib/wayfarer/networking/context.rb +12 -1
- data/lib/wayfarer/networking/ferrum.rb +1 -4
- data/lib/wayfarer/networking/follow.rb +2 -1
- data/lib/wayfarer/networking/pool.rb +12 -7
- data/lib/wayfarer/networking/selenium.rb +15 -7
- data/lib/wayfarer/page.rb +0 -2
- data/lib/wayfarer/parsing/xml.rb +1 -1
- data/lib/wayfarer/parsing.rb +2 -5
- data/lib/wayfarer/redis/barrier.rb +15 -2
- data/lib/wayfarer/redis/counter.rb +1 -2
- data/lib/wayfarer/routing/dsl.rb +166 -31
- data/lib/wayfarer/routing/hash_stack.rb +33 -0
- data/lib/wayfarer/routing/matchers/custom.rb +8 -5
- data/lib/wayfarer/routing/matchers/{suffix.rb → empty_params.rb} +2 -6
- data/lib/wayfarer/routing/matchers/host.rb +15 -9
- data/lib/wayfarer/routing/matchers/path.rb +11 -33
- data/lib/wayfarer/routing/matchers/query.rb +41 -17
- data/lib/wayfarer/routing/matchers/result.rb +12 -0
- data/lib/wayfarer/routing/matchers/scheme.rb +13 -5
- data/lib/wayfarer/routing/matchers/url.rb +13 -5
- data/lib/wayfarer/routing/path_consumer.rb +130 -0
- data/lib/wayfarer/routing/path_finder.rb +151 -23
- data/lib/wayfarer/routing/result.rb +1 -1
- data/lib/wayfarer/routing/root_route.rb +14 -2
- data/lib/wayfarer/routing/route.rb +71 -14
- data/lib/wayfarer/routing/serializable.rb +28 -0
- data/lib/wayfarer/routing/sub_route.rb +53 -0
- data/lib/wayfarer/routing/target_route.rb +17 -1
- data/lib/wayfarer/stringify.rb +1 -2
- data/lib/wayfarer/task.rb +3 -5
- data/lib/wayfarer/uri/normalization.rb +120 -0
- data/lib/wayfarer.rb +50 -10
- data/mise.toml +2 -0
- data/mkdocs.yml +8 -17
- data/rake/lint.rake +0 -96
- data/rake/release.rake +5 -11
- data/rake/tests.rake +8 -4
- data/requirements.txt +1 -1
- data/spec/factories/job.rb +8 -0
- data/spec/factories/middleware.rb +2 -2
- data/spec/factories/path_finder.rb +11 -0
- data/spec/factories/redis.rb +19 -0
- data/spec/factories/task.rb +39 -1
- data/spec/spec_helpers.rb +50 -57
- data/spec/support/active_job_helpers.rb +8 -0
- data/spec/support/integration_helpers.rb +21 -0
- data/spec/support/redis_helpers.rb +9 -0
- data/spec/support/test_app.rb +64 -43
- data/spec/{base_spec.rb → wayfarer/base_spec.rb} +32 -36
- data/spec/wayfarer/batch_completion_spec.rb +142 -0
- data/spec/wayfarer/cli/job_spec.rb +88 -0
- data/spec/wayfarer/cli/routing_spec.rb +322 -0
- data/spec/{cli → wayfarer/cli}/version_spec.rb +1 -1
- data/spec/wayfarer/gc_spec.rb +29 -0
- data/spec/{handler_spec.rb → wayfarer/handler_spec.rb} +1 -3
- data/spec/{integration → wayfarer/integration}/callbacks_spec.rb +9 -6
- data/spec/wayfarer/integration/content_type_spec.rb +37 -0
- data/spec/wayfarer/integration/custom_routing_spec.rb +51 -0
- data/spec/{integration → wayfarer/integration}/gc_spec.rb +9 -13
- data/spec/{integration → wayfarer/integration}/handler_spec.rb +9 -10
- data/spec/{integration → wayfarer/integration}/page_spec.rb +8 -6
- data/spec/{integration → wayfarer/integration}/params_spec.rb +4 -4
- data/spec/{integration → wayfarer/integration}/parsing_spec.rb +7 -33
- data/spec/wayfarer/integration/retry_spec.rb +112 -0
- data/spec/{integration → wayfarer/integration}/stage_spec.rb +5 -5
- data/spec/{middleware → wayfarer/middleware}/batch_completion_spec.rb +4 -5
- data/spec/{middleware → wayfarer/middleware}/chain_spec.rb +20 -15
- data/spec/{middleware → wayfarer/middleware}/content_type_spec.rb +18 -21
- data/spec/{middleware → wayfarer/middleware}/controller_spec.rb +22 -20
- data/spec/wayfarer/middleware/dedup_spec.rb +66 -0
- data/spec/wayfarer/middleware/normalize_spec.rb +32 -0
- data/spec/{middleware → wayfarer/middleware}/router_spec.rb +18 -20
- data/spec/{middleware → wayfarer/middleware}/stage_spec.rb +11 -10
- data/spec/wayfarer/middleware/uri_parser_spec.rb +63 -0
- data/spec/{middleware → wayfarer/middleware}/user_agent_spec.rb +34 -32
- data/spec/wayfarer/networking/capybara_spec.rb +13 -0
- data/spec/{networking → wayfarer/networking}/context_spec.rb +46 -38
- data/spec/wayfarer/networking/ferrum_spec.rb +13 -0
- data/spec/{networking → wayfarer/networking}/follow_spec.rb +9 -4
- data/spec/wayfarer/networking/http_spec.rb +12 -0
- data/spec/{networking → wayfarer/networking}/pool_spec.rb +11 -9
- data/spec/wayfarer/networking/selenium_spec.rb +12 -0
- data/spec/{networking → wayfarer/networking}/strategy.rb +33 -54
- data/spec/{page_spec.rb → wayfarer/page_spec.rb} +3 -3
- data/spec/{parsing → wayfarer/parsing}/json_spec.rb +1 -1
- data/spec/{parsing/xml_spec.rb → wayfarer/parsing/xml_parse_spec.rb} +4 -3
- data/spec/{redis → wayfarer/redis}/barrier_spec.rb +5 -4
- data/spec/wayfarer/redis/counter_spec.rb +34 -0
- data/spec/{redis → wayfarer/redis}/pool_spec.rb +3 -2
- data/spec/{routing → wayfarer/routing}/dsl_spec.rb +12 -22
- data/spec/wayfarer/routing/hash_stack_spec.rb +63 -0
- data/spec/wayfarer/routing/integration_spec.rb +101 -0
- data/spec/wayfarer/routing/matchers/custom_spec.rb +39 -0
- data/spec/wayfarer/routing/matchers/host_spec.rb +56 -0
- data/spec/wayfarer/routing/matchers/matcher.rb +17 -0
- data/spec/wayfarer/routing/matchers/path_spec.rb +43 -0
- data/spec/wayfarer/routing/matchers/query_spec.rb +123 -0
- data/spec/wayfarer/routing/matchers/scheme_spec.rb +45 -0
- data/spec/wayfarer/routing/matchers/url_spec.rb +33 -0
- data/spec/wayfarer/routing/path_consumer_spec.rb +123 -0
- data/spec/wayfarer/routing/path_finder_spec.rb +409 -0
- data/spec/wayfarer/routing/root_route_spec.rb +51 -0
- data/spec/wayfarer/routing/route_spec.rb +74 -0
- data/spec/wayfarer/routing/sub_route_spec.rb +103 -0
- data/spec/wayfarer/uri/normalization_spec.rb +98 -0
- data/spec/wayfarer_spec.rb +2 -2
- data/wayfarer.gemspec +17 -28
- metadata +768 -246
- data/.rbenv-gemsets +0 -1
- data/.ruby-version +0 -1
- data/RELEASING.md +0 -17
- data/docs/cookbook/user_agent.md +0 -7
- data/docs/design.md +0 -36
- data/docs/guides/jobs/error_handling.md +0 -40
- data/docs/reference/configuration.md +0 -36
- data/spec/batch_completion_spec.rb +0 -104
- data/spec/cli/job_spec.rb +0 -74
- data/spec/cli/routing_spec.rb +0 -101
- data/spec/fixtures/dummy_job.rb +0 -9
- data/spec/gc_spec.rb +0 -17
- data/spec/integration/content_type_spec.rb +0 -145
- data/spec/integration/routing_spec.rb +0 -18
- data/spec/middleware/dedup_spec.rb +0 -71
- data/spec/middleware/dispatch_spec.rb +0 -59
- data/spec/middleware/normalize_spec.rb +0 -60
- data/spec/middleware/uri_parser_spec.rb +0 -53
- data/spec/networking/capybara_spec.rb +0 -12
- data/spec/networking/ferrum_spec.rb +0 -12
- data/spec/networking/http_spec.rb +0 -12
- data/spec/networking/selenium_spec.rb +0 -12
- data/spec/redis/counter_spec.rb +0 -44
- data/spec/routing/integration_spec.rb +0 -110
- data/spec/routing/matchers/custom_spec.rb +0 -31
- data/spec/routing/matchers/host_spec.rb +0 -49
- data/spec/routing/matchers/path_spec.rb +0 -43
- data/spec/routing/matchers/query_spec.rb +0 -137
- data/spec/routing/matchers/scheme_spec.rb +0 -25
- data/spec/routing/matchers/suffix_spec.rb +0 -41
- data/spec/routing/matchers/uri_spec.rb +0 -27
- data/spec/routing/path_finder_spec.rb +0 -33
- data/spec/routing/root_route_spec.rb +0 -29
- data/spec/routing/route_spec.rb +0 -43
- data/docs/{reference → guides}/cli.md +0 -0
- data/spec/{stringify_spec.rb → wayfarer/stringify_spec.rb} +2 -2
- /data/spec/{task_spec.rb → wayfarer/task_spec.rb} +0 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helpers"
|
4
|
+
|
5
|
+
describe "Custom routing", :redis do
|
6
|
+
let(:task) { build(:task) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
stub_const("DummyJob", Class.new(ActiveJob::Base).include(Wayfarer::Base))
|
10
|
+
DummyJob.class_eval do
|
11
|
+
class_attribute :action,
|
12
|
+
:params
|
13
|
+
|
14
|
+
route.custom do |root, uri, _task|
|
15
|
+
if uri.host == "example.com"
|
16
|
+
root.path ":segment" do
|
17
|
+
path "users", path: "/:id", to: :foo
|
18
|
+
end
|
19
|
+
else
|
20
|
+
root.to :bar
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
after_action do
|
25
|
+
self.class.params = params
|
26
|
+
end
|
27
|
+
|
28
|
+
def foo
|
29
|
+
self.class.action = :foo
|
30
|
+
end
|
31
|
+
|
32
|
+
def bar
|
33
|
+
self.class.action = :bar
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
specify do
|
39
|
+
DummyJob.crawl("https://example.com/foobar/users/42")
|
40
|
+
perform_enqueued_jobs
|
41
|
+
expect(DummyJob.action).to be(:foo)
|
42
|
+
assert_performed_jobs 1
|
43
|
+
expect(enqueued_jobs).to be_empty
|
44
|
+
|
45
|
+
DummyJob.crawl(test_app_path("/"))
|
46
|
+
perform_enqueued_jobs
|
47
|
+
expect(DummyJob.action).to be(:bar)
|
48
|
+
assert_performed_jobs 2
|
49
|
+
expect(enqueued_jobs).to be_empty
|
50
|
+
end
|
51
|
+
end
|
@@ -2,32 +2,28 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe "Garbage collection", redis
|
5
|
+
describe "Garbage collection", :redis do
|
6
|
+
subject(:perform) do
|
7
|
+
DummyJob.perform_later(task)
|
8
|
+
perform_enqueued_jobs
|
9
|
+
end
|
10
|
+
|
6
11
|
let(:task) { build(:task, :redis_pool) }
|
7
|
-
let(:counter) {
|
8
|
-
let(:barrier) {
|
9
|
-
let(:redis) { ::Redis.new(url: redis_host) }
|
12
|
+
let(:counter) { build(:counter, task: task) }
|
13
|
+
let(:barrier) { build(:barrier, task: task) }
|
10
14
|
|
11
15
|
before do
|
12
|
-
|
16
|
+
mock_job! :dummy_job
|
13
17
|
|
14
18
|
DummyJob.class_eval do
|
15
19
|
route.to :index
|
16
20
|
|
17
21
|
def index; end
|
18
22
|
end
|
19
|
-
end
|
20
|
-
|
21
|
-
before do
|
22
23
|
redis.set(counter.redis_key, 0)
|
23
24
|
redis.hset(barrier.redis_key, "", "")
|
24
25
|
end
|
25
26
|
|
26
|
-
subject(:perform) do
|
27
|
-
DummyJob.perform_later(task)
|
28
|
-
perform_enqueued_jobs
|
29
|
-
end
|
30
|
-
|
31
27
|
it "resets counter" do
|
32
28
|
expect { perform }.to change { redis.exists?(counter.redis_key) }.from(true).to(false)
|
33
29
|
|
@@ -2,11 +2,13 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe "Handlers", redis
|
5
|
+
describe "Handlers", :redis do
|
6
6
|
before do
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
mock_job! :dummy_job
|
8
|
+
mock_job! :other_job
|
9
|
+
|
10
|
+
mock_handler! :dummy_handler
|
11
|
+
mock_handler! :other_handler
|
10
12
|
end
|
11
13
|
|
12
14
|
describe "bypassing the router" do
|
@@ -45,11 +47,6 @@ describe "Handlers", redis: true do
|
|
45
47
|
end
|
46
48
|
|
47
49
|
describe "dispatching to another Wayfarer::Base" do
|
48
|
-
before do
|
49
|
-
stub_const("DummyJob", Class.new(ActiveJob::Base).include(Wayfarer::Base))
|
50
|
-
stub_const("OtherJob", Class.new(ActiveJob::Base).include(Wayfarer::Base))
|
51
|
-
end
|
52
|
-
|
53
50
|
before do
|
54
51
|
DummyJob.class_eval do
|
55
52
|
extend SpecHelpers
|
@@ -58,9 +55,11 @@ describe "Handlers", redis: true do
|
|
58
55
|
end
|
59
56
|
end
|
60
57
|
|
58
|
+
let(:error) { Wayfarer::Middleware::Dispatch::InvalidTargetError }
|
59
|
+
|
61
60
|
specify do
|
62
61
|
DummyJob.crawl(test_app_path("/"))
|
63
|
-
expect { perform_enqueued_jobs }.to raise_error(
|
62
|
+
expect { perform_enqueued_jobs }.to raise_error(error, "routed to invalid action: #{OtherJob}")
|
64
63
|
end
|
65
64
|
end
|
66
65
|
end
|
@@ -2,14 +2,14 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe "Pages" do
|
6
|
-
let(:url) { test_app_path("git-scm.com/book/en/v2.html") }
|
7
|
-
|
5
|
+
describe "Pages", :redis do
|
8
6
|
before do
|
9
|
-
|
10
|
-
|
7
|
+
mock_job! :dummy_job
|
8
|
+
mock_handler! :dummy_handler
|
11
9
|
end
|
12
10
|
|
11
|
+
let(:url) { test_app_path("git-scm.com/book/en/v2.html") }
|
12
|
+
|
13
13
|
shared_examples "executes" do
|
14
14
|
specify do
|
15
15
|
DummyJob.crawl(url)
|
@@ -46,6 +46,8 @@ describe "Pages" do
|
|
46
46
|
|
47
47
|
describe "page content with handler" do
|
48
48
|
before do
|
49
|
+
handler = DummyHandler
|
50
|
+
|
49
51
|
DummyJob.class_eval do
|
50
52
|
include RSpec::Matchers
|
51
53
|
include SpecHelpers
|
@@ -68,7 +70,7 @@ describe "Pages" do
|
|
68
70
|
extend SpecHelpers
|
69
71
|
include SpecHelpers
|
70
72
|
|
71
|
-
route.host test_app_host, to:
|
73
|
+
route.host test_app_host, to: handler
|
72
74
|
end
|
73
75
|
end
|
74
76
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe "
|
5
|
+
describe "Path parameters", :redis do
|
6
6
|
let(:url) { test_app_path("git-scm.com/book/en/v2.html") }
|
7
7
|
|
8
8
|
before do
|
@@ -25,7 +25,7 @@ describe "URL parameters" do
|
|
25
25
|
extend SpecHelpers
|
26
26
|
include RSpec::Matchers
|
27
27
|
|
28
|
-
route.to :index, host: test_app_host do
|
28
|
+
route.to :index, host: test_app_host, scheme: :https do
|
29
29
|
path "git-scm.com/book/:lang/:file"
|
30
30
|
end
|
31
31
|
|
@@ -58,7 +58,7 @@ describe "URL parameters" do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
-
end
|
62
61
|
|
63
|
-
|
62
|
+
it_behaves_like "executes"
|
63
|
+
end
|
64
64
|
end
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe "Response body parsing", redis
|
5
|
+
describe "Response body parsing", :redis do
|
6
6
|
before do
|
7
|
-
|
8
|
-
|
7
|
+
mock_job! :dummy_job
|
8
|
+
mock_handler! :dummy_handler
|
9
9
|
end
|
10
10
|
|
11
11
|
shared_examples "executes" do
|
@@ -17,24 +17,6 @@ describe "Response body parsing", redis: true do
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
context "with XML" do
|
21
|
-
let(:url) { test_app_path("xml/dummy.xml") }
|
22
|
-
|
23
|
-
before do
|
24
|
-
DummyJob.class_eval do
|
25
|
-
include RSpec::Matchers
|
26
|
-
|
27
|
-
route.to :index
|
28
|
-
|
29
|
-
def index
|
30
|
-
expect(page.doc).to be_a(Nokogiri::XML::Document)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
it_behaves_like "executes"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
20
|
context "with HTML" do
|
39
21
|
let(:url) { test_app_path("finders.html") }
|
40
22
|
|
@@ -90,16 +72,8 @@ describe "Response body parsing", redis: true do
|
|
90
72
|
end
|
91
73
|
|
92
74
|
before do
|
93
|
-
Wayfarer
|
94
|
-
Wayfarer
|
95
|
-
end
|
96
|
-
|
97
|
-
after do
|
98
|
-
Wayfarer::Parsing.registry.delete("foo/bar")
|
99
|
-
Wayfarer::Parsing.registry.delete("bar/qux")
|
100
|
-
end
|
101
|
-
|
102
|
-
before do
|
75
|
+
Wayfarer.config[:parsing][:registry]["foo/bar"] = parser
|
76
|
+
Wayfarer.config[:parsing][:registry]["bar/qux"] = [parser_with_options, :ok]
|
103
77
|
DummyJob.class_eval do
|
104
78
|
route.to :index
|
105
79
|
|
@@ -115,10 +89,10 @@ describe "Response body parsing", redis: true do
|
|
115
89
|
)
|
116
90
|
end
|
117
91
|
|
118
|
-
specify do
|
92
|
+
specify :aggregate_failures do
|
119
93
|
expect(perform("foo/bar")).to be(:ok)
|
120
94
|
expect(perform("bar/qux")).to be(:ok)
|
121
|
-
expect(perform("image/jpeg")).to
|
95
|
+
expect(perform("image/jpeg")).to be_nil
|
122
96
|
end
|
123
97
|
end
|
124
98
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helpers"
|
4
|
+
|
5
|
+
describe "Retries", :redis do
|
6
|
+
# rubocop:disable RSpec/MultipleExpectations
|
7
|
+
let(:task) { build(:task, :normalized, :redis_pool) }
|
8
|
+
let(:counter) { build(:counter, task: task) }
|
9
|
+
let(:barrier) { build(:barrier, task: task) }
|
10
|
+
|
11
|
+
before do
|
12
|
+
stub_const("DummyJob", Class.new(ActiveJob::Base).include(Wayfarer::Base))
|
13
|
+
|
14
|
+
Wayfarer::Redis::Counter.new(task).increment # avoid garbage collection
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when job fails" do
|
18
|
+
before do
|
19
|
+
DummyJob.class_eval do
|
20
|
+
class_attribute :attempts, default: 0
|
21
|
+
|
22
|
+
retry_on RuntimeError, wait: 0, attempts: 3
|
23
|
+
|
24
|
+
route.to :index
|
25
|
+
|
26
|
+
def index
|
27
|
+
self.class.attempts += 1
|
28
|
+
|
29
|
+
raise
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
specify do
|
35
|
+
DummyJob.perform_later(task)
|
36
|
+
|
37
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(0).to(1)
|
38
|
+
|
39
|
+
assert_performed_jobs 1
|
40
|
+
expect(enqueued_jobs.pluck("executions")).to contain_exactly(1)
|
41
|
+
|
42
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(1).to(2)
|
43
|
+
assert_performed_jobs 2
|
44
|
+
expect(enqueued_jobs.pluck("executions")).to contain_exactly(2)
|
45
|
+
|
46
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(2).to(3).and raise_error(RuntimeError)
|
47
|
+
assert_performed_jobs 3
|
48
|
+
expect(enqueued_jobs).to be_empty
|
49
|
+
|
50
|
+
expect(redis.hget(barrier.redis_key, task[:uri].to_s)).to eq(Wayfarer::Redis::Barrier::VALUE)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when job fails and eventually succeeds" do
|
55
|
+
before do
|
56
|
+
DummyJob.class_eval do
|
57
|
+
class_attribute :attempts, default: 0
|
58
|
+
|
59
|
+
retry_on RuntimeError, wait: 0, attempts: 3
|
60
|
+
|
61
|
+
route.to :index
|
62
|
+
|
63
|
+
def index
|
64
|
+
raise if [1, 2].include?(self.class.attempts += 1)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
specify do
|
70
|
+
DummyJob.perform_later(task)
|
71
|
+
|
72
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(0).to(1)
|
73
|
+
assert_performed_jobs 1
|
74
|
+
expect(enqueued_jobs.pluck("executions")).to contain_exactly(1)
|
75
|
+
|
76
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(1).to(2)
|
77
|
+
assert_performed_jobs 2
|
78
|
+
expect(enqueued_jobs.pluck("executions")).to contain_exactly(2)
|
79
|
+
|
80
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(2).to(3)
|
81
|
+
assert_performed_jobs 3
|
82
|
+
expect(enqueued_jobs).to be_empty
|
83
|
+
|
84
|
+
expect(redis.hget(barrier.redis_key, task[:uri].to_s)).to eq(Wayfarer::Redis::Barrier::VALUE)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "when job succeeds" do
|
89
|
+
before do
|
90
|
+
DummyJob.class_eval do
|
91
|
+
class_attribute :attempts, default: 0
|
92
|
+
|
93
|
+
route.to :index
|
94
|
+
|
95
|
+
def index
|
96
|
+
self.class.attempts += 1
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
specify do
|
102
|
+
DummyJob.perform_later(task)
|
103
|
+
|
104
|
+
expect { perform_enqueued_jobs }.to change(DummyJob, :attempts).from(0).to(1)
|
105
|
+
assert_performed_jobs 1
|
106
|
+
expect(enqueued_jobs).to be_empty
|
107
|
+
|
108
|
+
expect(redis.hget(barrier.redis_key, task[:uri].to_s)).to eq(Wayfarer::Redis::Barrier::VALUE)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
# rubocop:enable RSpec/MultipleExpectations
|
112
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe "Staging", redis
|
5
|
+
describe "Staging", :redis do
|
6
6
|
let(:url) { test_app_path("git-scm.com/book/en/v2.html") }
|
7
7
|
|
8
8
|
before do
|
@@ -15,7 +15,7 @@ describe "Staging", redis: true do
|
|
15
15
|
DummyJob.class_eval do
|
16
16
|
extend SpecHelpers
|
17
17
|
|
18
|
-
route.host
|
18
|
+
route.host test_app_hostname, to: :index
|
19
19
|
|
20
20
|
def index
|
21
21
|
stage page.meta.links.all
|
@@ -27,7 +27,7 @@ describe "Staging", redis: true do
|
|
27
27
|
expect {
|
28
28
|
DummyJob.crawl(url)
|
29
29
|
perform_enqueued_jobs
|
30
|
-
}.to change
|
30
|
+
}.to change(enqueued_jobs, :size).by(157)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -36,7 +36,7 @@ describe "Staging", redis: true do
|
|
36
36
|
DummyJob.class_eval do
|
37
37
|
extend SpecHelpers
|
38
38
|
|
39
|
-
route.host
|
39
|
+
route.host test_app_hostname, to: DummyHandler
|
40
40
|
end
|
41
41
|
|
42
42
|
DummyHandler.class_eval do
|
@@ -52,7 +52,7 @@ describe "Staging", redis: true do
|
|
52
52
|
expect {
|
53
53
|
DummyJob.crawl(url)
|
54
54
|
perform_enqueued_jobs
|
55
|
-
}.to change
|
55
|
+
}.to change(enqueued_jobs, :size).by(157)
|
56
56
|
end
|
57
57
|
end
|
58
58
|
end
|
@@ -2,13 +2,12 @@
|
|
2
2
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
|
-
describe Wayfarer::Middleware::BatchCompletion
|
6
|
-
let(:task) { build(:task) }
|
7
|
-
let(:job) { double(exception_executions: exception_executions) }
|
8
|
-
let(:exception_executions) { {} }
|
5
|
+
describe Wayfarer::Middleware::BatchCompletion do
|
9
6
|
subject(:batch_completion) { described_class.new }
|
10
7
|
|
11
|
-
|
8
|
+
let(:task) { build(:task, job: job) }
|
9
|
+
let(:job) { build(:job, exception_executions: exception_executions) }
|
10
|
+
let(:exception_executions) { {} }
|
12
11
|
|
13
12
|
it "assigns cloned exception_executions" do
|
14
13
|
expect {
|
@@ -3,9 +3,10 @@
|
|
3
3
|
require "spec_helpers"
|
4
4
|
|
5
5
|
describe Wayfarer::Middleware::Chain do
|
6
|
+
subject(:chain) { described_class.new(middlewares) }
|
7
|
+
|
6
8
|
let(:task) { build(:task) }
|
7
9
|
let(:middlewares) { [] }
|
8
|
-
subject(:chain) { described_class.new(middlewares) }
|
9
10
|
|
10
11
|
describe "::empty" do
|
11
12
|
it "returns an empty chain" do
|
@@ -13,7 +14,7 @@ describe Wayfarer::Middleware::Chain do
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
describe "#
|
17
|
+
describe "#push" do
|
17
18
|
it "adds middleware" do
|
18
19
|
expect {
|
19
20
|
chain.push(Class.new)
|
@@ -35,20 +36,23 @@ describe Wayfarer::Middleware::Chain do
|
|
35
36
|
context "when middleware yields" do
|
36
37
|
let(:middlewares) { [spy, spy] }
|
37
38
|
|
38
|
-
it "yields" do
|
39
|
+
it "yields", :aggregate_failures do
|
39
40
|
allow(middlewares.first).to receive(:call).and_yield
|
40
|
-
|
41
|
+
allow(middlewares.last).to receive(:call).with(task).and_yield
|
42
|
+
|
41
43
|
expect { |spy| chain.call(task, &spy) }.to yield_control
|
44
|
+
expect(middlewares.last).to have_received(:call).with(task)
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
48
|
context "when middleware does not yield" do
|
46
49
|
let(:middlewares) { [spy, spy] }
|
47
50
|
|
48
|
-
it "does not yield" do
|
51
|
+
it "does not yield", :aggregate_failures do
|
49
52
|
allow(middlewares.first).to receive(:call)
|
50
|
-
|
53
|
+
|
51
54
|
expect { |spy| chain.call(task, &spy) }.not_to yield_control
|
55
|
+
expect(middlewares.last).not_to have_received(:call)
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
@@ -58,7 +62,8 @@ describe Wayfarer::Middleware::Chain do
|
|
58
62
|
it "does not alter its middleware" do
|
59
63
|
allow(middlewares.first).to receive(:call).and_yield
|
60
64
|
allow(middlewares.second).to receive(:call).and_yield
|
61
|
-
|
65
|
+
|
66
|
+
expect { chain.call(task) }.not_to(change(chain, :middlewares))
|
62
67
|
end
|
63
68
|
end
|
64
69
|
|
@@ -81,22 +86,22 @@ describe Wayfarer::Middleware::Chain do
|
|
81
86
|
describe "Metadata" do
|
82
87
|
let(:first) do
|
83
88
|
build(:middleware, receiver: lambda do |task, &block|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
89
|
+
task[:foobar] = 42
|
90
|
+
block.call
|
91
|
+
task[:barqux] = 1337
|
92
|
+
end)
|
88
93
|
end
|
89
94
|
|
90
95
|
let(:last) do
|
91
96
|
build(:middleware, receiver: lambda do |task|
|
92
|
-
|
93
|
-
|
94
|
-
|
97
|
+
raise unless task[:foobar] == 42
|
98
|
+
raise if task[:barqux]
|
99
|
+
end)
|
95
100
|
end
|
96
101
|
|
97
102
|
let(:middlewares) { [first, last] }
|
98
103
|
|
99
|
-
it "is accessible across middleware" do
|
104
|
+
it "is accessible across middleware", :aggregate_failures do
|
100
105
|
expect {
|
101
106
|
chain.call(task)
|
102
107
|
}.not_to raise_error
|
@@ -4,66 +4,63 @@ require "spec_helpers"
|
|
4
4
|
|
5
5
|
describe Wayfarer::Middleware::ContentType do
|
6
6
|
let(:content_type) { "text/html" }
|
7
|
-
let(:task) { build(:task) }
|
7
|
+
let(:task) { build(:task, :normalized) }
|
8
8
|
let(:page) { build(:page, headers: { "Content-Type" => content_type }) }
|
9
9
|
|
10
10
|
describe "#call" do
|
11
|
-
subject { Class.new(described_class).include(described_class::API).new }
|
11
|
+
subject(:middleware) { Class.new(described_class).include(described_class::API).new }
|
12
12
|
|
13
13
|
before do
|
14
14
|
task[:page] = page
|
15
|
-
task[:controller] =
|
15
|
+
task[:controller] = middleware
|
16
|
+
middleware.class.content_type "text/html"
|
16
17
|
end
|
17
18
|
|
18
|
-
before { subject.class.content_type "text/html" }
|
19
|
-
|
20
19
|
context "with permitted Content-Type" do
|
21
|
-
|
22
|
-
expect { |spy|
|
20
|
+
specify do
|
21
|
+
expect { |spy| middleware.call(task, &spy) }.to yield_control
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
25
|
context "when permitted Content-Type has parameters" do
|
27
|
-
let(:page) { build(:page, headers: { "Content-Type" => "#{content_type}; charset=UTF-" }) }
|
26
|
+
let(:page) { build(:page, headers: { "Content-Type" => "#{content_type}; charset=UTF-8" }) }
|
28
27
|
|
29
|
-
|
30
|
-
expect { |spy|
|
28
|
+
specify do
|
29
|
+
expect { |spy| middleware.call(task, &spy) }.to yield_control
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
33
|
context "with forbidden Content-Type" do
|
35
34
|
let(:content_type) { "application/json" }
|
36
35
|
|
37
|
-
|
38
|
-
expect { |spy|
|
36
|
+
specify do
|
37
|
+
expect { |spy| middleware.call(task, &spy) }.not_to yield_control
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
42
41
|
context "with permitted Regexp Content-Type" do
|
43
42
|
before do
|
44
|
-
|
43
|
+
middleware.class.content_type(/text/)
|
45
44
|
end
|
46
45
|
|
47
|
-
|
48
|
-
expect { |spy|
|
46
|
+
specify do
|
47
|
+
expect { |spy| middleware.call(task, &spy) }.to yield_control
|
49
48
|
end
|
50
49
|
end
|
51
50
|
end
|
52
51
|
|
53
52
|
describe described_class::API do
|
54
|
-
subject(:controller)
|
55
|
-
Struct.new(:task).include(described_class).new(task)
|
56
|
-
end
|
53
|
+
subject(:controller) { Class.new.include(described_class).new }
|
57
54
|
|
58
55
|
describe "::allowed_content_types" do
|
59
56
|
describe "index" do
|
60
|
-
subject { controller.class.allowed_content_types[:index] }
|
57
|
+
subject(:index) { controller.class.allowed_content_types[:index] }
|
61
58
|
|
62
59
|
it { is_expected.to be_empty }
|
63
60
|
end
|
64
61
|
|
65
62
|
describe "patterns" do
|
66
|
-
subject { controller.class.allowed_content_types[:patterns] }
|
63
|
+
subject(:patterns) { controller.class.allowed_content_types[:patterns] }
|
67
64
|
|
68
65
|
it { is_expected.to be_empty }
|
69
66
|
end
|
@@ -73,7 +70,7 @@ describe Wayfarer::Middleware::ContentType do
|
|
73
70
|
it "allows Content-Types" do
|
74
71
|
controller.class.content_type(content_type)
|
75
72
|
|
76
|
-
expect(controller.class.allowed_content_types
|
73
|
+
expect(controller.class.allowed_content_types.dig(:index, content_type)).to be(true)
|
77
74
|
end
|
78
75
|
|
79
76
|
it "allows Content-Type patterns" do
|