ruby-link-checker 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +12 -0
- data/CONTRIBUTING.md +0 -0
- data/Dangerfile +2 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +202 -83
- data/LICENSE.md +0 -0
- data/README.md +73 -3
- data/RELEASING.md +0 -0
- data/Rakefile +0 -0
- data/benchmarks/check.rb +27 -20
- data/benchmarks/data/opensearch.org/links.txt +0 -0
- data/benchmarks/data/opensearch.org/small.txt +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_asc.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_both.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_desc.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/application.css +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/application.js +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/border.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/controls.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/loading.gif +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/loading_background.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/favicon_green.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/favicon_red.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/favicon_yellow.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_222222_256x240.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_2e83ff_256x240.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_454545_256x240.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_888888_256x240.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/loading.gif +0 -0
- data/coverage/assets/{0.13.1 → 0.13.2}/magnify.png +0 -0
- data/coverage/index.html +2857 -7125
- data/lib/ruby-link-checker/async/http/checker.rb +92 -0
- data/lib/ruby-link-checker/async/http/config.rb +35 -0
- data/lib/ruby-link-checker/async/http/result.rb +81 -0
- data/lib/ruby-link-checker/async/http.rb +3 -0
- data/lib/ruby-link-checker/callbacks.rb +2 -2
- data/lib/ruby-link-checker/checker.rb +0 -0
- data/lib/ruby-link-checker/config.rb +2 -0
- data/lib/ruby-link-checker/errors/base_error.rb +0 -0
- data/lib/ruby-link-checker/errors/redirect_loop_error.rb +0 -0
- data/lib/ruby-link-checker/errors.rb +0 -0
- data/lib/ruby-link-checker/logger.rb +0 -0
- data/lib/ruby-link-checker/net/http/checker.rb +2 -0
- data/lib/ruby-link-checker/net/http/config.rb +0 -0
- data/lib/ruby-link-checker/net/http/result.rb +0 -0
- data/lib/ruby-link-checker/net/http.rb +0 -0
- data/lib/ruby-link-checker/result.rb +0 -0
- data/lib/ruby-link-checker/task.rb +0 -0
- data/lib/ruby-link-checker/tasks.rb +4 -2
- data/lib/ruby-link-checker/typhoeus/hydra/checker.rb +2 -1
- data/lib/ruby-link-checker/typhoeus/hydra/config.rb +0 -0
- data/lib/ruby-link-checker/typhoeus/hydra/result.rb +0 -0
- data/lib/ruby-link-checker/typhoeus/hydra.rb +0 -0
- data/lib/ruby-link-checker/version.rb +1 -1
- data/lib/ruby-link-checker.rb +1 -0
- data/lib/ruby_link_checker.rb +0 -0
- data/ruby-link-checker.gemspec +2 -1
- data/spec/fixtures/200.yml +0 -0
- data/spec/fixtures/301_200.yml +0 -0
- data/spec/fixtures/301_301.yml +0 -0
- data/spec/fixtures/301_400_301_200.yml +0 -0
- data/spec/fixtures/301_403.yml +0 -0
- data/spec/fixtures/308_429.yml +0 -0
- data/spec/fixtures/404.yml +0 -0
- data/spec/fixtures/404_200.yml +0 -0
- data/spec/fixtures/404_404.yml +0 -0
- data/spec/fixtures/429_200.yml +0 -0
- data/spec/fixtures/429_429_200.yml +0 -0
- data/spec/ruby-link-checker/async/http/checker_spec.rb +99 -0
- data/spec/ruby-link-checker/callbacks_spec.rb +33 -0
- data/spec/ruby-link-checker/checker_spec.rb +0 -0
- data/spec/ruby-link-checker/config_spec.rb +0 -0
- data/spec/ruby-link-checker/errors/redirect_loop_error_spec.rb +18 -0
- data/spec/ruby-link-checker/net/http/checker_spec.rb +17 -0
- data/spec/ruby-link-checker/result_error_spec.rb +17 -0
- data/spec/ruby-link-checker/result_spec.rb +41 -0
- data/spec/ruby-link-checker/task_spec.rb +20 -0
- data/spec/ruby-link-checker/tasks_spec.rb +47 -0
- data/spec/ruby-link-checker/typhoeus/hydra/checker_spec.rb +28 -2
- data/spec/ruby-link-checker/version_spec.rb +0 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/config.rb +0 -0
- data/spec/support/link_checker.rb +24 -0
- data/spec/support/vcr.rb +0 -0
- data/spec/support/with_result.rb +0 -0
- data/spec/support/with_url.rb +0 -0
- metadata +57 -37
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module LinkChecker
|
|
2
|
+
module Async
|
|
3
|
+
module HTTP
|
|
4
|
+
class Task < ::LinkChecker::Task
|
|
5
|
+
def run!
|
|
6
|
+
checker._queue { _run! }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def _run!
|
|
12
|
+
client = checker._client_for(uri)
|
|
13
|
+
headers = ::Protocol::HTTP::Headers[
|
|
14
|
+
[['user-agent', checker.user_agent]] + checker.headers.map { |key, value| [key, value] }
|
|
15
|
+
]
|
|
16
|
+
request = ::Protocol::HTTP::Request[method, uri.request_uri, headers]
|
|
17
|
+
response = _call(client, request)
|
|
18
|
+
result! Result.new(uri, method, original_uri, request, response, options)
|
|
19
|
+
rescue StandardError => e
|
|
20
|
+
result! ResultError.new(uri, method, original_uri, e, options)
|
|
21
|
+
ensure
|
|
22
|
+
response&.close
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def _call(client, request)
|
|
26
|
+
if checker.read_timeout
|
|
27
|
+
::Async::Task.current.with_timeout(checker.read_timeout) { client.call(request) }
|
|
28
|
+
else
|
|
29
|
+
client.call(request)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Checker < LinkChecker::Checker
|
|
35
|
+
extend ::LinkChecker::Async::HTTP::Config
|
|
36
|
+
|
|
37
|
+
attr_accessor(*LinkChecker::Async::HTTP::Config::ATTRIBUTES)
|
|
38
|
+
|
|
39
|
+
def initialize(options = {})
|
|
40
|
+
LinkChecker::Async::HTTP::Config::ATTRIBUTES.each do |key|
|
|
41
|
+
send("#{key}=", options[key] || LinkChecker::Async::HTTP::Config.send(key))
|
|
42
|
+
end
|
|
43
|
+
@queue = []
|
|
44
|
+
@clients = {}
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Runs all queued checks concurrently and blocks until they complete.
|
|
49
|
+
def run
|
|
50
|
+
Barrier do |barrier|
|
|
51
|
+
@barrier = barrier
|
|
52
|
+
_flush!
|
|
53
|
+
end
|
|
54
|
+
ensure
|
|
55
|
+
@barrier = nil
|
|
56
|
+
_close_clients!
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def _queue(&block)
|
|
60
|
+
if @barrier
|
|
61
|
+
@barrier.async(&block)
|
|
62
|
+
else
|
|
63
|
+
@queue << block
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns a client for the given URI's host/port/scheme, reusing (and
|
|
68
|
+
# pooling connections on) one client per endpoint across all checks.
|
|
69
|
+
def _client_for(uri)
|
|
70
|
+
key = [uri.scheme, uri.host, uri.port]
|
|
71
|
+
@clients[key] ||= begin
|
|
72
|
+
endpoint = ::Async::HTTP::Endpoint.parse(uri.to_s, timeout: open_timeout)
|
|
73
|
+
::Async::HTTP::Client.new(endpoint)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def _flush!
|
|
80
|
+
queue = @queue
|
|
81
|
+
@queue = []
|
|
82
|
+
queue.each { |block| @barrier.async(&block) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def _close_clients!
|
|
86
|
+
@clients.each_value(&:close)
|
|
87
|
+
@clients = {}
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LinkChecker
|
|
4
|
+
module Async
|
|
5
|
+
module HTTP
|
|
6
|
+
module Config
|
|
7
|
+
extend self
|
|
8
|
+
|
|
9
|
+
ATTRIBUTES = %i[
|
|
10
|
+
read_timeout
|
|
11
|
+
open_timeout
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
attr_accessor(*Config::ATTRIBUTES)
|
|
15
|
+
|
|
16
|
+
def reset
|
|
17
|
+
self.read_timeout = nil
|
|
18
|
+
self.open_timeout = nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def configure
|
|
24
|
+
block_given? ? yield(Config) : Config
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def config
|
|
28
|
+
Config
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
LinkChecker::Async::HTTP::Config.reset
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
module LinkChecker
|
|
2
|
+
module Async
|
|
3
|
+
module HTTP
|
|
4
|
+
# A thin, case-insensitive wrapper around Protocol::HTTP::Headers,
|
|
5
|
+
# since Protocol::HTTP::Headers#[] expects a lower-cased key.
|
|
6
|
+
class Headers
|
|
7
|
+
def initialize(headers)
|
|
8
|
+
@headers = headers
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def [](key)
|
|
12
|
+
value = @headers[key.to_s.downcase]
|
|
13
|
+
value.is_a?(Array) ? value.join(', ') : value
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Wraps an Async::HTTP response to expose a `code` and case-insensitive
|
|
18
|
+
# `headers`, similar to the other checkers' response objects.
|
|
19
|
+
class Response
|
|
20
|
+
attr_reader :status, :headers
|
|
21
|
+
|
|
22
|
+
def initialize(response)
|
|
23
|
+
@status = response.status
|
|
24
|
+
@headers = Headers.new(response.headers)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def code
|
|
28
|
+
@status.to_s
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class Result < ::LinkChecker::Result
|
|
33
|
+
attr_accessor :request, :response
|
|
34
|
+
|
|
35
|
+
def initialize(uri, method, original_uri, request, response, options)
|
|
36
|
+
@request = request
|
|
37
|
+
@response = response && Response.new(response)
|
|
38
|
+
super(uri, method, original_uri, options)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def error?
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def failure?
|
|
46
|
+
!success? && !redirect?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def code
|
|
50
|
+
@code ||= begin
|
|
51
|
+
response.status
|
|
52
|
+
rescue StandardError
|
|
53
|
+
-1
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def request_headers
|
|
58
|
+
Headers.new(request.headers)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def redirect_to
|
|
62
|
+
return nil unless response
|
|
63
|
+
|
|
64
|
+
response.headers['location']
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def redirect?
|
|
68
|
+
return false unless response
|
|
69
|
+
|
|
70
|
+
[301, 302, 303, 307, 308].include?(code)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def success?
|
|
74
|
+
return false unless response
|
|
75
|
+
|
|
76
|
+
code >= 200 && code <= 299
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
File without changes
|
|
@@ -7,6 +7,7 @@ module LinkChecker
|
|
|
7
7
|
ATTRIBUTES = %i[
|
|
8
8
|
methods
|
|
9
9
|
user_agent
|
|
10
|
+
headers
|
|
10
11
|
logger
|
|
11
12
|
retries
|
|
12
13
|
].freeze
|
|
@@ -16,6 +17,7 @@ module LinkChecker
|
|
|
16
17
|
def reset
|
|
17
18
|
self.methods = %w[HEAD GET]
|
|
18
19
|
self.user_agent = "Ruby Link Checker/#{LinkChecker::VERSION}"
|
|
20
|
+
self.headers = {}
|
|
19
21
|
self.logger = nil
|
|
20
22
|
self.retries = 0
|
|
21
23
|
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -8,6 +8,7 @@ module LinkChecker
|
|
|
8
8
|
http.open_timeout = checker.open_timeout if checker.open_timeout
|
|
9
9
|
request = ::Net::HTTPGenericRequest.new(method, false, true, uri)
|
|
10
10
|
request['User-Agent'] = checker.user_agent
|
|
11
|
+
checker.headers.each { |key, value| request[key] = value }
|
|
11
12
|
response = http.request(request)
|
|
12
13
|
result! Result.new(uri, method, original_uri, request, response, options)
|
|
13
14
|
end
|
|
@@ -16,6 +17,7 @@ module LinkChecker
|
|
|
16
17
|
|
|
17
18
|
class Checker < LinkChecker::Checker
|
|
18
19
|
extend ::LinkChecker::Net::HTTP::Config
|
|
20
|
+
|
|
19
21
|
attr_accessor(*LinkChecker::Net::HTTP::Config::ATTRIBUTES)
|
|
20
22
|
|
|
21
23
|
def initialize(options = {})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -103,8 +103,10 @@ module LinkChecker
|
|
|
103
103
|
execute!
|
|
104
104
|
end
|
|
105
105
|
rescue StandardError => e
|
|
106
|
-
logger.error("#{self}##{__method__}") { e }
|
|
107
|
-
|
|
106
|
+
logger.error("#{self}##{__method__}") { e } rescue nil
|
|
107
|
+
error_result = ResultError.new(result.uri, result.method, result.result_uri, e, options)
|
|
108
|
+
result! error_result
|
|
109
|
+
error! error_result
|
|
108
110
|
end
|
|
109
111
|
end
|
|
110
112
|
end
|
|
@@ -11,7 +11,7 @@ module LinkChecker
|
|
|
11
11
|
connecttimeout: checker.connecttimeout,
|
|
12
12
|
headers: {
|
|
13
13
|
'User-Agent' => checker.user_agent
|
|
14
|
-
}
|
|
14
|
+
}.merge(checker.headers)
|
|
15
15
|
}
|
|
16
16
|
)
|
|
17
17
|
request.on_complete do |response|
|
|
@@ -27,6 +27,7 @@ module LinkChecker
|
|
|
27
27
|
|
|
28
28
|
class Checker < LinkChecker::Checker
|
|
29
29
|
extend ::LinkChecker::Typhoeus::Hydra::Config
|
|
30
|
+
|
|
30
31
|
attr_accessor(*LinkChecker::Typhoeus::Hydra::Config::ATTRIBUTES)
|
|
31
32
|
|
|
32
33
|
def initialize(options = {})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
data/lib/ruby-link-checker.rb
CHANGED
data/lib/ruby_link_checker.rb
CHANGED
|
File without changes
|
data/ruby-link-checker.gemspec
CHANGED
|
@@ -10,12 +10,13 @@ Gem::Specification.new do |s|
|
|
|
10
10
|
s.email = 'dblock@dblock.org'
|
|
11
11
|
s.platform = Gem::Platform::RUBY
|
|
12
12
|
s.required_rubygems_version = '>= 1.3.6'
|
|
13
|
-
s.required_ruby_version = '>=
|
|
13
|
+
s.required_ruby_version = '>= 3.3'
|
|
14
14
|
s.files = Dir['**/*']
|
|
15
15
|
s.require_paths = ['lib']
|
|
16
16
|
s.homepage = 'http://github.com/dblock/ruby-link-checker'
|
|
17
17
|
s.licenses = ['MIT']
|
|
18
18
|
s.summary = 'Fast ruby link checker.'
|
|
19
19
|
s.metadata['rubygems_mfa_required'] = 'true'
|
|
20
|
+
s.add_development_dependency 'async-http'
|
|
20
21
|
s.add_development_dependency 'typhoeus'
|
|
21
22
|
end
|
data/spec/fixtures/200.yml
CHANGED
|
File without changes
|
data/spec/fixtures/301_200.yml
CHANGED
|
File without changes
|
data/spec/fixtures/301_301.yml
CHANGED
|
File without changes
|
|
File without changes
|
data/spec/fixtures/301_403.yml
CHANGED
|
File without changes
|
data/spec/fixtures/308_429.yml
CHANGED
|
File without changes
|
data/spec/fixtures/404.yml
CHANGED
|
File without changes
|
data/spec/fixtures/404_200.yml
CHANGED
|
File without changes
|
data/spec/fixtures/404_404.yml
CHANGED
|
File without changes
|
data/spec/fixtures/429_200.yml
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
require 'async'
|
|
5
|
+
require 'async/http'
|
|
6
|
+
|
|
7
|
+
describe LinkChecker::Async::HTTP::Checker do
|
|
8
|
+
module AsyncTestLinkChecker
|
|
9
|
+
class Task < LinkChecker::Async::HTTP::Task; end
|
|
10
|
+
|
|
11
|
+
class LinkChecker < LinkChecker::Async::HTTP::Checker
|
|
12
|
+
def check(url, options = {})
|
|
13
|
+
super
|
|
14
|
+
run
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
before :all do
|
|
20
|
+
VCR.configure do |config|
|
|
21
|
+
config.hook_into :webmock
|
|
22
|
+
# async-http's WebMock adapter replays responses through a real HTTP/1
|
|
23
|
+
# parser, which rejects a Transfer-Encoding header alongside a
|
|
24
|
+
# Content-Length or an empty body, unlike the other checkers' adapters.
|
|
25
|
+
config.before_playback do |interaction|
|
|
26
|
+
interaction.response.headers.delete('Transfer-Encoding')
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
after do
|
|
32
|
+
LinkChecker::Async::HTTP::Config.reset
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe AsyncTestLinkChecker::LinkChecker do
|
|
36
|
+
it_behaves_like 'a link checker'
|
|
37
|
+
|
|
38
|
+
describe '.config' do
|
|
39
|
+
it 'returns the Config module' do
|
|
40
|
+
expect(LinkChecker::Async::HTTP.config).to eq LinkChecker::Async::HTTP::Config
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'with timeout options', vcr: { cassette_name: '200' } do
|
|
45
|
+
before do
|
|
46
|
+
LinkChecker::Async::HTTP.configure do |config|
|
|
47
|
+
config.read_timeout = 5
|
|
48
|
+
config.open_timeout = 10
|
|
49
|
+
end
|
|
50
|
+
expect(Async::HTTP::Endpoint).to receive(:parse).with(
|
|
51
|
+
URI(url).to_s,
|
|
52
|
+
hash_including(timeout: 10)
|
|
53
|
+
).and_call_original
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
include_context 'with url'
|
|
57
|
+
|
|
58
|
+
it 'creates requests with a default timeout' do
|
|
59
|
+
expect(result.success?).to be true
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'timeout' do
|
|
64
|
+
before do
|
|
65
|
+
stub_request(:get, 'https://www.example.org/').to_timeout
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
include_context 'with url'
|
|
69
|
+
|
|
70
|
+
around do |example|
|
|
71
|
+
VCR.turned_off { example.run }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'times out' do
|
|
75
|
+
expect(result.success?).to be false
|
|
76
|
+
expect(result.error?).to be true
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context 'with metadata' do
|
|
80
|
+
let(:options) { { foo: :bar } }
|
|
81
|
+
|
|
82
|
+
it 'times out' do
|
|
83
|
+
expect(result.error?).to be true
|
|
84
|
+
expect(result.options).to eq(foo: :bar)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe LinkChecker::Async::HTTP::Result do
|
|
91
|
+
describe '#code' do
|
|
92
|
+
it 'returns -1 when there is no response' do
|
|
93
|
+
request = instance_double(Protocol::HTTP::Request)
|
|
94
|
+
result = described_class.new(URI('https://www.example.org'), 'GET', nil, request, nil, {})
|
|
95
|
+
expect(result.code).to eq(-1)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -6,6 +6,10 @@ describe LinkChecker::Callbacks do
|
|
|
6
6
|
subject do
|
|
7
7
|
Class.new do
|
|
8
8
|
include LinkChecker::Callbacks
|
|
9
|
+
|
|
10
|
+
def logger
|
|
11
|
+
@logger ||= Logger.new(nil)
|
|
12
|
+
end
|
|
9
13
|
end.new
|
|
10
14
|
end
|
|
11
15
|
|
|
@@ -40,4 +44,33 @@ describe LinkChecker::Callbacks do
|
|
|
40
44
|
expect(subject).to have_received(:called!).with(:bar)
|
|
41
45
|
end
|
|
42
46
|
end
|
|
47
|
+
|
|
48
|
+
context 'method_missing' do
|
|
49
|
+
it 'invokes callback for a bang method' do
|
|
50
|
+
allow(subject).to receive(:called!)
|
|
51
|
+
subject.on :foo do |data|
|
|
52
|
+
subject.called! data
|
|
53
|
+
end
|
|
54
|
+
subject.foo! :foo
|
|
55
|
+
expect(subject).to have_received(:called!).with(:foo)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'raises NoMethodError for a non-bang method' do
|
|
59
|
+
expect { subject.foo }.to raise_error NoMethodError
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
context 'when a callback raises an error' do
|
|
64
|
+
before do
|
|
65
|
+
subject.on :foo do
|
|
66
|
+
raise StandardError, 'oops'
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'rescues and logs the error' do
|
|
71
|
+
allow(subject.logger).to receive(:error)
|
|
72
|
+
expect(subject.send(:callback, :foo)).to be false
|
|
73
|
+
expect(subject.logger).to have_received(:error)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
43
76
|
end
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe LinkChecker::Errors::RedirectLoopError do
|
|
6
|
+
subject(:error) do
|
|
7
|
+
described_class.new(['https://www.example.org', 'https://www.example.org/redirect'])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'returns the last url' do
|
|
11
|
+
expect(error.url).to eq 'https://www.example.org/redirect'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'formats a message with all urls' do
|
|
15
|
+
expect(error.message)
|
|
16
|
+
.to eq 'Redirect loop: https://www.example.org -> https://www.example.org/redirect.'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -15,6 +15,12 @@ describe LinkChecker::Net::HTTP::Checker do
|
|
|
15
15
|
|
|
16
16
|
it_behaves_like 'a link checker'
|
|
17
17
|
|
|
18
|
+
describe '.config' do
|
|
19
|
+
it 'returns the Config module' do
|
|
20
|
+
expect(LinkChecker::Net::HTTP.config).to eq LinkChecker::Net::HTTP::Config
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
18
24
|
context 'with timeout options', vcr: { cassette_name: '200' } do
|
|
19
25
|
before do
|
|
20
26
|
LinkChecker::Net::HTTP.configure do |config|
|
|
@@ -58,4 +64,15 @@ describe LinkChecker::Net::HTTP::Checker do
|
|
|
58
64
|
end
|
|
59
65
|
end
|
|
60
66
|
end
|
|
67
|
+
|
|
68
|
+
describe LinkChecker::Net::HTTP::Result do
|
|
69
|
+
describe '#code' do
|
|
70
|
+
it 'returns -1 when the response raises' do
|
|
71
|
+
response = instance_double(Net::HTTPResponse)
|
|
72
|
+
allow(response).to receive(:code).and_raise(StandardError)
|
|
73
|
+
result = described_class.new(URI('https://www.example.org'), 'GET', nil, nil, response, {})
|
|
74
|
+
expect(result.code).to eq(-1)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
61
78
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe LinkChecker::ResultError do
|
|
6
|
+
subject(:result) do
|
|
7
|
+
described_class.new('https://www.example.org', 'GET', 'https://www.example.org', StandardError.new('oops'))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'is an error' do
|
|
11
|
+
expect(result.error?).to be true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'returns the error class name as code' do
|
|
15
|
+
expect(result.code).to eq 'StandardError'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe LinkChecker::Result do
|
|
6
|
+
subject(:result) do
|
|
7
|
+
described_class.new('https://www.example.org', 'GET', 'https://www.example.org')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'defaults to not successful' do
|
|
11
|
+
expect(result.success?).to be false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'defaults to not a failure' do
|
|
15
|
+
expect(result.failure?).to be false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'defaults to not an error' do
|
|
19
|
+
expect(result.error?).to be false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it 'defaults to not a redirect' do
|
|
23
|
+
expect(result.redirect?).to be false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'defaults redirect_to to nil' do
|
|
27
|
+
expect(result.redirect_to).to be_nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'defaults request_headers to an empty hash' do
|
|
31
|
+
expect(result.request_headers).to eq({})
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'defaults code to nil' do
|
|
35
|
+
expect(result.code).to be_nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'defaults error to nil' do
|
|
39
|
+
expect(result.error).to be_nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe LinkChecker::Task do
|
|
6
|
+
subject(:task) do
|
|
7
|
+
described_class.new(
|
|
8
|
+
LinkChecker::Net::HTTP::Checker.new,
|
|
9
|
+
'https://www.example.org',
|
|
10
|
+
'GET',
|
|
11
|
+
'https://www.example.org'
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '#run!' do
|
|
16
|
+
it 'raises NotImplementedError' do
|
|
17
|
+
expect { task.run! }.to raise_error NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|