puppet-lint 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rspec +1 -0
  4. data/.travis.yml +11 -2
  5. data/CHANGELOG.md +657 -0
  6. data/Gemfile +19 -0
  7. data/README.md +6 -1
  8. data/lib/puppet-lint.rb +1 -1
  9. data/lib/puppet-lint/checks.rb +1 -5
  10. data/lib/puppet-lint/lexer.rb +4 -2
  11. data/lib/puppet-lint/optparser.rb +11 -2
  12. data/lib/puppet-lint/plugins/check_classes.rb +5 -1
  13. data/lib/puppet-lint/plugins/check_comments.rb +6 -8
  14. data/lib/puppet-lint/plugins/check_resources.rb +4 -3
  15. data/lib/puppet-lint/plugins/check_variables.rb +21 -3
  16. data/lib/puppet-lint/plugins/check_whitespace.rb +21 -1
  17. data/lib/puppet-lint/tasks/puppet-lint.rb +4 -0
  18. data/lib/puppet-lint/version.rb +1 -1
  19. data/puppet-lint.gemspec +1 -4
  20. data/spec/puppet-lint/bin_spec.rb +1 -1
  21. data/spec/puppet-lint/lexer_spec.rb +38 -0
  22. data/spec/puppet-lint/plugins/check_classes/parameter_order_spec.rb +51 -47
  23. data/spec/puppet-lint/plugins/check_comments/star_comments_spec.rb +22 -0
  24. data/spec/puppet-lint/plugins/check_resources/ensure_first_param_spec.rb +19 -0
  25. data/spec/puppet-lint/plugins/check_resources/file_mode_spec.rb +96 -0
  26. data/spec/puppet-lint/plugins/check_resources/unquoted_file_mode_spec.rb +28 -0
  27. data/spec/puppet-lint/plugins/check_strings/variables_not_enclosed_spec.rb +3 -3
  28. data/spec/puppet-lint/plugins/check_variables/variable_is_lowercase_spec.rb +25 -0
  29. data/spec/puppet-lint/plugins/check_whitespace/80chars_spec.rb +71 -0
  30. data/spec/puppet-lint/plugins/check_whitespace/arrow_alignment_spec.rb +37 -37
  31. data/spec/puppet-lint_spec.rb +6 -0
  32. metadata +13 -50
data/Gemfile CHANGED
@@ -1,3 +1,22 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ group :test do
6
+ gem 'rake', '~> 10.0'
7
+ gem 'rspec', '~> 3.0'
8
+ gem 'rspec-its', '~> 1.0'
9
+ gem 'rspec-collection_matchers', '~> 1.0'
10
+ gem 'rack', '~> 1.0'
11
+
12
+ if RUBY_VERSION < '2.0'
13
+ # json 2.x requires ruby 2.0. Lock to 1.8
14
+ gem 'json', '~> 1.8'
15
+ # json_pure 2.0.2 requires ruby 2.0. Lock to 2.0.1
16
+ gem 'json_pure', '= 2.0.1'
17
+ # addressable 2.4.0 requires ruby 1.9.0. Lock to 2.3.8.
18
+ gem 'addressable', '= 2.3.8'
19
+ else
20
+ gem 'json'
21
+ end
22
+ end
data/README.md CHANGED
@@ -174,6 +174,11 @@ If you find a bug in puppet-lint or its results, please create an issue in the
174
174
  [repo issues tracker](https://github.com/rodjek/puppet-lint/issues/). Bonus
175
175
  points will be awarded if you also include a patch that fixes the issue.
176
176
 
177
+ ## Executing puppet-lint tests suite
178
+
179
+ bundle exec rspec [spec/puppet-lint/a_single_test.rb]
180
+
181
+
177
182
  ## Thank You
178
183
 
179
184
  Many thanks to the following people for contributing to puppet-lint
@@ -187,7 +192,7 @@ As well as the many people who have reported the issues they've had!
187
192
 
188
193
  ## License
189
194
 
190
- Copyright (c) 2011 Tim Sharpe
195
+ Copyright (c) 2011-2016 Tim Sharpe
191
196
 
192
197
  Permission is hereby granted, free of charge, to any person obtaining
193
198
  a copy of this software and associated documentation files (the
@@ -166,7 +166,7 @@ class PuppetLint
166
166
 
167
167
  if @code.empty?
168
168
  @problems = []
169
- @manifest = []
169
+ @manifest = ''
170
170
  return
171
171
  end
172
172
 
@@ -58,16 +58,12 @@ class PuppetLint::Checks
58
58
  problems = klass.run
59
59
 
60
60
  if PuppetLint.configuration.fix
61
- checks_run << klass
61
+ @problems.concat(klass.fix_problems)
62
62
  else
63
63
  @problems.concat(problems)
64
64
  end
65
65
  end
66
66
 
67
- checks_run.each do |check|
68
- @problems.concat(check.fix_problems)
69
- end
70
-
71
67
  @problems
72
68
  end
73
69
 
@@ -62,6 +62,7 @@ class PuppetLint
62
62
  :MATCH => true,
63
63
  :NOMATCH => true,
64
64
  :COMMA => true,
65
+ :LBRACK => true,
65
66
  }
66
67
 
67
68
  # Internal: An Array of Arrays containing tokens that can be described by
@@ -69,6 +70,7 @@ class PuppetLint
69
70
  # name of the token as a Symbol and a regular expression describing the
70
71
  # value of the token.
71
72
  KNOWN_TOKENS = [
73
+ [:TYPE, /\A(Integer|Float|Boolean|Regexp|String|Array|Hash|Resource|Class|Collection|Scalar|Numeric|CatalogEntry|Data|Tuple|Struct|Optional|NotUndef|Variant|Enum|Pattern|Any|Callable|Type|Runtime|Undef|Default)/],
72
74
  [:CLASSREF, /\A(((::){0,1}[A-Z][-\w]*)+)/],
73
75
  [:NUMBER, /\A\b((?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?))\b/],
74
76
  [:NAME, /\A(((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*)/],
@@ -168,7 +170,7 @@ class PuppetLint
168
170
  end
169
171
 
170
172
  unless found
171
- if var_name = chunk[/\A\$((::)?([\w-]+::)*[\w-]+(\[.+?\])*)/, 1]
173
+ if var_name = chunk[/\A\$((::)?([\w]+::)*[\w]+(\[.+?\])*)/, 1]
172
174
  length = var_name.size + 1
173
175
  tokens << new_token(:VARIABLE, var_name, length)
174
176
 
@@ -344,7 +346,7 @@ class PuppetLint
344
346
  tokens << new_token(:DQMID, value, value.size, :line => line, :column => token_column)
345
347
  end
346
348
  if ss.scan(/\{/).nil?
347
- var_name = ss.scan(/(::)?([\w-]+::)*[\w-]+/)
349
+ var_name = ss.scan(/(::)?([\w]+::)*[\w]+/)
348
350
  if var_name.nil?
349
351
  token_column = column + ss.pos - 1
350
352
  tokens << new_token(:DQMID, "$", 1, :line => line, :column => token_column)
@@ -94,8 +94,12 @@ class PuppetLint::OptParser
94
94
 
95
95
  opts.on('--only-checks CHECKS', 'A comma separated list of checks that should be run') do |checks|
96
96
  enable_checks = checks.split(',').map(&:to_sym)
97
- (PuppetLint.configuration.checks - enable_checks).each do |check|
98
- PuppetLint.configuration.send("disable_#{check}")
97
+ (PuppetLint.configuration.checks).each do |check|
98
+ if enable_checks.include? check
99
+ PuppetLint.configuration.send("enable_#{check}")
100
+ else
101
+ PuppetLint.configuration.send("disable_#{check}")
102
+ end
99
103
  end
100
104
  end
101
105
 
@@ -103,6 +107,11 @@ class PuppetLint::OptParser
103
107
  opts.on("--no-#{check}-check", "Skip the #{check} check.") do
104
108
  PuppetLint.configuration.send("disable_#{check}")
105
109
  end
110
+ unless PuppetLint.configuration.send("#{check}_enabled?")
111
+ opts.on("--#{check}-check", "Enable the #{check} check.") do
112
+ PuppetLint.configuration.send("enable_#{check}")
113
+ end
114
+ end
106
115
  end
107
116
 
108
117
  opts.load('/etc/puppet-lint.rc')
@@ -83,13 +83,14 @@ PuppetLint.new_check(:class_inherits_from_params_class) do
83
83
  end
84
84
  end
85
85
  end
86
+ PuppetLint.configuration.send('disable_class_inherits_from_params_class')
86
87
 
87
88
  # Public: Test the manifest tokens for any parameterised classes or defined
88
89
  # types that take parameters and record a warning if there are any optional
89
90
  # parameters listed before required parameters.
90
91
  PuppetLint.new_check(:parameter_order) do
91
92
  def check
92
- defined_type_indexes.each do |class_idx|
93
+ (class_indexes + defined_type_indexes).each do |class_idx|
93
94
  unless class_idx[:param_tokens].nil?
94
95
  paren_stack = []
95
96
  class_idx[:param_tokens].each_with_index do |token, i|
@@ -197,6 +198,9 @@ PuppetLint.new_check(:variable_scope) do
197
198
  'stage',
198
199
  'subscribe',
199
200
  'tag',
201
+ 'facts',
202
+ 'trusted',
203
+ 'server_facts',
200
204
  ]
201
205
  POST_VAR_TOKENS = Set[:COMMA, :EQUALS, :RPAREN]
202
206
 
@@ -37,19 +37,17 @@ PuppetLint.new_check(:star_comments) do
37
37
 
38
38
  def fix(problem)
39
39
  comment_lines = problem[:token].value.strip.split("\n").map(&:strip)
40
+
40
41
  first_line = comment_lines.shift
41
42
  problem[:token].type = :COMMENT
42
43
  problem[:token].value = " #{first_line}"
43
44
 
44
- index = tokens.index(problem[:token].next_token)
45
+ index = tokens.index(problem[:token].next_token) || 1
45
46
  comment_lines.reverse.each do |line|
46
- [
47
- PuppetLint::Lexer::Token.new(:COMMENT, " #{line}", 0, 0),
48
- PuppetLint::Lexer::Token.new(:INDENT, problem[:token].prev_token.value.dup, 0, 0),
49
- PuppetLint::Lexer::Token.new(:NEWLINE, "\n", 0, 0),
50
- ].each do |new_token|
51
- tokens.insert(index, new_token)
52
- end
47
+ indent = problem[:token].prev_token.nil? ? nil : problem[:token].prev_token.value.dup
48
+ tokens.insert(index, PuppetLint::Lexer::Token.new(:COMMENT, " #{line}", 0, 0))
49
+ tokens.insert(index, PuppetLint::Lexer::Token.new(:INDENT, indent, 0, 0)) if indent
50
+ tokens.insert(index, PuppetLint::Lexer::Token.new(:NEWLINE, "\n", 0, 0))
53
51
  end
54
52
  end
55
53
  end
@@ -25,6 +25,7 @@ end
25
25
  PuppetLint.new_check(:ensure_first_param) do
26
26
  def check
27
27
  resource_indexes.each do |resource|
28
+ next if [:CLASS].include? resource[:type].type
28
29
  ensure_attr_index = resource[:param_tokens].index { |param_token|
29
30
  param_token.value == 'ensure'
30
31
  }
@@ -87,7 +88,7 @@ PuppetLint.new_check(:unquoted_file_mode) do
87
88
 
88
89
  def check
89
90
  resource_indexes.each do |resource|
90
- if resource[:type].value == "file"
91
+ if resource[:type].value == "file" or resource[:type].value == "concat"
91
92
  resource[:param_tokens].select { |param_token|
92
93
  param_token.value == 'mode' &&
93
94
  TOKEN_TYPES.include?(param_token.next_code_token.next_code_token.type)
@@ -120,7 +121,7 @@ PuppetLint.new_check(:file_mode) do
120
121
 
121
122
  def check
122
123
  resource_indexes.each do |resource|
123
- if resource[:type].value == "file"
124
+ if resource[:type].value == "file" or resource[:type].value == "concat"
124
125
  resource[:param_tokens].select { |param_token|
125
126
  param_token.value == 'mode'
126
127
  }.each do |param_token|
@@ -184,7 +185,7 @@ PuppetLint.new_check(:ensure_not_symlink_target) do
184
185
  PuppetLint::Lexer::Token.new(:NEWLINE, "\n", 0, 0),
185
186
  PuppetLint::Lexer::Token.new(:INDENT, problem[:param_token].prev_token.value.dup, 0, 0),
186
187
  PuppetLint::Lexer::Token.new(:NAME, 'target', 0, 0),
187
- PuppetLint::Lexer::Token.new(:WHITESPACE, problem[:param_token].next_token.value.dup, 0, 0),
188
+ PuppetLint::Lexer::Token.new(:WHITESPACE, ' ', 0, 0),
188
189
  PuppetLint::Lexer::Token.new(:FARROW, '=>', 0, 0),
189
190
  PuppetLint::Lexer::Token.new(:WHITESPACE, ' ', 0, 0),
190
191
  ].reverse.each do |new_token|
@@ -1,13 +1,13 @@
1
1
  # Public: Test the manifest tokens for variables that contain a dash and
2
2
  # record a warning for each instance found.
3
3
  PuppetLint.new_check(:variable_contains_dash) do
4
- VARIABLE_TYPES = Set[:VARIABLE, :UNENC_VARIABLE]
4
+ VARIABLE_DASH_TYPES = Set[:VARIABLE, :UNENC_VARIABLE]
5
5
 
6
6
  def check
7
7
  tokens.select { |r|
8
- VARIABLE_TYPES.include? r.type
8
+ VARIABLE_DASH_TYPES.include? r.type
9
9
  }.each do |token|
10
- if token.value.gsub(/\[.+?\]/, '').match(/-/)
10
+ if not token.next_token.nil? and Set[:DQMID, :DQPOST, :MINUS].include? token.next_token.type and token.next_token.value.start_with? '-'
11
11
  notify :warning, {
12
12
  :message => 'variable contains a dash',
13
13
  :line => token.line,
@@ -17,3 +17,21 @@ PuppetLint.new_check(:variable_contains_dash) do
17
17
  end
18
18
  end
19
19
  end
20
+
21
+ PuppetLint.new_check(:variable_is_lowercase) do
22
+ VARIABLE_LOWERCASE_TYPES = Set[:VARIABLE, :UNENC_VARIABLE]
23
+
24
+ def check
25
+ tokens.select { |r|
26
+ VARIABLE_LOWERCASE_TYPES.include? r.type
27
+ }.each do |token|
28
+ if token.value.gsub(/\[.+?\]/, '').match(/[A-Z]/)
29
+ notify :warning, {
30
+ :message => 'variable contains an uppercase letter',
31
+ :line => token.line,
32
+ :column => token.column,
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -68,6 +68,26 @@ PuppetLint.new_check(:'140chars') do
68
68
  end
69
69
  end
70
70
 
71
+ # Public: Test the raw manifest string for lines containing more than 80
72
+ # characters. This is DISABLED by default and behaves like the default
73
+ # 140chars check by excepting URLs and template() calls.
74
+ PuppetLint.new_check(:'80chars') do
75
+ def check
76
+ manifest_lines.each_with_index do |line, idx|
77
+ unless line =~ /:\/\// || line =~ /template\(/
78
+ if line.scan(/./mu).size > 80
79
+ notify :warning, {
80
+ :message => 'line has more than 80 characters',
81
+ :line => idx + 1,
82
+ :column => 80,
83
+ }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ PuppetLint.configuration.send("disable_80chars")
90
+
71
91
  # Public: Check the manifest tokens for any indentation not using 2 space soft
72
92
  # tabs and record an error for each instance found.
73
93
  PuppetLint.new_check(:'2sp_soft_tabs') do
@@ -128,7 +148,7 @@ PuppetLint.new_check(:arrow_alignment) do
128
148
  unless arrow_tok.column == indent_depth[indent_depth_idx] || level_tokens[indent_depth_idx].size == 1
129
149
  arrows_on_line = level_tokens[indent_depth_idx].select { |t| t.line == arrow_tok.line }
130
150
  notify :warning, {
131
- :message => 'indentation of => is not properly aligned',
151
+ :message => "indentation of => is not properly aligned (expected in column #{indent_depth[indent_depth_idx]}, but found it in column #{arrow_tok.column})",
132
152
  :line => arrow_tok.line,
133
153
  :column => arrow_tok.column,
134
154
  :token => arrow_tok,
@@ -78,6 +78,10 @@ class PuppetLint
78
78
  linter.file = puppet_file
79
79
  linter.run
80
80
  linter.print_problems
81
+
82
+ if PuppetLint.configuration.fix && !linter.problems.any? { |e| e[:check] == :syntax }
83
+ IO.write(puppet_file, linter.manifest)
84
+ end
81
85
  end
82
86
  abort if linter.errors? || (
83
87
  linter.warnings? && PuppetLint.configuration.fail_on_warnings
@@ -1,3 +1,3 @@
1
1
  class PuppetLint
2
- VERSION = '2.0.0'
2
+ VERSION = '2.0.1'
3
3
  end
@@ -14,10 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
15
  s.require_paths = ["lib"]
16
16
 
17
- s.add_development_dependency 'rake', '~> 10.0'
18
- s.add_development_dependency 'rspec', '~> 3.0'
19
- s.add_development_dependency 'rspec-its', '~> 1.0'
20
- s.add_development_dependency 'rspec-collection_matchers', '~> 1.0'
17
+ s.add_development_dependency 'github_changelog_generator'
21
18
 
22
19
  s.authors = ['Tim Sharpe']
23
20
  s.email = 'tim@sharpe.id.au'
@@ -99,7 +99,7 @@ describe PuppetLint::Bin do
99
99
 
100
100
  context 'when specifying a specific check to run' do
101
101
  let(:args) { [
102
- '--only-check', 'parameter_order',
102
+ '--only-checks', 'parameter_order',
103
103
  'spec/fixtures/test/manifests/warning.pp',
104
104
  'spec/fixtures/test/manifests/fail.pp',
105
105
  ] }
@@ -579,6 +579,32 @@ describe PuppetLint::Lexer do
579
579
  end
580
580
  end
581
581
 
582
+ context ':TYPE' do
583
+ it 'should match Data Types' do
584
+ token = @lexer.tokenise('Integer').first
585
+ expect(token.type).to eq(:TYPE)
586
+ expect(token.value).to eq('Integer')
587
+ end
588
+
589
+ it 'should match Catalog Types' do
590
+ token = @lexer.tokenise('Resource').first
591
+ expect(token.type).to eq(:TYPE)
592
+ expect(token.value).to eq('Resource')
593
+ end
594
+
595
+ it 'should match Abstract Types' do
596
+ token = @lexer.tokenise('Collection').first
597
+ expect(token.type).to eq(:TYPE)
598
+ expect(token.value).to eq('Collection')
599
+ end
600
+
601
+ it 'should match Platform Types' do
602
+ token = @lexer.tokenise('Callable').first
603
+ expect(token.type).to eq(:TYPE)
604
+ expect(token.value).to eq('Callable')
605
+ end
606
+ end
607
+
582
608
  context ':CLASSREF' do
583
609
  it 'should match single capitalised alphanumeric term' do
584
610
  token = @lexer.tokenise('One').first
@@ -767,6 +793,18 @@ describe PuppetLint::Lexer do
767
793
  expect(token.value).to eq('this is \\/ a regex')
768
794
  end
769
795
 
796
+ it 'should be allowed as a param to a data type' do
797
+ tokens = @lexer.tokenise('Foo[/bar/]')
798
+ expect(tokens[2].type).to eq(:REGEX)
799
+ expect(tokens[2].value).to eq('bar')
800
+ end
801
+
802
+ it 'should be allowed as a param to an optional data type' do
803
+ tokens = @lexer.tokenise('Optional[Regexp[/^puppet/]]')
804
+ expect(tokens[4].type).to eq(:REGEX)
805
+ expect(tokens[4].value).to eq('^puppet')
806
+ end
807
+
770
808
  it 'should not match chained division' do
771
809
  tokens = @lexer.tokenise('$x = $a/$b/$c')
772
810
  expect(tokens.select { |r| r.type == :REGEX }).to be_empty
@@ -3,71 +3,75 @@ require 'spec_helper'
3
3
  describe 'parameter_order' do
4
4
  let(:msg) { 'optional parameter listed before required parameter' }
5
5
 
6
- context 'define with attrs in order' do
7
- let(:code) { "define foo($bar, $baz='gronk') { }" }
6
+ ['define', 'class'].each do |type|
7
+ context "#{type} with attrs in order" do
8
+ let(:code) { "#{type} foo($bar, $baz='gronk') { }" }
8
9
 
9
- it 'should not detect any problems' do
10
- expect(problems).to have(0).problems
10
+ it 'should not detect any problems' do
11
+ expect(problems).to have(0).problems
12
+ end
11
13
  end
12
- end
13
14
 
14
- context 'define with parameter that calls a function' do
15
- let(:code) { "define foo($bar=extlookup($name)) {}" }
15
+ context "#{type} with parameter that calls a function" do
16
+ let(:code) { "#{type} foo($bar=extlookup($name)) {}" }
16
17
 
17
- it 'should not detect any problems' do
18
- expect(problems).to have(0).problems
18
+ it 'should not detect any problems' do
19
+ expect(problems).to have(0).problems
20
+ end
19
21
  end
20
- end
21
22
 
22
- context 'define with attrs out of order' do
23
- let(:code) { "define foo($bar='baz', $gronk) { }" }
23
+ context "#{type} with attrs out of order" do
24
+ let(:code) { "#{type} foo($bar='baz', $gronk) { }" }
24
25
 
25
- it 'should only detect a single problem' do
26
- expect(problems).to have(1).problem
27
- end
26
+ it 'should only detect a single problem' do
27
+ expect(problems).to have(1).problem
28
+ end
28
29
 
29
- it 'should create a warning' do
30
- expect(problems).to contain_warning(msg).on_line(1).in_column(24)
30
+ col = (type == "class" ? 23 : 24)
31
+ it 'should create a warning' do
32
+ expect(problems).to contain_warning(msg).on_line(1).in_column(col)
33
+ end
31
34
  end
32
- end
33
35
 
34
- context 'class/define parameter set to another variable' do
35
- let(:code) { "
36
- define foo($bar, $baz = $name, $gronk=$::fqdn) {
37
- }"
38
- }
36
+ context "#{type} parameter set to another variable" do
37
+ let(:code) { "
38
+ #{type} foo($bar, $baz = $name, $gronk=$::fqdn) {
39
+ }"
40
+ }
39
41
 
40
- it 'should not detect any problems' do
41
- expect(problems).to have(0).problems
42
+ it 'should not detect any problems' do
43
+ expect(problems).to have(0).problems
44
+ end
42
45
  end
43
- end
44
46
 
45
- context 'class/define parameter set to another variable with incorrect order' do
46
- let(:code) { "
47
- define foo($baz = $name, $bar, $gronk=$::fqdn) {
48
- }"
49
- }
47
+ context "#{type} parameter set to another variable with incorrect order" do
48
+ let(:code) { "
49
+ #{type} foo($baz = $name, $bar, $gronk=$::fqdn) {
50
+ }"
51
+ }
50
52
 
51
- it 'should only detect a single problem' do
52
- expect(problems).to have(1).problem
53
- end
53
+ it 'should only detect a single problem' do
54
+ expect(problems).to have(1).problem
55
+ end
54
56
 
55
- it 'should create a warning' do
56
- expect(problems).to contain_warning(msg).on_line(2).in_column(32)
57
+ col = (type == "class" ? 33 : 34)
58
+ it 'should create a warning' do
59
+ expect(problems).to contain_warning(msg).on_line(2).in_column(col)
60
+ end
57
61
  end
58
- end
59
62
 
60
- context 'issue-101' do
61
- let(:code) { "
62
- define b (
63
- $foo,
64
- $bar='',
65
- $baz={}
66
- ) { }
67
- " }
63
+ context 'issue-101' do
64
+ let(:code) { "
65
+ #{type} b (
66
+ $foo,
67
+ $bar='',
68
+ $baz={}
69
+ ) { }
70
+ " }
68
71
 
69
- it 'should not detect any problems' do
70
- expect(problems).to have(0).problems
72
+ it 'should not detect any problems' do
73
+ expect(problems).to have(0).problems
74
+ end
71
75
  end
72
76
  end
73
77
  end