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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/CONTRIBUTING.md +0 -0
  4. data/Dangerfile +2 -0
  5. data/Gemfile +2 -1
  6. data/Gemfile.lock +202 -83
  7. data/LICENSE.md +0 -0
  8. data/README.md +73 -3
  9. data/RELEASING.md +0 -0
  10. data/Rakefile +0 -0
  11. data/benchmarks/check.rb +27 -20
  12. data/benchmarks/data/opensearch.org/links.txt +0 -0
  13. data/benchmarks/data/opensearch.org/small.txt +0 -0
  14. data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_asc.png +0 -0
  15. data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  16. data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_both.png +0 -0
  17. data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_desc.png +0 -0
  18. data/coverage/assets/{0.13.1 → 0.13.2}/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  19. data/coverage/assets/{0.13.1 → 0.13.2}/application.css +0 -0
  20. data/coverage/assets/{0.13.1 → 0.13.2}/application.js +0 -0
  21. data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/border.png +0 -0
  22. data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/controls.png +0 -0
  23. data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/loading.gif +0 -0
  24. data/coverage/assets/{0.13.1 → 0.13.2}/colorbox/loading_background.png +0 -0
  25. data/coverage/assets/{0.13.1 → 0.13.2}/favicon_green.png +0 -0
  26. data/coverage/assets/{0.13.1 → 0.13.2}/favicon_red.png +0 -0
  27. data/coverage/assets/{0.13.1 → 0.13.2}/favicon_yellow.png +0 -0
  28. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  29. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  30. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  31. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  32. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  33. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  34. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  35. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  36. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_222222_256x240.png +0 -0
  37. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_2e83ff_256x240.png +0 -0
  38. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_454545_256x240.png +0 -0
  39. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_888888_256x240.png +0 -0
  40. data/coverage/assets/{0.13.1 → 0.13.2}/images/ui-icons_cd0a0a_256x240.png +0 -0
  41. data/coverage/assets/{0.13.1 → 0.13.2}/loading.gif +0 -0
  42. data/coverage/assets/{0.13.1 → 0.13.2}/magnify.png +0 -0
  43. data/coverage/index.html +2857 -7125
  44. data/lib/ruby-link-checker/async/http/checker.rb +92 -0
  45. data/lib/ruby-link-checker/async/http/config.rb +35 -0
  46. data/lib/ruby-link-checker/async/http/result.rb +81 -0
  47. data/lib/ruby-link-checker/async/http.rb +3 -0
  48. data/lib/ruby-link-checker/callbacks.rb +2 -2
  49. data/lib/ruby-link-checker/checker.rb +0 -0
  50. data/lib/ruby-link-checker/config.rb +2 -0
  51. data/lib/ruby-link-checker/errors/base_error.rb +0 -0
  52. data/lib/ruby-link-checker/errors/redirect_loop_error.rb +0 -0
  53. data/lib/ruby-link-checker/errors.rb +0 -0
  54. data/lib/ruby-link-checker/logger.rb +0 -0
  55. data/lib/ruby-link-checker/net/http/checker.rb +2 -0
  56. data/lib/ruby-link-checker/net/http/config.rb +0 -0
  57. data/lib/ruby-link-checker/net/http/result.rb +0 -0
  58. data/lib/ruby-link-checker/net/http.rb +0 -0
  59. data/lib/ruby-link-checker/result.rb +0 -0
  60. data/lib/ruby-link-checker/task.rb +0 -0
  61. data/lib/ruby-link-checker/tasks.rb +4 -2
  62. data/lib/ruby-link-checker/typhoeus/hydra/checker.rb +2 -1
  63. data/lib/ruby-link-checker/typhoeus/hydra/config.rb +0 -0
  64. data/lib/ruby-link-checker/typhoeus/hydra/result.rb +0 -0
  65. data/lib/ruby-link-checker/typhoeus/hydra.rb +0 -0
  66. data/lib/ruby-link-checker/version.rb +1 -1
  67. data/lib/ruby-link-checker.rb +1 -0
  68. data/lib/ruby_link_checker.rb +0 -0
  69. data/ruby-link-checker.gemspec +2 -1
  70. data/spec/fixtures/200.yml +0 -0
  71. data/spec/fixtures/301_200.yml +0 -0
  72. data/spec/fixtures/301_301.yml +0 -0
  73. data/spec/fixtures/301_400_301_200.yml +0 -0
  74. data/spec/fixtures/301_403.yml +0 -0
  75. data/spec/fixtures/308_429.yml +0 -0
  76. data/spec/fixtures/404.yml +0 -0
  77. data/spec/fixtures/404_200.yml +0 -0
  78. data/spec/fixtures/404_404.yml +0 -0
  79. data/spec/fixtures/429_200.yml +0 -0
  80. data/spec/fixtures/429_429_200.yml +0 -0
  81. data/spec/ruby-link-checker/async/http/checker_spec.rb +99 -0
  82. data/spec/ruby-link-checker/callbacks_spec.rb +33 -0
  83. data/spec/ruby-link-checker/checker_spec.rb +0 -0
  84. data/spec/ruby-link-checker/config_spec.rb +0 -0
  85. data/spec/ruby-link-checker/errors/redirect_loop_error_spec.rb +18 -0
  86. data/spec/ruby-link-checker/net/http/checker_spec.rb +17 -0
  87. data/spec/ruby-link-checker/result_error_spec.rb +17 -0
  88. data/spec/ruby-link-checker/result_spec.rb +41 -0
  89. data/spec/ruby-link-checker/task_spec.rb +20 -0
  90. data/spec/ruby-link-checker/tasks_spec.rb +47 -0
  91. data/spec/ruby-link-checker/typhoeus/hydra/checker_spec.rb +28 -2
  92. data/spec/ruby-link-checker/version_spec.rb +0 -0
  93. data/spec/spec_helper.rb +3 -1
  94. data/spec/support/config.rb +0 -0
  95. data/spec/support/link_checker.rb +24 -0
  96. data/spec/support/vcr.rb +0 -0
  97. data/spec/support/with_result.rb +0 -0
  98. data/spec/support/with_url.rb +0 -0
  99. 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
@@ -0,0 +1,3 @@
1
+ require_relative 'http/config'
2
+ require_relative 'http/result'
3
+ require_relative 'http/checker'
@@ -20,9 +20,9 @@ module LinkChecker
20
20
  end
21
21
  end
22
22
 
23
- def method_missing(m, *args, &block)
23
+ def method_missing(m, *, &)
24
24
  if m.to_s[-1] == '!'
25
- callback(m.to_s[...-1].to_sym, *args)
25
+ callback(m.to_s[...-1].to_sym, *)
26
26
  else
27
27
  super
28
28
  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
- _handle_result ResultError.new(result.uri, result.method, result.result_uri, e, options)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LinkChecker
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -11,3 +11,4 @@ require_relative 'ruby-link-checker/checker'
11
11
  require_relative 'ruby-link-checker/result'
12
12
  require_relative 'ruby-link-checker/net/http'
13
13
  require_relative 'ruby-link-checker/typhoeus/hydra'
14
+ require_relative 'ruby-link-checker/async/http'
File without changes
@@ -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 = '>= 2.7'
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
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