ndd-url_checker 0.1.1
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 +7 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +50 -0
- data/Gemfile.lock +152 -0
- data/Guardfile +38 -0
- data/LICENSE.txt +20 -0
- data/README.md +19 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/lib/ndd/url_checker.rb +6 -0
- data/lib/ndd/url_checker/abstract_url_checker.rb +30 -0
- data/lib/ndd/url_checker/blocking_url_checker.rb +134 -0
- data/lib/ndd/url_checker/forked_url_checker.rb +100 -0
- data/lib/ndd/url_checker/parallel_url_checker.rb +45 -0
- data/lib/ndd/url_checker/status.rb +104 -0
- data/lib/ndd/url_checker/threaded_url_checker.rb +36 -0
- data/spec/ndd/url_checker/abstract_url_checker_spec.rb +17 -0
- data/spec/ndd/url_checker/blocking_url_checker_spec.rb +12 -0
- data/spec/ndd/url_checker/forked_url_checker_spec.rb +12 -0
- data/spec/ndd/url_checker/parallel_url_checker_spec.rb +62 -0
- data/spec/ndd/url_checker/status_spec.rb +510 -0
- data/spec/ndd/url_checker/threaded_url_checker_spec.rb +13 -0
- data/spec/spec_helper.rb +83 -0
- data/spec/support/multiple_url_checker_spec.rb +66 -0
- data/spec/support/single_url_checker_spec.rb +207 -0
- metadata +312 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'cod'
|
2
|
+
require 'logging'
|
3
|
+
require 'ndd/url_checker/abstract_url_checker'
|
4
|
+
require 'ndd/url_checker/blocking_url_checker'
|
5
|
+
require 'ndd/url_checker/status'
|
6
|
+
|
7
|
+
module NDD
|
8
|
+
module UrlChecker
|
9
|
+
|
10
|
+
# An URL checker using forks to parallelize processing. To be used with MRI.
|
11
|
+
# @author David DIDIER
|
12
|
+
class ForkedUrlChecker < AbstractUrlChecker
|
13
|
+
|
14
|
+
attr_reader :delegate
|
15
|
+
attr_reader :parallelism
|
16
|
+
|
17
|
+
# Create a new instance.
|
18
|
+
# @param [AbstractUrlChecker] delegate_checker defaults to {NDD::UrlChecker::BlockingUrlChecker}.
|
19
|
+
# @param [Fixnum] parallelism the number of processes.
|
20
|
+
def initialize(delegate_checker=nil, parallelism=10)
|
21
|
+
@logger = Logging.logger[self]
|
22
|
+
@delegate = delegate_checker || BlockingUrlChecker.new
|
23
|
+
@parallelism = parallelism
|
24
|
+
end
|
25
|
+
|
26
|
+
def check(*urls)
|
27
|
+
return delegate.check(*urls) if urls.size < 2
|
28
|
+
process(urls, :check)
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate(*urls)
|
32
|
+
return delegate.validate(*urls) if urls.size < 2
|
33
|
+
process(urls, :validate)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def process(urls, method)
|
40
|
+
# for receiving results
|
41
|
+
result_pipe = Cod.pipe
|
42
|
+
# partition the URLs, but not too much :)
|
43
|
+
url_slices = partition(urls, [parallelism, urls.size].min)
|
44
|
+
# and distribute them among the workers
|
45
|
+
pids = url_slices.each_with_index.map do |url_slice, index|
|
46
|
+
fork { Worker.new(index, result_pipe, delegate).send(method, url_slice) }
|
47
|
+
end
|
48
|
+
|
49
|
+
# read back the results
|
50
|
+
results = urls.reduce({}) do |hash, _|
|
51
|
+
result = result_pipe.get
|
52
|
+
hash.merge!(result)
|
53
|
+
@logger.debug("Processed URLs #{hash.size}/#{urls.size}")
|
54
|
+
hash
|
55
|
+
end
|
56
|
+
|
57
|
+
# kill all the workers
|
58
|
+
pids.each { |pid| Process.kill(:TERM, pid) }
|
59
|
+
|
60
|
+
results
|
61
|
+
end
|
62
|
+
|
63
|
+
# Evenly distributes data into buckets. For example:
|
64
|
+
# partition([1, 2, 3], 2) => [[1, 3], [2]]
|
65
|
+
# partition([1, 2, 3, 4, 5, 6], 3) => [[1, 4], [2, 5], [3, 6]]
|
66
|
+
def partition(data, buckets)
|
67
|
+
Array.new.tap do |slices|
|
68
|
+
buckets.times.each { |_| slices << Array.new }
|
69
|
+
data.each_with_index { |element, index| slices[index % buckets] << element }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# A simple worker class processing URL one by one.
|
75
|
+
class Worker
|
76
|
+
def initialize(id, result_pipe, url_checker)
|
77
|
+
@logger = Logging.logger[self]
|
78
|
+
@id = id
|
79
|
+
@result_pipe = result_pipe
|
80
|
+
@url_checker = url_checker
|
81
|
+
end
|
82
|
+
|
83
|
+
def check(urls)
|
84
|
+
@logger.debug("[worker #{@id}] Checking #{urls.size} URLs")
|
85
|
+
urls.each do |url|
|
86
|
+
@result_pipe.put({url => @url_checker.check(url)})
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate(urls)
|
91
|
+
@logger.debug("[worker #{@id}] Validating #{urls.size} URLs")
|
92
|
+
urls.each do |url|
|
93
|
+
@result_pipe.put({url => @url_checker.validate(url)})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'ndd/url_checker/abstract_url_checker'
|
3
|
+
require 'ndd/url_checker/forked_url_checker'
|
4
|
+
require 'ndd/url_checker/status'
|
5
|
+
require 'ndd/url_checker/threaded_url_checker'
|
6
|
+
|
7
|
+
module NDD
|
8
|
+
module UrlChecker
|
9
|
+
|
10
|
+
# Wraps an instance of ThreadedUrlChecker or ForkedUrlChecker
|
11
|
+
# depending of the underlying Ruby implementation.
|
12
|
+
# @author David DIDIER
|
13
|
+
class ParallelUrlChecker < AbstractUrlChecker
|
14
|
+
|
15
|
+
attr_reader :delegate
|
16
|
+
|
17
|
+
# Create a new instance.
|
18
|
+
# @param [AbstractUrlChecker] delegate_checker defaults to {NDD::UrlChecker::BlockingUrlChecker}.
|
19
|
+
# @param [Fixnum] parallelism the number of threads or processes.
|
20
|
+
def initialize(delegate_checker=nil, parallelism=10)
|
21
|
+
@logger = Logging.logger[self]
|
22
|
+
|
23
|
+
@logger.debug "Ruby engine is #{RUBY_ENGINE}"
|
24
|
+
if RUBY_ENGINE == 'jruby'
|
25
|
+
@logger.info 'Creating a threaded URL checker'
|
26
|
+
parallel_checker = ThreadedUrlChecker.new(delegate_checker, parallelism)
|
27
|
+
else
|
28
|
+
@logger.info 'Creating a forked URL checker'
|
29
|
+
parallel_checker = ForkedUrlChecker.new(delegate_checker, parallelism)
|
30
|
+
end
|
31
|
+
|
32
|
+
@delegate = parallel_checker
|
33
|
+
end
|
34
|
+
|
35
|
+
def check(*urls)
|
36
|
+
@delegate.check(*urls)
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate(*urls)
|
40
|
+
@delegate.validate(*urls)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module NDD
|
2
|
+
module UrlChecker
|
3
|
+
|
4
|
+
# The result of a URI test.
|
5
|
+
# @author David DIDIER
|
6
|
+
# @attr_reader code [String] the state
|
7
|
+
# @attr_reader error [String|StandardError] the last error if any
|
8
|
+
# @attr_reader uris [Array<String>] the list of requested URI
|
9
|
+
class Status
|
10
|
+
|
11
|
+
attr_reader :code
|
12
|
+
attr_reader :error
|
13
|
+
attr_reader :uris
|
14
|
+
|
15
|
+
# Create a new NDD::UrlChecker::Status instance in the unknown state.
|
16
|
+
# @param uri [String|URI::HTTP] the requested URI.
|
17
|
+
def initialize(uri)
|
18
|
+
@uris = [uri.to_s]
|
19
|
+
@code = :unknown
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the first requested URI.
|
23
|
+
# @return [String] the first requested URI.
|
24
|
+
def uri
|
25
|
+
uris.first
|
26
|
+
end
|
27
|
+
|
28
|
+
# Note that the code :unknown is neither valid nor invalid.
|
29
|
+
# @return [Boolean] true if valid, false otherwise.
|
30
|
+
def valid?
|
31
|
+
VALID_CODES.include? @code
|
32
|
+
end
|
33
|
+
|
34
|
+
# Note that the code :unknown is neither valid nor invalid.
|
35
|
+
# @return [Boolean] true if invalid, false otherwise.
|
36
|
+
def invalid?
|
37
|
+
INVALID_CODES.include? @code
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Boolean] true if redirected, false otherwise.
|
41
|
+
def redirected?
|
42
|
+
@code == :redirected
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# When the URI is valid without any redirect.
|
47
|
+
# @return [NDD::UrlChecker::Status] self.
|
48
|
+
def direct
|
49
|
+
update_code(:direct, %i(unknown))
|
50
|
+
end
|
51
|
+
|
52
|
+
# When a generic error is raised.
|
53
|
+
# @param error [StandardError|String] the generic error.
|
54
|
+
# @return [NDD::UrlChecker::Status] self.
|
55
|
+
def failed(error)
|
56
|
+
@error = error
|
57
|
+
update_code(:failed, %i(unknown redirected))
|
58
|
+
end
|
59
|
+
|
60
|
+
# Adds a new URI to the redirected URI list.
|
61
|
+
# @param uri [String|URI::HTTP] the redirection URI.
|
62
|
+
# @return [NDD::UrlChecker::Status] self.
|
63
|
+
def redirected(uri)
|
64
|
+
@uris << uri.to_s
|
65
|
+
update_code(:redirected, %i(unknown redirected))
|
66
|
+
end
|
67
|
+
|
68
|
+
# When there are too many redirects.
|
69
|
+
# @return [NDD::UrlChecker::Status] self.
|
70
|
+
def too_many_redirects
|
71
|
+
update_code(:too_many_redirects, %i(unknown redirected))
|
72
|
+
end
|
73
|
+
|
74
|
+
# When the host cannot be resolved.
|
75
|
+
# @return [NDD::UrlChecker::Status] self.
|
76
|
+
def unknown_host
|
77
|
+
update_code(:unknown_host, %i(unknown redirected))
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
self.inspect
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
VALID_CODES = %i(direct redirected).freeze
|
88
|
+
INVALID_CODES = %i(failed too_many_redirects unknown_host).freeze
|
89
|
+
|
90
|
+
# Updates the code if the transition is valid.
|
91
|
+
# @param code [Symbol] the new code.
|
92
|
+
# @param valid_source_codes [Array<Symbol>] the codes from which the transition can happen.
|
93
|
+
# @return [NDD::UrlChecker::Status] self.
|
94
|
+
def update_code(code, valid_source_codes)
|
95
|
+
unless valid_source_codes.include?(@code)
|
96
|
+
raise "Changing the status code from :#{@code} to :#{code} is forbidden"
|
97
|
+
end
|
98
|
+
@code = code
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'logging'
|
2
|
+
require 'ndd/url_checker/abstract_url_checker'
|
3
|
+
require 'ndd/url_checker/blocking_url_checker'
|
4
|
+
require 'ndd/url_checker/status'
|
5
|
+
|
6
|
+
module NDD
|
7
|
+
module UrlChecker
|
8
|
+
|
9
|
+
# An URL checker using threads to parallelize processing. Does not work on MRI.
|
10
|
+
# @author David DIDIER
|
11
|
+
class ThreadedUrlChecker < AbstractUrlChecker
|
12
|
+
|
13
|
+
attr_reader :delegate
|
14
|
+
|
15
|
+
# Create a new instance.
|
16
|
+
# @param [AbstractUrlChecker] delegate_checker defaults to {NDD::UrlChecker::BlockingUrlChecker}.
|
17
|
+
# @param [Fixnum] parallelism the number of threads.
|
18
|
+
def initialize(delegate_checker=nil, parallelism=10)
|
19
|
+
@logger = Logging.logger[self]
|
20
|
+
@delegate = delegate_checker || BlockingUrlChecker.new
|
21
|
+
@parallelism = parallelism
|
22
|
+
end
|
23
|
+
|
24
|
+
def check(*urls)
|
25
|
+
# delegate.check(*urls)
|
26
|
+
raise 'TODO'
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate(*urls)
|
30
|
+
# delegate.validate(*urls)
|
31
|
+
raise 'TODO'
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NDD::UrlChecker::AbstractUrlChecker do
|
4
|
+
|
5
|
+
context '#validate' do
|
6
|
+
it 'is not implemented' do
|
7
|
+
expect { subject.validate('http://www.valid.mock/') }.to raise_error /must be implemented/
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context '#check' do
|
12
|
+
it 'is not implemented' do
|
13
|
+
expect { subject.check('http://www.valid.mock/') }.to raise_error /must be implemented/
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NDD::UrlChecker::ParallelUrlChecker do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
# Logging.logger.root.level = :debug
|
7
|
+
end
|
8
|
+
|
9
|
+
# ---------------------------------------------------------------------------------------------------------- MRI -----
|
10
|
+
context 'when running MRI' do
|
11
|
+
|
12
|
+
before(:all) do
|
13
|
+
@old_engine = RUBY_ENGINE
|
14
|
+
silence_warnings { RUBY_ENGINE = 'ruby' }
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#initialize' do
|
18
|
+
it 'delegates to a ForkedUrlChecker instance' do
|
19
|
+
expect(NDD::UrlChecker::ParallelUrlChecker.new.delegate).to be_a NDD::UrlChecker::ForkedUrlChecker
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it_behaves_like 'a single URL checker'
|
24
|
+
|
25
|
+
after(:all) do
|
26
|
+
silence_warnings { RUBY_ENGINE = @old_engine }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# -------------------------------------------------------------------------------------------------------- JRuby -----
|
31
|
+
context 'when running JRuby' do
|
32
|
+
before(:all) do
|
33
|
+
@old_engine = RUBY_ENGINE
|
34
|
+
silence_warnings { RUBY_ENGINE = 'jruby' }
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#initialize' do
|
38
|
+
it 'delegates to a ThreadedUrlChecker instance' do
|
39
|
+
expect(NDD::UrlChecker::ParallelUrlChecker.new.delegate).to be_a NDD::UrlChecker::ThreadedUrlChecker
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO
|
44
|
+
# it_behaves_like 'a single URL checker'
|
45
|
+
|
46
|
+
after(:all) do
|
47
|
+
silence_warnings { RUBY_ENGINE = @old_engine }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# ------------------------------------------------------------------------------------------------------ private -----
|
53
|
+
private
|
54
|
+
|
55
|
+
def silence_warnings(&block)
|
56
|
+
warn_level = $VERBOSE
|
57
|
+
$VERBOSE = nil
|
58
|
+
result = block.call
|
59
|
+
$VERBOSE = warn_level
|
60
|
+
result
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,510 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe NDD::UrlChecker::Status do
|
4
|
+
|
5
|
+
# ------------------------------------------------------------------------------------------------------ unknown -----
|
6
|
+
context 'when initialized' do
|
7
|
+
let(:uri) { 'http://www.example.com' }
|
8
|
+
let(:status) { NDD::UrlChecker::Status.new(uri) }
|
9
|
+
|
10
|
+
context '#uri' do
|
11
|
+
it 'returns the original URI' do
|
12
|
+
expect(status.uri).to eq uri
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context '#uris' do
|
17
|
+
it 'returns the original URI' do
|
18
|
+
expect(status.uris).to eq [uri]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context '#code' do
|
23
|
+
it 'returns :unknown' do
|
24
|
+
expect(status.code).to eq :unknown
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context '#valid?' do
|
29
|
+
it 'returns false' do
|
30
|
+
expect(status.valid?).to be_falsey
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context '#invalid?' do
|
35
|
+
it 'returns false' do
|
36
|
+
expect(status.invalid?).to be_falsey
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context '#error' do
|
41
|
+
it 'returns nil' do
|
42
|
+
expect(status.error).to be_nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context '#direct' do
|
47
|
+
let!(:new_status) { status.direct }
|
48
|
+
it 'changes the code to :direct' do
|
49
|
+
expect(status.code).to eq :direct
|
50
|
+
end
|
51
|
+
it 'returns the status' do
|
52
|
+
expect(new_status).to eq status
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context '#failed' do
|
57
|
+
let!(:new_status) { status.failed('some error') }
|
58
|
+
it 'changes the code to :failed' do
|
59
|
+
expect(status.code).to eq :failed
|
60
|
+
end
|
61
|
+
it 'returns the status' do
|
62
|
+
expect(new_status).to eq status
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context '#redirected' do
|
67
|
+
let!(:new_status) { status.redirected('http://www.redirected.com') }
|
68
|
+
it 'changes the code to :redirected' do
|
69
|
+
expect(status.code).to eq :redirected
|
70
|
+
end
|
71
|
+
it 'returns the status' do
|
72
|
+
expect(new_status).to eq status
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context '#too_many_redirects' do
|
77
|
+
let!(:new_status) { status.too_many_redirects }
|
78
|
+
it 'changes the code to :too_many_redirects' do
|
79
|
+
expect(status.code).to eq :too_many_redirects
|
80
|
+
end
|
81
|
+
it 'returns the status' do
|
82
|
+
expect(new_status).to eq status
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context '#unknown_host' do
|
87
|
+
it 'changes the code to :unknown_host' do
|
88
|
+
status.unknown_host
|
89
|
+
expect(status.code).to eq :unknown_host
|
90
|
+
end
|
91
|
+
it 'returns the status' do
|
92
|
+
expect(status.unknown_host).to eq status
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context '#to_s' do
|
97
|
+
it 'returns the status representation' do
|
98
|
+
expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:unknown>$}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# ------------------------------------------------------------------------------------------------------- direct -----
|
104
|
+
context 'when code is :direct' do
|
105
|
+
let(:uri) { 'http://www.example.com' }
|
106
|
+
let(:status) { NDD::UrlChecker::Status.new(uri).direct }
|
107
|
+
|
108
|
+
context '#uri' do
|
109
|
+
it 'returns the original URI' do
|
110
|
+
expect(status.uri).to eq uri
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context '#uris' do
|
115
|
+
it 'returns the original URI' do
|
116
|
+
expect(status.uris).to eq [uri]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context '#code' do
|
121
|
+
it 'returns :direct' do
|
122
|
+
expect(status.code).to eq :direct
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context '#valid?' do
|
127
|
+
it 'returns true' do
|
128
|
+
expect(status.valid?).to be_truthy
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context '#invalid?' do
|
133
|
+
it 'returns false' do
|
134
|
+
expect(status.invalid?).to be_falsey
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context '#error' do
|
139
|
+
it 'returns nil' do
|
140
|
+
expect(status.error).to be_nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context '#direct' do
|
145
|
+
it 'raises an error' do
|
146
|
+
expect { status.direct }.to raise_error(/from :direct to :direct is forbidden/)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context '#failed' do
|
151
|
+
it 'raises an error' do
|
152
|
+
expect { status.failed('some error') }.to raise_error(/from :direct to :failed is forbidden/)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context '#redirected' do
|
157
|
+
it 'raises an error' do
|
158
|
+
expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :direct to :redirected is forbidden/)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context '#too_many_redirects' do
|
163
|
+
it 'raises an error' do
|
164
|
+
expect { status.too_many_redirects }.to raise_error(/from :direct to :too_many_redirects is forbidden/)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context '#unknown_host' do
|
169
|
+
it 'raises an error' do
|
170
|
+
expect { status.unknown_host }.to raise_error(/from :direct to :unknown_host is forbidden/)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context '#to_s' do
|
175
|
+
it 'returns the status representation' do
|
176
|
+
expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:direct>$}
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# ------------------------------------------------------------------------------------------------------- failed -----
|
182
|
+
context 'when code is :failed' do
|
183
|
+
let(:uri) { 'http://www.example.com' }
|
184
|
+
let(:status) { NDD::UrlChecker::Status.new(uri).failed('some error') }
|
185
|
+
|
186
|
+
context '#uri' do
|
187
|
+
it 'returns the original URI' do
|
188
|
+
expect(status.uri).to eq uri
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context '#uris' do
|
193
|
+
it 'returns the original URI' do
|
194
|
+
expect(status.uris).to eq [uri]
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context '#code' do
|
199
|
+
it 'returns :failed' do
|
200
|
+
expect(status.code).to eq :failed
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context '#valid?' do
|
205
|
+
it 'returns false' do
|
206
|
+
expect(status.valid?).to be_falsey
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context '#invalid?' do
|
211
|
+
it 'returns true' do
|
212
|
+
expect(status.invalid?).to be_truthy
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context '#error' do
|
217
|
+
it 'returns the error' do
|
218
|
+
expect(status.error).to eq 'some error'
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context '#direct' do
|
223
|
+
it 'raises an error' do
|
224
|
+
expect { status.direct }.to raise_error(/from :failed to :direct is forbidden/)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context '#failed' do
|
229
|
+
it 'raises an error' do
|
230
|
+
expect { status.failed('some error') }.to raise_error(/from :failed to :failed is forbidden/)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
context '#redirected' do
|
235
|
+
it 'raises an error' do
|
236
|
+
expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :failed to :redirected is forbidden/)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context '#too_many_redirects' do
|
241
|
+
it 'raises an error' do
|
242
|
+
expect { status.too_many_redirects }.to raise_error(/from :failed to :too_many_redirects is forbidden/)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context '#unknown_host' do
|
247
|
+
it 'raises an error' do
|
248
|
+
expect { status.unknown_host }.to raise_error(/from :failed to :unknown_host is forbidden/)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
context '#to_s' do
|
253
|
+
it 'returns the status representation' do
|
254
|
+
expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:failed, @error="some error">$}
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
# --------------------------------------------------------------------------------------------------- redirected -----
|
260
|
+
context 'when code is :redirected' do
|
261
|
+
let(:uri) { 'http://www.example.com' }
|
262
|
+
let(:redirect_uri) { 'http://www.redirected.com' }
|
263
|
+
let(:status) { NDD::UrlChecker::Status.new(uri).redirected(redirect_uri) }
|
264
|
+
|
265
|
+
context '#uri' do
|
266
|
+
it 'returns the original URI' do
|
267
|
+
expect(status.uri).to eq uri
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context '#uris' do
|
272
|
+
it 'returns all the URI' do
|
273
|
+
expect(status.uris).to eq [uri, redirect_uri]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context '#code' do
|
278
|
+
it 'returns :redirected' do
|
279
|
+
expect(status.code).to eq :redirected
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context '#valid?' do
|
284
|
+
it 'returns true' do
|
285
|
+
expect(status.valid?).to be_truthy
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context '#invalid?' do
|
290
|
+
it 'returns false' do
|
291
|
+
expect(status.invalid?).to be_falsey
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
context '#error' do
|
296
|
+
it 'returns nil' do
|
297
|
+
expect(status.error).to be_nil
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
context '#direct' do
|
302
|
+
it 'raises an error' do
|
303
|
+
expect { status.direct }.to raise_error(/from :redirected to :direct is forbidden/)
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context '#failed' do
|
308
|
+
let!(:new_status) { status.failed('some error') }
|
309
|
+
it 'changes the code to :failed' do
|
310
|
+
expect(status.code).to eq :failed
|
311
|
+
end
|
312
|
+
it 'returns the status' do
|
313
|
+
expect(new_status).to eq status
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context '#redirected' do
|
318
|
+
let!(:new_status) { status.redirected('http://www.redirected.com') }
|
319
|
+
it 'changes the code to :redirected' do
|
320
|
+
expect(status.code).to eq :redirected
|
321
|
+
end
|
322
|
+
it 'returns the status' do
|
323
|
+
expect(new_status).to eq status
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
context '#too_many_redirects' do
|
328
|
+
let!(:new_status) { status.too_many_redirects }
|
329
|
+
it 'changes the code to :too_many_redirects' do
|
330
|
+
expect(status.code).to eq :too_many_redirects
|
331
|
+
end
|
332
|
+
it 'returns the status' do
|
333
|
+
expect(new_status).to eq status
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context '#unknown_host' do
|
338
|
+
it 'changes the code to :unknown_host' do
|
339
|
+
status.unknown_host
|
340
|
+
expect(status.code).to eq :unknown_host
|
341
|
+
end
|
342
|
+
it 'returns the status' do
|
343
|
+
expect(status.unknown_host).to eq status
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context '#to_s' do
|
348
|
+
it 'returns the status representation' do
|
349
|
+
expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com", "http://www.redirected.com"\], @code=:redirected>$}
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
# ------------------------------------------------------------------------------------------- too_many_redirects -----
|
355
|
+
context 'when code is :too_many_redirects' do
|
356
|
+
let(:uri) { 'http://www.example.com' }
|
357
|
+
let(:status) { NDD::UrlChecker::Status.new(uri).too_many_redirects }
|
358
|
+
|
359
|
+
context '#uri' do
|
360
|
+
it 'returns the original URI' do
|
361
|
+
expect(status.uri).to eq uri
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
context '#uris' do
|
366
|
+
it 'returns the original URI' do
|
367
|
+
expect(status.uris).to eq [uri]
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
context '#code' do
|
372
|
+
it 'returns :too_many_redirects' do
|
373
|
+
expect(status.code).to eq :too_many_redirects
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
context '#valid?' do
|
378
|
+
it 'returns false' do
|
379
|
+
expect(status.valid?).to be_falsey
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context '#invalid?' do
|
384
|
+
it 'returns true' do
|
385
|
+
expect(status.invalid?).to be_truthy
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
context '#error' do
|
390
|
+
it 'returns nil' do
|
391
|
+
expect(status.error).to be_nil
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
context '#direct' do
|
396
|
+
it 'raises an error' do
|
397
|
+
expect { status.direct }.to raise_error(/from :too_many_redirects to :direct is forbidden/)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
context '#failed' do
|
402
|
+
it 'raises an error' do
|
403
|
+
expect { status.failed('some error') }.to raise_error(/from :too_many_redirects to :failed is forbidden/)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
context '#redirected' do
|
408
|
+
it 'raises an error' do
|
409
|
+
expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :too_many_redirects to :redirected is forbidden/)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
context '#too_many_redirects' do
|
414
|
+
it 'raises an error' do
|
415
|
+
expect { status.too_many_redirects }.to raise_error(/from :too_many_redirects to :too_many_redirects is forbidden/)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
context '#unknown_host' do
|
420
|
+
it 'raises an error' do
|
421
|
+
expect { status.unknown_host }.to raise_error(/from :too_many_redirects to :unknown_host is forbidden/)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
context '#to_s' do
|
426
|
+
it 'returns the status representation' do
|
427
|
+
expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:too_many_redirects>$}
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# ------------------------------------------------------------------------------------------------- unknown_host -----
|
433
|
+
context 'when code is :unknown_host' do
|
434
|
+
let(:uri) { 'http://www.example.com' }
|
435
|
+
let(:status) { NDD::UrlChecker::Status.new(uri).unknown_host }
|
436
|
+
|
437
|
+
context '#uri' do
|
438
|
+
it 'returns the original URI' do
|
439
|
+
expect(status.uri).to eq uri
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
context '#uris' do
|
444
|
+
it 'returns the original URI' do
|
445
|
+
expect(status.uris).to eq [uri]
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
context '#code' do
|
450
|
+
it 'returns :unknown_host' do
|
451
|
+
expect(status.code).to eq :unknown_host
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
context '#valid?' do
|
456
|
+
it 'returns false' do
|
457
|
+
expect(status.valid?).to be_falsey
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
context '#invalid?' do
|
462
|
+
it 'returns true' do
|
463
|
+
expect(status.invalid?).to be_truthy
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
context '#error' do
|
468
|
+
it 'returns nil' do
|
469
|
+
expect(status.error).to be_nil
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
context '#direct' do
|
474
|
+
it 'raises an error' do
|
475
|
+
expect { status.direct }.to raise_error(/from :unknown_host to :direct is forbidden/)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
context '#failed' do
|
480
|
+
it 'raises an error' do
|
481
|
+
expect { status.failed('some error') }.to raise_error(/from :unknown_host to :failed is forbidden/)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
context '#redirected' do
|
486
|
+
it 'raises an error' do
|
487
|
+
expect { status.redirected('http://www.redirected.com') }.to raise_error(/from :unknown_host to :redirected is forbidden/)
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
context '#too_many_redirects' do
|
492
|
+
it 'raises an error' do
|
493
|
+
expect { status.too_many_redirects }.to raise_error(/from :unknown_host to :too_many_redirects is forbidden/)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context '#unknown_host' do
|
498
|
+
it 'raises an error' do
|
499
|
+
expect { status.unknown_host }.to raise_error(/from :unknown_host to :unknown_host is forbidden/)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
context '#to_s' do
|
504
|
+
it 'returns the status representation' do
|
505
|
+
expect(status.to_s).to match %r{^#<NDD::UrlChecker::Status:0[xX][0-9a-fA-F]+ @uris=\["http://www.example.com"\], @code=:unknown_host>$}
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
end
|