cms_scanner 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +14 -0
- data/Gemfile +6 -0
- data/README.md +20 -0
- data/Rakefile +9 -0
- data/app/app.rb +4 -0
- data/app/controllers.rb +2 -0
- data/app/controllers/core.rb +46 -0
- data/app/controllers/core/cli_options.rb +68 -0
- data/app/controllers/interesting_files.rb +12 -0
- data/app/finders.rb +1 -0
- data/app/finders/interesting_files.rb +21 -0
- data/app/finders/interesting_files/fantastico_fileslist.rb +23 -0
- data/app/finders/interesting_files/headers.rb +15 -0
- data/app/finders/interesting_files/robots_txt.rb +22 -0
- data/app/finders/interesting_files/search_replace_db_2.rb +28 -0
- data/app/finders/interesting_files/xml_rpc.rb +62 -0
- data/app/formatters.rb +3 -0
- data/app/formatters/cli.rb +18 -0
- data/app/formatters/cli_no_colour.rb +15 -0
- data/app/formatters/json.rb +12 -0
- data/app/models.rb +5 -0
- data/app/models/fantastico_fileslist.rb +20 -0
- data/app/models/headers.rb +37 -0
- data/app/models/interesting_file.rb +30 -0
- data/app/models/robots_txt.rb +20 -0
- data/app/models/xml_rpc.rb +35 -0
- data/app/views/cli/core/finished.erb +4 -0
- data/app/views/cli/core/started.erb +3 -0
- data/app/views/cli/interesting_files/findings.erb +19 -0
- data/app/views/cli/scan_aborted.erb +4 -0
- data/app/views/json/core/finished.erb +3 -0
- data/app/views/json/core/started.erb +3 -0
- data/app/views/json/interesting_files/findings.erb +1 -0
- data/app/views/json/scan_aborted.erb +4 -0
- data/cms_scanner.gemspec +37 -0
- data/examples/views/cli/wp_custom/test.erb +1 -0
- data/examples/views/json/wp_custom/test.erb +1 -0
- data/examples/wpscan.rb +29 -0
- data/lib/cms_scanner.rb +71 -0
- data/lib/cms_scanner/browser.rb +68 -0
- data/lib/cms_scanner/browser/actions.rb +48 -0
- data/lib/cms_scanner/browser/options.rb +53 -0
- data/lib/cms_scanner/cache/file_store.rb +75 -0
- data/lib/cms_scanner/cache/typhoeus.rb +21 -0
- data/lib/cms_scanner/controller.rb +90 -0
- data/lib/cms_scanner/controllers.rb +34 -0
- data/lib/cms_scanner/errors/auth_errors.rb +15 -0
- data/lib/cms_scanner/finders.rb +5 -0
- data/lib/cms_scanner/finders/finder.rb +27 -0
- data/lib/cms_scanner/finders/finding.rb +32 -0
- data/lib/cms_scanner/finders/findings.rb +25 -0
- data/lib/cms_scanner/finders/independent_finder.rb +30 -0
- data/lib/cms_scanner/finders/independent_finders.rb +41 -0
- data/lib/cms_scanner/formatter.rb +118 -0
- data/lib/cms_scanner/formatter/buffer.rb +15 -0
- data/lib/cms_scanner/target.rb +33 -0
- data/lib/cms_scanner/target/platform.rb +2 -0
- data/lib/cms_scanner/target/platform/php.rb +39 -0
- data/lib/cms_scanner/target/platform/wordpress.rb +35 -0
- data/lib/cms_scanner/target/platform/wordpress/custom_directories.rb +62 -0
- data/lib/cms_scanner/target/server.rb +3 -0
- data/lib/cms_scanner/target/server/apache.rb +43 -0
- data/lib/cms_scanner/target/server/generic.rb +34 -0
- data/lib/cms_scanner/target/server/iis.rb +48 -0
- data/lib/cms_scanner/version.rb +4 -0
- data/lib/cms_scanner/web_site.rb +68 -0
- data/lib/helper.rb +24 -0
- data/spec/app/controllers/core_spec.rb +152 -0
- data/spec/app/controllers/interesting_files_spec.rb +50 -0
- data/spec/app/finders/interesting_files/fantastico_fileslist_spec.rb +68 -0
- data/spec/app/finders/interesting_files/headers_spec.rb +38 -0
- data/spec/app/finders/interesting_files/robots_txt_spec.rb +56 -0
- data/spec/app/finders/interesting_files/search_replace_db_2_spec.rb +55 -0
- data/spec/app/finders/interesting_files/xml_rpc_spec.rb +138 -0
- data/spec/app/finders/interesting_files_spec.rb +13 -0
- data/spec/app/formatters/cli_no_colour_spec.rb +17 -0
- data/spec/app/formatters/cli_spec.rb +21 -0
- data/spec/app/formatters/json_spec.rb +33 -0
- data/spec/app/models/fantastico_fileslist_spec.rb +32 -0
- data/spec/app/models/headers_spec.rb +52 -0
- data/spec/app/models/interesting_file_spec.rb +51 -0
- data/spec/app/models/robots_txt_spec.rb +28 -0
- data/spec/app/models/xml_rpc_spec.rb +47 -0
- data/spec/cache/.gitignore +4 -0
- data/spec/dummy_finders.rb +41 -0
- data/spec/fixtures/interesting_files/fantastico_fileslist/fantastico_fileslist.txt +12 -0
- data/spec/fixtures/interesting_files/file.txt +4 -0
- data/spec/fixtures/interesting_files/headers/interesting.txt +14 -0
- data/spec/fixtures/interesting_files/headers/no_interesting.txt +12 -0
- data/spec/fixtures/interesting_files/robots_txt/robots.txt +10 -0
- data/spec/fixtures/interesting_files/search_replace_db_2/searchreplacedb2.php +188 -0
- data/spec/fixtures/interesting_files/xml_rpc/homepage_in_scope_pingback.html +7 -0
- data/spec/fixtures/interesting_files/xml_rpc/homepage_out_of_scope_pingback.html +7 -0
- data/spec/fixtures/interesting_files/xml_rpc/xmlrpc.php +1 -0
- data/spec/fixtures/output.txt +0 -0
- data/spec/fixtures/target/platform/php/debug_log/debug.log +2 -0
- data/spec/fixtures/target/platform/php/fpd/wp_rss_functions.php +2 -0
- data/spec/fixtures/target/platform/wordpress/custom_directories/custom_w_spaces.html +10 -0
- data/spec/fixtures/target/platform/wordpress/custom_directories/default.html +14 -0
- data/spec/fixtures/target/platform/wordpress/custom_directories/https.html +12 -0
- data/spec/fixtures/target/platform/wordpress/detection/default.html +4 -0
- data/spec/fixtures/target/platform/wordpress/detection/not_wp.html +8 -0
- data/spec/fixtures/target/platform/wordpress/detection/wp_includes.html +3 -0
- data/spec/fixtures/target/server/apache/directory_listing/2.2.16.html +15 -0
- data/spec/fixtures/target/server/generic/server/apache/basic.txt +5 -0
- data/spec/fixtures/target/server/generic/server/iis/basic.txt +6 -0
- data/spec/fixtures/target/server/generic/server/not_detected.txt +3 -0
- data/spec/fixtures/target/server/iis/directory_listing/no_parent.html +3 -0
- data/spec/fixtures/target/server/iis/directory_listing/with_parent.html +3 -0
- data/spec/fixtures/views/base/ctrl/local.erb +1 -0
- data/spec/fixtures/views/base/ctrl/test.erb +3 -0
- data/spec/fixtures/views/base/global.erb +1 -0
- data/spec/fixtures/views/base/test.erb +2 -0
- data/spec/fixtures/views/based_format/test.erb +1 -0
- data/spec/fixtures/views/json/render_me.erb +4 -0
- data/spec/lib/browser_spec.rb +141 -0
- data/spec/lib/cache/file_store_spec.rb +101 -0
- data/spec/lib/cache/typhoeus_spec.rb +30 -0
- data/spec/lib/cms_scanner_spec.rb +45 -0
- data/spec/lib/controller_spec.rb +23 -0
- data/spec/lib/controllers_spec.rb +52 -0
- data/spec/lib/finders/findings_spec.rb +49 -0
- data/spec/lib/finders/independent_finders_spec.rb +98 -0
- data/spec/lib/formatter_spec.rb +136 -0
- data/spec/lib/sub_scanner_spec.rb +27 -0
- data/spec/lib/target/platforms_spec.rb +13 -0
- data/spec/lib/target/servers_spec.rb +13 -0
- data/spec/lib/target_spec.rb +50 -0
- data/spec/lib/web_site_spec.rb +124 -0
- data/spec/shared_examples.rb +11 -0
- data/spec/shared_examples/browser_actions.rb +32 -0
- data/spec/shared_examples/finding.rb +20 -0
- data/spec/shared_examples/formatter_buffer.rb +8 -0
- data/spec/shared_examples/formatter_class_methods.rb +26 -0
- data/spec/shared_examples/independent_finder.rb +33 -0
- data/spec/shared_examples/target/platform/php.rb +58 -0
- data/spec/shared_examples/target/platform/wordpress.rb +41 -0
- data/spec/shared_examples/target/platform/wordpress/custom_directories.rb +50 -0
- data/spec/shared_examples/target/server/apache.rb +33 -0
- data/spec/shared_examples/target/server/generic.rb +34 -0
- data/spec/shared_examples/target/server/iis.rb +38 -0
- data/spec/spec_helper.rb +41 -0
- metadata +432 -0
@@ -0,0 +1,3 @@
|
|
1
|
+
<html><head><title>ex.lo - /dir/</title></head><body><H1>ex.lo - /dir/</H1><hr>
|
2
|
+
|
3
|
+
<pre><A HREF="/">[To Parent Directory]</A><br><br> 10/8/2014 11:00 PM <dir> <A HREF="/sub-dir/">sub-dir</A>10/10/2014 10:00 PM 168 <A HREF="/web.config">web.config</A><br></pre><hr></body></html>
|
@@ -0,0 +1 @@
|
|
1
|
+
Local View
|
@@ -0,0 +1 @@
|
|
1
|
+
Global View
|
@@ -0,0 +1 @@
|
|
1
|
+
Override the base/test.erb
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Browser do
|
4
|
+
|
5
|
+
it_behaves_like described_class::Actions
|
6
|
+
|
7
|
+
subject(:browser) { described_class.instance(options) }
|
8
|
+
before { described_class.reset }
|
9
|
+
let(:options) { {} }
|
10
|
+
let(:default) do
|
11
|
+
{
|
12
|
+
ssl_verifypeer: false, ssl_verifyhost: 2,
|
13
|
+
headers: { 'User-Agent' => "CMSScanner v#{CMSScanner::VERSION}" }
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#forge_request' do
|
18
|
+
it 'returns a Typhoeus::Request' do
|
19
|
+
expect(browser.forge_request('http://example.com')).to be_a Typhoeus::Request
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#default_request_params' do
|
24
|
+
its(:default_request_params) { should eq default }
|
25
|
+
|
26
|
+
context 'when some attributes are set' do
|
27
|
+
let(:options) do
|
28
|
+
{
|
29
|
+
cache_ttl: 200, connect_timeout: 10,
|
30
|
+
http_auth: { username: 'log', password: 'pwd' },
|
31
|
+
cookie_jar: '/tmp/cookie_jar.txt'
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:expected) do
|
36
|
+
default.merge(
|
37
|
+
cache_ttl: 200, connecttimeout: 10, userpwd: 'log:pwd',
|
38
|
+
cookiejar: options[:cookie_jar], cookiefile: options[:cookie_jar]
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
its(:default_request_params) { should eq expected }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#request_params' do
|
47
|
+
context 'when no param is given' do
|
48
|
+
its(:request_params) { should eq default }
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when params are supplied' do
|
52
|
+
let(:params) { { another_param: true, headers: { 'Accept' => 'None' } } }
|
53
|
+
|
54
|
+
it 'merges them (headers should be correctly merged)' do
|
55
|
+
expect(browser.request_params(params)).to eq default
|
56
|
+
.merge(params) { |key, oldval, newval| key == :headers ? oldval.merge(newval) : newval }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when browser options' do
|
60
|
+
let(:options) { { proxy: 'http://127.0.0.1:8080' } }
|
61
|
+
|
62
|
+
it 'returns the correct hash' do
|
63
|
+
expect(browser.request_params(params)).to eq default
|
64
|
+
.merge(options)
|
65
|
+
.merge(params) { |key, oldval, newval| key == :headers ? oldval.merge(newval) : newval }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#load_options' do
|
72
|
+
context 'when no options' do
|
73
|
+
it 'does not load anything' do
|
74
|
+
described_class::OPTIONS.each do |sym|
|
75
|
+
expected = sym == :user_agent ? "CMSScanner v#{CMSScanner::VERSION}" : nil
|
76
|
+
|
77
|
+
expect(browser.send(sym)).to eq expected
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'when options are supplied' do
|
83
|
+
module CMSScanner
|
84
|
+
# Test accessor
|
85
|
+
class Browser
|
86
|
+
attr_accessor :test
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
let(:options) do
|
91
|
+
{ cache_ttl: 200, max_threads: 10, test: 'should not be set',
|
92
|
+
user_agent: 'UA', proxy: false }
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'merges the browser options only' do
|
96
|
+
described_class::OPTIONS.each do |sym|
|
97
|
+
expected = options.key?(sym) ? options[sym] : nil
|
98
|
+
|
99
|
+
expect(browser.send(sym)).to eq expected
|
100
|
+
end
|
101
|
+
|
102
|
+
expect(browser.test).to be nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#hydra' do
|
108
|
+
context 'when #max_threads is nil' do
|
109
|
+
its('hydra.max_concurrency') { should eq 1 }
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when #max_threads' do
|
113
|
+
let(:options) { { max_threads: 20 } }
|
114
|
+
|
115
|
+
its('hydra.max_concurrency') { should eq options[:max_threads] }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
describe '#max_threads=' do
|
120
|
+
after do
|
121
|
+
browser.max_threads = @threads
|
122
|
+
|
123
|
+
expect(browser.max_threads).to eq @expected
|
124
|
+
expect(browser.hydra.max_concurrency).to eq @expected
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'when <= 0' do
|
128
|
+
it 'sets the @threads to 1' do
|
129
|
+
@threads = -2
|
130
|
+
@expected = 1
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when > 0' do
|
135
|
+
it 'sets the @threads' do
|
136
|
+
@threads = 20
|
137
|
+
@expected = @threads
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Cache::FileStore do
|
4
|
+
|
5
|
+
let(:cache_dir) { File.join(CACHE, 'cache_file_store') }
|
6
|
+
subject(:cache) { described_class.new(cache_dir) }
|
7
|
+
|
8
|
+
before { FileUtils.rm_r(cache_dir, secure: true) if Dir.exist?(cache_dir) }
|
9
|
+
after { cache.clean }
|
10
|
+
|
11
|
+
describe '#new, #storage_path, #serializer' do
|
12
|
+
its(:serializer) { should be Marshal }
|
13
|
+
its(:storage_path) { should eq cache_dir }
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#clean' do
|
17
|
+
it 'removes all files from the cache dir' do
|
18
|
+
# let's create some files into the directory first
|
19
|
+
(0..5).each do |i|
|
20
|
+
File.new(File.join(cache.storage_path, "file_#{i}.txt"), File::CREAT)
|
21
|
+
end
|
22
|
+
|
23
|
+
expect(count_files_in_dir(cache.storage_path, 'file_*.txt')).to eq 6
|
24
|
+
cache.clean
|
25
|
+
expect(count_files_in_dir(cache.storage_path)).to eq 0
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#read_entry?' do
|
30
|
+
let(:key) { 'key1' }
|
31
|
+
|
32
|
+
after do
|
33
|
+
File.write(cache.entry_expiration_path(key), @expiration) if @expiration
|
34
|
+
|
35
|
+
expect(cache.read_entry(key)).to eq @expected
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when the entry does not exists' do
|
39
|
+
it 'returns nil' do
|
40
|
+
@expected = nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when the file is empty (marshal data too short error)' do
|
45
|
+
it 'returns nil' do
|
46
|
+
File.new(cache.entry_path(key), File::CREAT)
|
47
|
+
|
48
|
+
@expiration = Time.now.to_i + 200
|
49
|
+
@expected = nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when the entry has expired' do
|
54
|
+
it 'returns nil' do
|
55
|
+
@expiration = Time.now.to_i - 200
|
56
|
+
@expected = nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when the entry has not expired' do
|
61
|
+
it 'returns the entry' do
|
62
|
+
File.write(cache.entry_path(key), cache.serializer.dump('testing data'))
|
63
|
+
|
64
|
+
@expiration = Time.now.to_i + 600
|
65
|
+
@expected = 'testing data'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#write_entry' do
|
71
|
+
after do
|
72
|
+
cache.write_entry(@key, @data, @ttl)
|
73
|
+
expect(cache.read_entry(@key)).to eq @expected
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should get the correct entry (string)' do
|
77
|
+
@ttl = 10
|
78
|
+
@key = 'some_key'
|
79
|
+
@data = 'Hello World !'
|
80
|
+
@expected = @data
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when cache_ttl <= 0' do
|
84
|
+
it 'does not write the entry' do
|
85
|
+
@ttl = 0
|
86
|
+
@key = 'another_key'
|
87
|
+
@data = 'Another Hello World !'
|
88
|
+
@expected = nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when cache_ttl is nil' do
|
93
|
+
it 'does not write the entry' do
|
94
|
+
@ttl = nil
|
95
|
+
@key = 'test'
|
96
|
+
@data = 'test'
|
97
|
+
@expected = nil
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Cache::Typhoeus do
|
4
|
+
|
5
|
+
subject(:cache) { described_class.new(cache_dir) }
|
6
|
+
|
7
|
+
let(:cache_dir) { File.join(CACHE, 'typhoeus_cache') }
|
8
|
+
let(:url) { 'http://example.com' }
|
9
|
+
let(:request) { Typhoeus::Request.new(url, cache_ttl: 20) }
|
10
|
+
let(:key) { request.hash.to_s }
|
11
|
+
|
12
|
+
describe '#get' do
|
13
|
+
it 'calls #read_entry' do
|
14
|
+
expect(cache).to receive(:read_entry).with(key)
|
15
|
+
|
16
|
+
cache.get(request)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#set' do
|
21
|
+
let(:response) { Typhoeus::Response.new }
|
22
|
+
|
23
|
+
it 'calls #write_entry' do
|
24
|
+
expect(cache).to receive(:write_entry).with(key, response, request.cache_ttl)
|
25
|
+
|
26
|
+
cache.set(request, response)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CMSScanner
|
4
|
+
module Controller
|
5
|
+
# Failure class for testing
|
6
|
+
class SpecFailure < Base
|
7
|
+
def before_scan
|
8
|
+
fail 'error spotted'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe CMSScanner::Scan do
|
15
|
+
|
16
|
+
subject(:scanner) { described_class.new }
|
17
|
+
let(:controller) { CMSScanner::Controller }
|
18
|
+
|
19
|
+
describe '#new, #controllers' do
|
20
|
+
its(:controllers) { should eq([controller::Core.new]) }
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#run' do
|
24
|
+
it 'runs the controlllers and calls the formatter#beautify' do
|
25
|
+
expect(scanner.controllers).to receive(:run)
|
26
|
+
expect(scanner.formatter).to receive(:beautify)
|
27
|
+
scanner.run
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when an error is raised during the #run' do
|
31
|
+
it 'aborts the scan with the associated output' do
|
32
|
+
scanner.controllers[0] = controller::SpecFailure.new
|
33
|
+
|
34
|
+
expect(scanner.formatter).to receive(:output)
|
35
|
+
.with('@scan_aborted', hash_including(:reason, :trace, :verbose))
|
36
|
+
|
37
|
+
scanner.run
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#datastore' do
|
43
|
+
its(:datastore) { should eq({}) }
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe CMSScanner::Controller do
|
4
|
+
|
5
|
+
subject(:controller) { described_class::Base.new }
|
6
|
+
|
7
|
+
context 'when parsed_options' do
|
8
|
+
before { described_class::Base.parsed_options = parsed_options }
|
9
|
+
let(:parsed_options) { { url: 'http://example.com/' } }
|
10
|
+
|
11
|
+
its(:parsed_options) { should eq(parsed_options) }
|
12
|
+
its(:formatter) { should be_a CMSScanner::Formatter::Cli }
|
13
|
+
its(:target) { should be_a CMSScanner::Target }
|
14
|
+
|
15
|
+
describe '#render' do
|
16
|
+
it 'calls the formatter#render' do
|
17
|
+
expect(controller.formatter).to receive(:render).with('test', { verbose: nil }, 'base')
|
18
|
+
controller.render('test')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CMSScanner
|
4
|
+
module Controller
|
5
|
+
class Spec < Base
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe CMSScanner::Controllers do
|
11
|
+
|
12
|
+
subject(:controllers) { described_class.new }
|
13
|
+
let(:controller_mod) { CMSScanner::Controller }
|
14
|
+
|
15
|
+
describe '#<<' do
|
16
|
+
its(:size) { should be 0 }
|
17
|
+
|
18
|
+
context 'when controllers are added' do
|
19
|
+
before { controllers << controller_mod::Spec.new << controller_mod::Base.new }
|
20
|
+
|
21
|
+
its(:size) { should be 2 }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when a controller is added twice' do
|
25
|
+
before { 2.times { controllers << controller_mod::Spec.new } }
|
26
|
+
|
27
|
+
its(:size) { should be 1 }
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns self' do
|
31
|
+
expect(controllers << controller_mod::Spec.new).to be_a described_class
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#run' do
|
36
|
+
it 'runs the before_scan, run and after_scan methods of each controller' do
|
37
|
+
spec = controller_mod::Spec.new
|
38
|
+
base = controller_mod::Base.new
|
39
|
+
|
40
|
+
controllers << spec << base
|
41
|
+
|
42
|
+
# TODO: Any way to test the orders ? (after_scan should reverse the order)
|
43
|
+
[base, spec].each do |c|
|
44
|
+
expect(c).to receive(:before_scan)
|
45
|
+
expect(c).to receive(:run)
|
46
|
+
expect(c).to receive(:after_scan)
|
47
|
+
end
|
48
|
+
controllers.run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|