Class: Goodcheck::Commands::Test

Inherits:
Object
  • Object
show all
Includes:
ConfigLoading, ExitStatus, HomePath
Defined in:
lib/goodcheck/commands/test.rb

Constant Summary

Constants included from ExitStatus

ExitStatus::EXIT_ERROR, ExitStatus::EXIT_MATCH, ExitStatus::EXIT_SUCCESS, ExitStatus::EXIT_TEST_FAILED

Instance Attribute Summary collapse

Attributes included from ConfigLoading

#config

Instance Method Summary collapse

Methods included from HomePath

#cache_dir_path

Methods included from ConfigLoading

#handle_config_errors, #load_config!

Constructor Details

#initialize(stdout:, stderr:, config_path:, force_download:, home_path:) ⇒ Test

Returns a new instance of Test.



14
15
16
17
18
19
20
# File 'lib/goodcheck/commands/test.rb', line 14

def initialize(stdout:, stderr:, config_path:, force_download:, home_path:)
  @stdout = stdout
  @stderr = stderr
  @config_path = config_path
  @force_download = force_download
  @home_path = home_path
end

Instance Attribute Details

#config_pathObject (readonly)

Returns the value of attribute config_path.



10
11
12
# File 'lib/goodcheck/commands/test.rb', line 10

def config_path
  @config_path
end

#force_downloadObject (readonly)

Returns the value of attribute force_download.



12
13
14
# File 'lib/goodcheck/commands/test.rb', line 12

def force_download
  @force_download
end

#home_pathObject (readonly)

Returns the value of attribute home_path.



11
12
13
# File 'lib/goodcheck/commands/test.rb', line 11

def home_path
  @home_path
end

#stderrObject (readonly)

Returns the value of attribute stderr.



9
10
11
# File 'lib/goodcheck/commands/test.rb', line 9

def stderr
  @stderr
end

#stdoutObject (readonly)

Returns the value of attribute stdout.



8
9
10
# File 'lib/goodcheck/commands/test.rb', line 8

def stdout
  @stdout
end

Instance Method Details

#rule_matches_example?(rule, trigger, example) ⇒ Boolean

Returns:

  • (Boolean)


150
151
152
153
154
# File 'lib/goodcheck/commands/test.rb', line 150

def rule_matches_example?(rule, trigger, example)
  buffer = Buffer.new(path: Pathname("-"), content: example)
  analyzer = Analyzer.new(rule: rule, buffer: buffer, trigger: trigger)
  analyzer.scan.count > 0
end

#runObject



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/goodcheck/commands/test.rb', line 22

def run
  handle_config_errors stderr do
    load_config!(cache_path: cache_dir_path, force_download: force_download)

    if config.rules.empty?
      stdout.puts "No rules."
      return EXIT_SUCCESS
    end

    validate_rule_uniqueness or return EXIT_TEST_FAILED
    validate_rules or return EXIT_TEST_FAILED

    EXIT_SUCCESS
  end
end

#validate_rule_uniquenessObject



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/goodcheck/commands/test.rb', line 38

def validate_rule_uniqueness
  stdout.puts "Validating rule ID uniqueness..."

  duplicated_ids = []

  config.rules.group_by(&:id).each do |id, rules|
    if rules.size > 1
      duplicated_ids << id
    end
  end

  if duplicated_ids.empty?
    stdout.puts Rainbow("  OK! πŸ‘").green
    true
  else
    count = duplicated_ids.size
    duplication = count == 1 ? 'duplication' : 'duplications'
    stdout.puts "  Found #{Rainbow(count).bold} #{duplication}. 😱"
    duplicated_ids.each do |id|
      stdout.puts "    - #{Rainbow(id).background(:red)}"
    end
    false
  end
end

#validate_rulesObject



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/goodcheck/commands/test.rb', line 63

def validate_rules
  success_count = 0
  failed_rule_ids = Set[]

  config.rules.each do |rule|
    stdout.puts "Testing rule #{Rainbow(rule.id).cyan}..."

    rule_ok = true

    if rule.triggers.any? {|trigger| !trigger.passes.empty? || !trigger.fails.empty?}
      rule.triggers.each.with_index do |trigger, index|
        if !trigger.passes.empty? || !trigger.fails.empty?
          if trigger.by_pattern?
            stdout.puts "  Testing pattern..."
          else
            stdout.puts "  #{index + 1}. Testing trigger..."
          end

          pass_errors = trigger.passes.each.with_index.select do |pass, _|
            rule_matches_example?(rule, trigger, pass)
          end

          fail_errors = trigger.fails.each.with_index.reject do |fail, _|
            rule_matches_example?(rule, trigger, fail)
          end

          unless pass_errors.empty?
            rule_ok = false

            pass_errors.each do |_, index|
              stdout.puts "    #{index + 1}. #{Rainbow('pass').green} example matched. 😱"
              failed_rule_ids << rule.id
            end
          end

          unless fail_errors.empty?
            rule_ok = false

            fail_errors.each do |_, index|
              stdout.puts "    #{index + 1}. #{Rainbow('fail').red} example didn’t match. 😱"
              failed_rule_ids << rule.id
            end
          end
        end
      end

      if rule.triggers.any?(&:skips_fail_examples?)
        stdout.puts "    The rule contains a `pattern` with `glob`, which is not supported by the test command. 🚨"
        stdout.puts "    Skips testing `fail` examples."
      end
    end

    if rule.severity && !config.severity_allowed?(rule.severity)
      allowed_severities = config.allowed_severities.map { |s| %("#{s}") }.join(', ')
      stdout.puts Rainbow("  \"#{rule.severity}\" severity isn’t allowed. Must be one of #{allowed_severities}. 😱").red
      rule_ok = false
      failed_rule_ids << rule.id
    end

    if !rule.severity && config.severity_required?
      stdout.puts Rainbow("  Severity is required. 😱").red
      rule_ok = false
      failed_rule_ids << rule.id
    end

    if rule_ok
      stdout.puts Rainbow("  OK! πŸ‘").green
      success_count += 1
    end
  end

  unless failed_rule_ids.empty?
    stdout.puts ""
    stdout.puts "Failed rules:"
    failed_rule_ids.each do |rule_id|
      stdout.puts "  - #{Rainbow(rule_id).background(:red)}"
    end
  end

  total = success_count + failed_rule_ids.size
  stdout.puts ""
  stdout.puts "#{Rainbow(total).bold} #{total == 1 ? 'rule' : 'rules'} tested: " \
              "#{Rainbow(success_count.to_s + ' successful').green.bold}, #{Rainbow(failed_rule_ids.size.to_s + ' failed').red.bold}"

  failed_rule_ids.empty?
end