recog 3.1.1 → 3.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/Gemfile +6 -0
- data/Rakefile +7 -5
- data/lib/recog/db.rb +67 -68
- data/lib/recog/db_manager.rb +22 -21
- data/lib/recog/fingerprint/regexp_factory.rb +10 -13
- data/lib/recog/fingerprint/test.rb +9 -8
- data/lib/recog/fingerprint.rb +252 -262
- data/lib/recog/fingerprint_parse_error.rb +3 -1
- data/lib/recog/formatter.rb +41 -39
- data/lib/recog/match_reporter.rb +82 -83
- data/lib/recog/matcher.rb +37 -40
- data/lib/recog/matcher_factory.rb +7 -6
- data/lib/recog/nizer.rb +218 -224
- data/lib/recog/verifier.rb +30 -28
- data/lib/recog/verify_reporter.rb +69 -73
- data/lib/recog/version.rb +3 -1
- data/lib/recog.rb +2 -0
- data/recog/bin/recog_match +21 -20
- data/recog/xml/apache_modules.xml +2 -0
- data/recog/xml/dhcp_vendor_class.xml +1 -1
- data/recog/xml/favicons.xml +133 -1
- data/recog/xml/ftp_banners.xml +1 -1
- data/recog/xml/html_title.xml +140 -1
- data/recog/xml/http_cookies.xml +20 -2
- data/recog/xml/http_servers.xml +38 -17
- data/recog/xml/http_wwwauth.xml +17 -4
- data/recog/xml/mdns_device-info_txt.xml +49 -15
- data/recog/xml/sip_banners.xml +0 -2
- data/recog/xml/sip_user_agents.xml +1 -1
- data/recog/xml/snmp_sysdescr.xml +1 -2
- data/recog/xml/ssh_banners.xml +8 -0
- data/recog/xml/telnet_banners.xml +3 -2
- data/recog/xml/tls_jarm.xml +1 -1
- data/recog/xml/x11_banners.xml +1 -0
- data/recog/xml/x509_issuers.xml +1 -1
- data/recog/xml/x509_subjects.xml +0 -1
- data/recog.gemspec +14 -13
- data/spec/lib/recog/db_spec.rb +37 -36
- data/spec/lib/recog/fingerprint/regexp_factory_spec.rb +19 -20
- data/spec/lib/recog/fingerprint_spec.rb +44 -42
- data/spec/lib/recog/formatter_spec.rb +20 -18
- data/spec/lib/recog/match_reporter_spec.rb +35 -30
- data/spec/lib/recog/nizer_spec.rb +85 -101
- data/spec/lib/recog/verify_reporter_spec.rb +45 -44
- data/spec/spec_helper.rb +2 -1
- data.tar.gz.sig +1 -3
- metadata +3 -3
- metadata.gz.sig +0 -0
data/spec/lib/recog/db_spec.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'recog/db'
|
2
4
|
|
3
5
|
describe Recog::DB do
|
4
|
-
|
5
|
-
|
6
|
-
context "with inline example content" do
|
6
|
+
describe '#fingerprints' do
|
7
|
+
context 'with inline example content' do
|
7
8
|
let(:xml_file) { File.expand_path File.join('spec', 'data', 'test_fingerprints.xml') }
|
8
9
|
subject { Recog::DB.new(xml_file) }
|
9
10
|
|
@@ -11,50 +12,50 @@ describe Recog::DB do
|
|
11
12
|
|
12
13
|
it { is_expected.to be_a(Enumerable) }
|
13
14
|
|
14
|
-
context
|
15
|
+
context 'with only a pattern' do
|
15
16
|
subject(:entry) { described_class.new(xml_file).fingerprints[0] }
|
16
17
|
|
17
|
-
it
|
18
|
+
it 'has a blank name with no description' do
|
18
19
|
expect(entry.name).to be_empty
|
19
20
|
end
|
20
21
|
|
21
|
-
it
|
22
|
-
expect(entry.regex.source).to eq(
|
22
|
+
it 'has a pattern' do
|
23
|
+
expect(entry.regex.source).to eq('.*\\(iSeries\\).*')
|
23
24
|
end
|
24
25
|
|
25
|
-
it
|
26
|
+
it 'has no params' do
|
26
27
|
expect(entry.params).to be_empty
|
27
28
|
end
|
28
29
|
|
29
|
-
it
|
30
|
+
it 'has no tests' do
|
30
31
|
expect(entry.tests).to be_empty
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
|
-
context
|
35
|
+
context 'with params' do
|
35
36
|
subject(:entry) { described_class.new(xml_file).fingerprints[1] }
|
36
37
|
|
37
|
-
it
|
38
|
+
it 'has a name' do
|
38
39
|
expect(entry.name).to eq('PalmOS')
|
39
40
|
end
|
40
41
|
|
41
|
-
it
|
42
|
-
expect(entry.regex.source).to eq(
|
42
|
+
it 'has a pattern' do
|
43
|
+
expect(entry.regex.source).to eq('.*\\(PalmOS\\).*')
|
43
44
|
end
|
44
45
|
|
45
|
-
it
|
46
|
-
expect(entry.params).to eq({
|
46
|
+
it 'has params' do
|
47
|
+
expect(entry.params).to eq({ 'os.vendor' => [1, 'Palm'], 'os.device' => [2, 'General'] })
|
47
48
|
end
|
48
49
|
|
49
|
-
it
|
50
|
+
it 'has no tests' do
|
50
51
|
expect(entry.tests).to be_empty
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
54
|
-
context
|
55
|
+
context 'with pattern flags' do
|
55
56
|
subject(:entry) { described_class.new(xml_file).fingerprints[2] }
|
56
57
|
|
57
|
-
it
|
58
|
+
it 'has a name and only uses the first value' do
|
58
59
|
expect(entry.name).to eq('HP Designjet printer')
|
59
60
|
end
|
60
61
|
|
@@ -63,57 +64,57 @@ describe Recog::DB do
|
|
63
64
|
expect(entry.regex.options).to eq(Recog::Fingerprint::RegexpFactory::DEFAULT_FLAGS | Regexp::IGNORECASE)
|
64
65
|
end
|
65
66
|
|
66
|
-
it
|
67
|
+
it 'has a pattern' do
|
67
68
|
expect(entry.regex).to be_a(Regexp)
|
68
|
-
expect(entry.regex.source).to eq(
|
69
|
+
expect(entry.regex.source).to eq('(designjet \\S+)')
|
69
70
|
end
|
70
71
|
|
71
|
-
it
|
72
|
-
expect(entry.params).to eq({
|
72
|
+
it 'has params' do
|
73
|
+
expect(entry.params).to eq({ 'service.vendor' => [0, 'HP'] })
|
73
74
|
end
|
74
75
|
|
75
|
-
it
|
76
|
+
it 'has no tests' do
|
76
77
|
expect(entry.tests).to be_empty
|
77
78
|
end
|
78
79
|
end
|
79
80
|
|
80
|
-
context
|
81
|
+
context 'with test' do
|
81
82
|
subject(:entry) { described_class.new(xml_file).fingerprints[3] }
|
82
83
|
|
83
|
-
it
|
84
|
+
it 'has a name' do
|
84
85
|
expect(entry.name).to eq('HP JetDirect Printer')
|
85
86
|
end
|
86
87
|
|
87
|
-
it
|
88
|
-
expect(entry.regex.source).to eq(
|
88
|
+
it 'has a pattern' do
|
89
|
+
expect(entry.regex.source).to eq('laserjet (.*)(?: series)?')
|
89
90
|
end
|
90
91
|
|
91
|
-
it
|
92
|
-
expect(entry.params).to eq({
|
92
|
+
it 'has params' do
|
93
|
+
expect(entry.params).to eq({ 'service.vendor' => [0, 'HP'] })
|
93
94
|
end
|
94
95
|
|
95
|
-
it
|
96
|
-
expect(entry.tests.map(&:content)).to match_array([
|
96
|
+
it 'has tests' do
|
97
|
+
expect(entry.tests.map(&:content)).to match_array(['HP LaserJet 4100 Series', 'HP LaserJet 2200'])
|
97
98
|
end
|
98
99
|
end
|
99
100
|
end
|
100
101
|
|
101
|
-
context
|
102
|
+
context 'with external example content' do
|
102
103
|
let(:xml_file) { File.expand_path File.join('spec', 'data', 'external_example_fingerprint.xml') }
|
103
104
|
subject { Recog::DB.new(xml_file) }
|
104
105
|
|
105
106
|
subject(:entry) { described_class.new(xml_file).fingerprints[0] }
|
106
107
|
|
107
|
-
it
|
108
|
-
expect(entry.tests.map(&:content)).to match_array([
|
108
|
+
it 'has tests' do
|
109
|
+
expect(entry.tests.map(&:content)).to match_array(['HP LaserJet 4100 Series', 'HP LaserJet 2200'])
|
109
110
|
end
|
110
111
|
end
|
111
112
|
|
112
|
-
context
|
113
|
+
context 'with external example content illegal path' do
|
113
114
|
let(:xml_file) { File.expand_path File.join('spec', 'data', 'external_example_illegal_path_fingerprint.xml') }
|
114
115
|
subject { Recog::DB.new(xml_file) }
|
115
116
|
|
116
|
-
it
|
117
|
+
it 'raises an illegal file path error' do
|
117
118
|
expect { subject }.to raise_error(/an example specifies an illegal file path '.+'/)
|
118
119
|
end
|
119
120
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
require 'recog/fingerprint/regexp_factory'
|
3
4
|
|
4
5
|
describe Recog::Fingerprint::RegexpFactory do
|
5
|
-
|
6
6
|
describe 'FLAG_MAP' do
|
7
7
|
subject { described_class::FLAG_MAP }
|
8
8
|
|
9
|
-
it
|
9
|
+
it 'should have the right number of flags' do
|
10
10
|
expect(subject.size).to be 5
|
11
11
|
end
|
12
12
|
end
|
@@ -15,56 +15,55 @@ describe Recog::Fingerprint::RegexpFactory do
|
|
15
15
|
subject { described_class.build(pattern, options) }
|
16
16
|
|
17
17
|
let(:pattern) { 'Apache/(\d+)' }
|
18
|
-
let(:options) { [
|
18
|
+
let(:options) { ['REG_ICASE'] }
|
19
19
|
|
20
20
|
it { is_expected.to be_a(Regexp) }
|
21
21
|
it { is_expected.to match('Apache/2') }
|
22
|
-
|
23
22
|
end
|
24
23
|
|
25
24
|
describe '.build_options' do
|
26
25
|
subject { described_class.build_options(flags) }
|
27
26
|
|
28
|
-
let(:flags) { [
|
27
|
+
let(:flags) { [] }
|
29
28
|
it { is_expected.to be_a(Integer) }
|
30
29
|
|
31
30
|
context 'without any explicit flags' do
|
32
|
-
let(:flags) { [
|
33
|
-
specify
|
31
|
+
let(:flags) { [] }
|
32
|
+
specify 'sets default flags' do
|
34
33
|
expect(subject).to be Recog::Fingerprint::RegexpFactory::DEFAULT_FLAGS
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
context 'with REG_ICASE' do
|
39
|
-
let(:flags) { [
|
40
|
-
specify
|
41
|
-
expect(subject).to be
|
38
|
+
let(:flags) { ['REG_ICASE'] }
|
39
|
+
specify 'sets IGNORECASE' do
|
40
|
+
expect(subject).to be(Recog::Fingerprint::RegexpFactory::DEFAULT_FLAGS | Regexp::IGNORECASE)
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
45
44
|
context 'with REG_DOT_NEWLINE' do
|
46
|
-
let(:flags) { [
|
47
|
-
specify
|
48
|
-
expect(subject).to be
|
45
|
+
let(:flags) { ['REG_DOT_NEWLINE'] }
|
46
|
+
specify 'sets MULTILINE' do
|
47
|
+
expect(subject).to be(Recog::Fingerprint::RegexpFactory::DEFAULT_FLAGS | Regexp::MULTILINE)
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
52
51
|
context 'with REG_LINE_ANY_CRLF' do
|
53
|
-
let(:flags) { [
|
54
|
-
specify
|
55
|
-
expect(subject).to be
|
52
|
+
let(:flags) { ['REG_LINE_ANY_CRLF'] }
|
53
|
+
specify 'sets MULTILINE' do
|
54
|
+
expect(subject).to be(Recog::Fingerprint::RegexpFactory::DEFAULT_FLAGS | Regexp::MULTILINE)
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
59
58
|
context 'with multiple flags' do
|
60
|
-
let(:flags) { [
|
61
|
-
specify
|
62
|
-
expect(subject).to be
|
59
|
+
let(:flags) { %w[REG_LINE_ANY_CRLF REG_ICASE] }
|
60
|
+
specify 'sets correct flags' do
|
61
|
+
expect(subject).to be(Recog::Fingerprint::RegexpFactory::DEFAULT_FLAGS | Regexp::MULTILINE | Regexp::IGNORECASE)
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
66
65
|
context 'with invalid flags' do
|
67
|
-
let(:flags) { %w
|
66
|
+
let(:flags) { %w[SYN ACK FIN] } # oh, wrong flags!
|
68
67
|
specify 'raises and lists supported/unsupported flags' do
|
69
68
|
expect { subject }.to raise_error(/SYN,ACK,FIN. Must be one of: .+/)
|
70
69
|
end
|
@@ -1,112 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'nokogiri'
|
2
4
|
require 'recog/fingerprint'
|
3
5
|
|
4
6
|
describe Recog::Fingerprint do
|
5
|
-
context
|
7
|
+
context 'whitespace' do
|
6
8
|
let(:xml) do
|
7
9
|
path = File.expand_path(File.join('spec', 'data', 'whitespaced_fingerprint.xml'))
|
8
10
|
doc = Nokogiri::XML(IO.read(path))
|
9
|
-
doc.xpath(
|
11
|
+
doc.xpath('//fingerprint').first
|
10
12
|
end
|
11
13
|
subject { Recog::Fingerprint.new(xml) }
|
12
14
|
|
13
|
-
describe
|
14
|
-
it
|
15
|
+
describe '#name' do
|
16
|
+
it 'properly squashes whitespace' do
|
15
17
|
expect(subject.name).to eq('I love whitespace!')
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
19
21
|
|
20
|
-
describe
|
22
|
+
describe '#verification' do
|
21
23
|
let(:xml_file) { File.expand_path(File.join('spec', 'data', 'verification_fingerprints.xml')) }
|
22
24
|
let(:doc) { Nokogiri::XML(IO.read(xml_file)) }
|
23
25
|
|
24
|
-
context
|
25
|
-
let(:entry) { described_class.new(doc.xpath(
|
26
|
+
context '0 params' do
|
27
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[0]) }
|
26
28
|
|
27
|
-
it
|
29
|
+
it 'does not yield if a fingerprint has 0 parameters' do
|
28
30
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.not_to yield_control
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
32
|
-
context
|
33
|
-
let(:entry) { described_class.new(doc.xpath(
|
34
|
+
context '0 capture groups' do
|
35
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[1]) }
|
34
36
|
|
35
|
-
it
|
37
|
+
it 'does not yield if a fingerprint has parameters, but 0 are defined by a capture group ' do
|
36
38
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.not_to yield_control
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
|
-
context
|
41
|
-
let(:entry) { described_class.new(doc.xpath(
|
42
|
+
context '0 examples' do
|
43
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[2]) }
|
42
44
|
|
43
|
-
it
|
45
|
+
it 'does not yield if a fingerprint has 0 examples' do
|
44
46
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.not_to yield_control
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
context
|
49
|
-
|
50
|
-
let(:entry) { described_class.new(doc.xpath("//fingerprints/fingerprint")[3]) }
|
50
|
+
context '1 capture group, 1 example' do
|
51
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[3]) }
|
51
52
|
|
52
|
-
it
|
53
|
+
it 'does not yield when one capture group parameter is tested for in one example' do
|
53
54
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.not_to yield_control
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
57
|
-
context
|
58
|
-
let(:entry) { described_class.new(doc.xpath(
|
58
|
+
context '2 capture groups, 1 example' do
|
59
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[4]) }
|
59
60
|
|
60
|
-
it
|
61
|
+
it 'does not yield when two capture group parameters are tested for in one example' do
|
61
62
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.not_to yield_control
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
65
|
-
context
|
66
|
-
let(:entry) { described_class.new(doc.xpath(
|
66
|
+
context '2 capture groups, 2 examples, 1 in each' do
|
67
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[5]) }
|
67
68
|
|
68
|
-
it
|
69
|
+
it 'does not yield when two capture group parameters are tested for in two examples, one parameter in each' do
|
69
70
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.not_to yield_control
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
|
-
context
|
74
|
-
|
75
|
-
let(:entry) { described_class.new(doc.xpath("//fingerprints/fingerprint")[6]) }
|
74
|
+
context '1 missing capture group, 1 example' do
|
75
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[6]) }
|
76
76
|
|
77
|
-
it
|
77
|
+
it 'identifies when a parameter defined by a capture group is not included in one example' do
|
78
78
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.to yield_successive_args([:fail, String])
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
-
context
|
83
|
-
let(:entry) { described_class.new(doc.xpath(
|
82
|
+
context '2 missing capture groups, 1 example' do
|
83
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[7]) }
|
84
84
|
|
85
|
-
it
|
86
|
-
expect
|
85
|
+
it 'identifies when two parameters defined by a capture groups are not included in one example' do
|
86
|
+
expect do |unused|
|
87
|
+
entry.verify_tests_have_capture_groups(&unused)
|
88
|
+
end.to yield_successive_args([:fail, String], [:fail, String])
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
90
|
-
context
|
92
|
+
context '1 missing capture group, 2 examples' do
|
93
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[8]) }
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
it "identifies when a parameter defined by a capture group is not included in one example" do
|
95
|
+
it 'identifies when a parameter defined by a capture group is not included in one example' do
|
95
96
|
expect { |unused| entry.verify_tests_have_capture_groups(&unused) }.to yield_successive_args([:fail, String])
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
99
|
-
context
|
100
|
-
let(:entry) { described_class.new(doc.xpath(
|
100
|
+
context '2 missing capture groups, 2 examples' do
|
101
|
+
let(:entry) { described_class.new(doc.xpath('//fingerprints/fingerprint')[9]) }
|
101
102
|
|
102
|
-
it
|
103
|
-
expect
|
103
|
+
it 'identifies when two parameters defined by a capture groups are not included in one example' do
|
104
|
+
expect do |unused|
|
105
|
+
entry.verify_tests_have_capture_groups(&unused)
|
106
|
+
end.to yield_successive_args([:fail, String], [:fail, String])
|
104
107
|
end
|
105
108
|
end
|
106
|
-
|
107
109
|
end
|
108
110
|
|
109
|
-
skip
|
111
|
+
skip 'value interpolation' do
|
110
112
|
# TODO
|
111
113
|
end
|
112
114
|
end
|
@@ -1,66 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'recog/formatter'
|
2
4
|
|
3
5
|
describe Recog::Formatter do
|
4
6
|
let(:output) { StringIO.new }
|
5
7
|
|
6
|
-
context
|
8
|
+
context 'with no color' do
|
7
9
|
subject { Recog::Formatter.new(double(color: false), output) }
|
8
10
|
|
9
|
-
describe
|
10
|
-
it
|
11
|
+
describe '#message' do
|
12
|
+
it 'outputs the text' do
|
11
13
|
subject.status_message 'some text'
|
12
14
|
expect(output.string).to eq("some text\n")
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
describe
|
17
|
-
it
|
18
|
+
describe '#success_message' do
|
19
|
+
it 'outputs the text' do
|
18
20
|
subject.success_message 'a success'
|
19
21
|
expect(output.string).to eq("a success\n")
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
describe
|
24
|
-
it
|
25
|
+
describe '#warning_message' do
|
26
|
+
it 'outputs the text' do
|
25
27
|
subject.warning_message 'a warning'
|
26
28
|
expect(output.string).to eq("a warning\n")
|
27
29
|
end
|
28
30
|
end
|
29
31
|
|
30
|
-
describe
|
31
|
-
it
|
32
|
+
describe '#failure_message' do
|
33
|
+
it 'outputs the text' do
|
32
34
|
subject.failure_message 'a failure'
|
33
35
|
expect(output.string).to eq("a failure\n")
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
|
-
context
|
40
|
+
context 'with color' do
|
39
41
|
subject { Recog::Formatter.new(double(color: true), output) }
|
40
42
|
|
41
|
-
describe
|
42
|
-
it
|
43
|
+
describe '#message' do
|
44
|
+
it 'outputs the text in white' do
|
43
45
|
subject.status_message 'some text'
|
44
46
|
expect(output.string).to eq("\e[15msome text\e[0m\n")
|
45
47
|
end
|
46
48
|
end
|
47
49
|
|
48
|
-
describe
|
49
|
-
it
|
50
|
+
describe '#success_message' do
|
51
|
+
it 'outputs the text in green' do
|
50
52
|
subject.success_message 'a success'
|
51
53
|
expect(output.string).to eq("\e[32ma success\e[0m\n")
|
52
54
|
end
|
53
55
|
end
|
54
56
|
|
55
|
-
describe
|
56
|
-
it
|
57
|
+
describe '#warning_message' do
|
58
|
+
it 'outputs the text in yellow' do
|
57
59
|
subject.warning_message 'a warning'
|
58
60
|
expect(output.string).to eq("\e[33ma warning\e[0m\n")
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
62
|
-
describe
|
63
|
-
it
|
64
|
+
describe '#failure_message' do
|
65
|
+
it 'outputs the text in red' do
|
64
66
|
subject.failure_message 'a failure'
|
65
67
|
expect(output.string).to eq("\e[31ma failure\e[0m\n")
|
66
68
|
end
|
@@ -1,103 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'recog/match_reporter'
|
2
4
|
|
3
5
|
describe Recog::MatchReporter do
|
4
|
-
let(:options) { double(detail: false, json_format: false, quiet: false, multi_match: false)
|
6
|
+
let(:options) { double(detail: false, json_format: false, quiet: false, multi_match: false) }
|
5
7
|
let(:formatter) { double('formatter').as_null_object }
|
6
8
|
subject { Recog::MatchReporter.new(options, formatter) }
|
7
9
|
|
8
10
|
def run_report
|
9
11
|
subject.report do
|
10
|
-
|
11
|
-
|
12
|
-
|
12
|
+
subject.increment_line_count
|
13
|
+
subject.match [{ 'data' => 'a match' }]
|
14
|
+
subject.failure 'a failure'
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
describe
|
17
|
-
it
|
18
|
+
describe '#report' do
|
19
|
+
it 'prints matches' do
|
18
20
|
expect(formatter).to receive(:success_message).with('MATCH: {"data"=>"a match"}')
|
19
21
|
run_report
|
20
22
|
end
|
21
23
|
|
22
|
-
it
|
24
|
+
it 'prints failures' do
|
23
25
|
expect(formatter).to receive(:failure_message).with('FAIL: a failure')
|
24
26
|
run_report
|
25
27
|
end
|
26
28
|
|
27
|
-
context
|
28
|
-
subject
|
29
|
+
context 'with detail' do
|
30
|
+
subject do
|
31
|
+
Recog::MatchReporter.new(double(detail: true, json_format: false, quiet: false, multi_match: false), formatter)
|
32
|
+
end
|
29
33
|
|
30
|
-
it
|
34
|
+
it 'prints the lines processed' do
|
31
35
|
expect(formatter).to receive(:status_message).with("\nProcessed 1 lines")
|
32
36
|
run_report
|
33
37
|
end
|
34
38
|
|
35
|
-
it
|
36
|
-
expect(formatter).to receive(:failure_message).with(
|
39
|
+
it 'prints summary' do
|
40
|
+
expect(formatter).to receive(:failure_message).with('SUMMARY: 1 matches and 1 failures')
|
37
41
|
run_report
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
41
|
-
context
|
42
|
-
subject
|
45
|
+
context 'with JSON' do
|
46
|
+
subject do
|
47
|
+
Recog::MatchReporter.new(double(detail: false, json_format: true, quiet: false, multi_match: false), formatter)
|
48
|
+
end
|
43
49
|
|
44
|
-
it
|
50
|
+
it 'prints matches' do
|
45
51
|
expect(formatter).to receive(:success_message).with('{"data":"a match","match":{}}')
|
46
52
|
run_report
|
47
53
|
end
|
48
54
|
|
49
|
-
it
|
55
|
+
it 'prints failures' do
|
50
56
|
expect(formatter).to receive(:failure_message).with('{"data":"a failure","match_failure":true,"match":null}')
|
51
57
|
run_report
|
52
58
|
end
|
53
59
|
end
|
54
60
|
end
|
55
61
|
|
56
|
-
describe
|
57
|
-
context
|
62
|
+
describe '#print_summary' do
|
63
|
+
context 'with all matches' do
|
58
64
|
before { subject.match ['match'] }
|
59
65
|
|
60
|
-
it
|
61
|
-
msg =
|
66
|
+
it 'prints a successful summary' do
|
67
|
+
msg = 'SUMMARY: 1 matches and 0 failures'
|
62
68
|
expect(formatter).to receive(:success_message).with(msg)
|
63
69
|
subject.print_summary
|
64
70
|
end
|
65
71
|
end
|
66
72
|
|
67
|
-
context
|
73
|
+
context 'with failures' do
|
68
74
|
before { subject.failure 'fail' }
|
69
75
|
|
70
|
-
it
|
71
|
-
msg =
|
76
|
+
it 'prints a failure summary' do
|
77
|
+
msg = 'SUMMARY: 0 matches and 1 failures'
|
72
78
|
expect(formatter).to receive(:failure_message).with(msg)
|
73
79
|
subject.print_summary
|
74
80
|
end
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
78
|
-
describe
|
79
|
-
context
|
80
|
-
|
84
|
+
describe '#stop?' do
|
85
|
+
context 'with a failure limit' do
|
81
86
|
let(:options) { double(fail_fast: true, stop_after: 3, detail: false, json_format: false, multi_match: false) }
|
82
87
|
before do
|
83
88
|
subject.failure 'first'
|
84
89
|
subject.failure 'second'
|
85
90
|
end
|
86
91
|
|
87
|
-
it
|
92
|
+
it 'returns true when the limit is reached ' do
|
88
93
|
subject.failure 'third'
|
89
94
|
expect(subject.stop?).to be true
|
90
95
|
end
|
91
96
|
|
92
|
-
it
|
97
|
+
it 'returns false when under the limit' do
|
93
98
|
expect(subject.stop?).to be false
|
94
99
|
end
|
95
100
|
end
|
96
101
|
|
97
|
-
context
|
102
|
+
context 'with no failure limit' do
|
98
103
|
let(:options) { double(fail_fast: false, detail: false, json_format: false, multi_match: false) }
|
99
104
|
|
100
|
-
it
|
105
|
+
it 'return false' do
|
101
106
|
expect(subject.stop?).to be false
|
102
107
|
end
|
103
108
|
end
|