summaryrefslogtreecommitdiff
path: root/lib/chef/audit/chef_json_formatter.rb
blob: 3c164120b403dafbe995cc89c7ede2c9c22efd8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
RSpec::Support.require_rspec_core "formatters/base_formatter"
require 'control_group_data'
require 'ffi_yajl'

class Chef
  class Audit
    class ChefJsonFormatter < ::RSpec::Core::Formatters::BaseFormatter
      ::RSpec::Core::Formatters.register self, :example_group_started, :message, :stop, :close, :example_failed

      attr_reader :control_group_data

      # TODO hopefully the runner can take care of this for us since there won't be an outer-most
      # control group
      @@outer_example_group_found = false

      def initialize(output)
        super
      end

      # Invoked for each `control`, `describe`, `context` block
      def example_group_started(notification)
        unless @@outer_example_group_found
          @control_group_data = ControlGroupData.new(notification.group.description)
          @@outer_example_group_found = true
        end
      end

      def example_failed(notification)
        e = notification.example.metadata[:execution_result].exception
        raise e unless e.kind_of? ::RSpec::Expectations::ExpectationNotMetError
      end

      def message(notification)
        puts "message: #{notification}"
      end

      def stop(notification)
        notification.examples.each do |example|
          control_data = build_control_from(example)
          e = example.exception
          if e
            control = control_group_data.example_failure(e.message, control_data)
          else
            control = control_group_data.example_success(control_data)
          end
          control.line_number = example.metadata[:line_number]
        end
      end

      def close(notification)
        output.write FFI_Yajl::Encoder.encode(control_group_data.to_hash, pretty: true)
        output.close if IO === output && output != $stdout
      end

      private

      def build_control_from(example)
        described_class = example.metadata[:described_class]
        if described_class
          resource_type = described_class.class.name.split(':')[-1]
          # TODO submit github PR to expose this
          resource_name = described_class.instance_variable_get(:@name)
        end

        describe_groups = []
        group = example.metadata[:example_group]
        # If the innermost block has a resource instead of a string, don't include it in context
        describe_groups.unshift(group[:description]) if described_class.nil?
        group = group[:parent_example_group]
        while !group.nil?
          describe_groups.unshift(group[:description])
          group = group[:parent_example_group]
        end
        # TODO remove this when we're no longer wrapping everything with "mysql audit"
        describe_groups.shift

        {
            :name => example.description,
            :desc => example.full_description,
            :resource_type => resource_type,
            :resource_name => resource_name,
            :context => describe_groups
        }
      end

    end
  end
end