recog 3.1.1 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- 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
|