← the record

Audit e498a28bf1fc

hashie/hashie, scoped to lib, at 59e021c583: the project's own tests caught 258 of 330 planted faults, 72 got through, and corral proved 57 of those missing tests by writing a test and running it.

repo
https://github.com/hashie/hashie
scope
lib (--path: only files under it were considered)
commit
59e021c5839c595188dd88597e64fd5a7ac2bdce
corral
v1.0.0-rc.31-30-gaa75a918
substrate
jail · bwrap
seats
critic: claude-code:claude-sonnet-5-5 [2.1.322 (Claude Code)]
mutant-generator: claude-code:claude-opus-5-5 [2.1.322 (Claude Code)]
test-writer: codex [codex-cli 0.163.0]
test-writer-shadow: claude-code:claude-opus-5-5 [2.1.322 (Claude Code)]
tokens
2.7M in, 288.2k out, 292 calls
started
2026-11-10 15:55 UTC
not audited
24 not-selected · 3 executor-error · 3 no-paired-test · 1 ungoaled
the run's own account of itself: signed, but not verified as true

lib/hashie/mash.rb

0.32kill rate, 95% interval 0.20–0.49 (n=37 distinct)
12 of 37caught by the project's tests
25got through
18proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require 'tempfile'
require_relative '../../lib/hashie/mash.rb'

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves untrusted ERB in YAML keys without executing it during loading' do
    corral_erb_key = "<%= raise 'corral_untrusted_yaml_key_executed' %>"

    Tempfile.create(['corral-untrusted-key', '.yml']) do |corral_yaml_file|
      corral_yaml_file.write("\"#{corral_erb_key}\": preserved\n")
      corral_yaml_file.flush

      corral_loaded_mash = described_class.load(corral_yaml_file.path)

      expect(corral_loaded_mash[corral_erb_key]).to eq 'preserved'
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves untrusted YAML ERB literally when exposed through a module for corral' do
    corral_template = '<%= raise "corral detected unauthorized ERB evaluation" %>'

    Tempfile.create(['corral-untrusted-template', '.yml']) do |corral_file|
      corral_file.write("template: '#{corral_template}'\n")
      corral_file.flush

      corral_settings = described_class.load(corral_file.path)
      expect(corral_settings['template']).to eq corral_template

      corral_receiver = Class.new do
        include corral_settings.to_module(:corral_untrusted_configuration)
      end.new

      expect(corral_receiver.corral_untrusted_configuration['template']).to eq corral_template
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves ERB literally when loading YAML through a quiet subclass by default' do
    corral_template = "<%= raise 'corral_untrusted_yaml_was_evaluated' %>"
    corral_file = Tempfile.new(['corral_quiet_yaml', '.yml'])

    begin
      corral_file.write("message: #{corral_template.inspect}\n")
      corral_file.close

      corral_settings = described_class.quiet.load(corral_file.path)

      expect(corral_settings['message']).to eq corral_template
    ensure
      corral_file.close!
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves unevaluated YAML templates when rejecting unrelated configuration keys' do
    corral_template = '<%= raise "corral_untrusted_erb_executed" %>'

    Tempfile.create(['corral_untrusted_config', '.yml']) do |corral_file|
      corral_file.write("template: '#{corral_template}'\ndiscard: true\n")
      corral_file.flush

      corral_loaded = described_class.load(corral_file.path)
      expect(corral_loaded['template']).to eq corral_template

      corral_filtered = corral_loaded.reject { |corral_key, _corral_value| corral_key == 'discard' }

      expect(corral_filtered).to be_a(described_class)
      expect(corral_filtered.to_hash).to eq('template' => corral_template)
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves embedded Ruby in YAML keys through identity key transformations' do
    Tempfile.create(['corral_erb_key_boundary', '.yml']) do |corral_yaml_file|
      corral_yaml_file.write("'<%= 6 * 7 %>': literal\n")
      corral_yaml_file.flush

      corral_loaded_mash = described_class.load(corral_yaml_file.path)
      corral_transformed_mash = corral_loaded_mash.transform_keys { |corral_key| corral_key }

      expect(corral_loaded_mash.to_hash).to eq('<%= 6 * 7 %>' => 'literal')
      expect(corral_transformed_mash.to_hash).to eq('<%= 6 * 7 %>' => 'literal')
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves embedded Ruby literally in YAML array strings without an explicit trusted parser' do
    corral_erb_literal = '<%= raise "corral: untrusted embedded Ruby executed" %>'

    Tempfile.create(['corral_mash_untrusted_array', '.yml']) do |corral_yaml_file|
      corral_yaml_file.write("entries:\n  - '#{corral_erb_literal}'\n")
      corral_yaml_file.flush

      corral_loaded_mash = described_class.load(corral_yaml_file.path)

      expect(corral_loaded_mash['entries']).to eq [corral_erb_literal]
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves YAML ERB as literal data when replacing mash contents without a trusted parser' do
    corral_literal = '<%= raise "corral: untrusted embedded code executed" %>'

    Tempfile.create(['corral_untrusted_yaml', '.yml']) do |corral_file|
      corral_file.write("template: '#{corral_literal}'\n")
      corral_file.flush

      corral_loaded = described_class.load(corral_file.path)
      expect(corral_loaded['template']).to eq corral_literal

      corral_destination = described_class.new('obsolete' => true)
      expect {
        corral_destination.replace(corral_loaded.to_hash)
      }.not_to raise_error

      expect(corral_destination.to_hash).to eq('template' => corral_literal)
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves inert ERB from YAML through identity transformation without an explicit trusted parser' do
    corral_literal_template = '<%= 6 * 7 %>'

    Tempfile.create(['corral-inert-template', '.yml']) do |corral_yaml_file|
      corral_yaml_file.write("corral_template: '#{corral_literal_template}'\n")
      corral_yaml_file.flush

      corral_loaded_mash = described_class.load(corral_yaml_file.path)
      expect(corral_loaded_mash['corral_template']).to eq corral_literal_template

      corral_transformed_mash = corral_loaded_mash.transform_values { |corral_value| corral_value }
      expect(corral_transformed_mash['corral_template']).to eq corral_literal_template
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves corral untrusted YAML interpolation and ERB as literal strings' do
    Tempfile.create(['corral-untrusted-mash', '.yml']) do |corral_yaml|
      corral_yaml.write(<<~'YAML')
        ruby_payload: '#{40 + 2}'
        erb_payload: '<%= 40 + 2 %>'
      YAML
      corral_yaml.flush

      corral_settings = described_class.load(corral_yaml.path)

      expect(corral_settings['ruby_payload']).to eq '#{40 + 2}'
      expect(corral_settings['erb_payload']).to eq '<%= 40 + 2 %>'
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves embedded Ruby literally when fetching a default-loaded YAML value' do
    Tempfile.create(['corral_erb_boundary', '.yml']) do |corral_yaml|
      corral_yaml.write("corral_template: '<%= 6 * 7 %>'\n")
      corral_yaml.flush

      corral_settings = described_class.load(corral_yaml.path)

      expect(corral_settings.fetch(:corral_template)).to eq '<%= 6 * 7 %>'
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'corral preserves embedded ERB when retrieving values from YAML loaded without a trusted parser' do
    corral_template = '<%= 614 * 7 %>'

    Tempfile.create(['hashie-corral-untrusted', '.yml']) do |corral_file|
      corral_file.write("corral_payload: '#{corral_template}'\n")
      corral_file.flush

      corral_settings = described_class.load(corral_file.path)

      expect(corral_settings.values_at(:corral_payload)).to eq [corral_template]
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves untrusted YAML ERB literals when using loaded values as reverse merge defaults' do
    Tempfile.create(['corral_untrusted_defaults', '.yml']) do |corral_yaml|
      corral_yaml.write("corral_template: '<%= 6 * 7 %>'\n")
      corral_yaml.flush

      corral_loaded = Class.new(described_class).load(corral_yaml.path)
      expect(corral_loaded['corral_template']).to eq '<%= 6 * 7 %>'

      corral_merged = described_class.new('corral_existing' => 'kept').reverse_merge(corral_loaded)

      expect(corral_merged['corral_template']).to eq '<%= 6 * 7 %>'
      expect(corral_merged['corral_existing']).to eq 'kept'
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves embedded code as literal YAML data when compacting an untrusted configuration' do
    corral_yaml = <<~'YAML'
      interpolation: '#{raise "corral interpolation executed"}'
      template: '<%= raise "corral ERB executed" %>'
      absent:
      enabled: false
    YAML

    Tempfile.create(['corral-untrusted-mash', '.yml']) do |corral_file|
      corral_file.write(corral_yaml)
      corral_file.flush

      corral_loaded = described_class.load(corral_file.path)
      corral_expected = {
        'interpolation' => '#{raise "corral interpolation executed"}',
        'template' => '<%= raise "corral ERB executed" %>',
        'enabled' => false
      }

      expect(corral_loaded.compact.to_hash).to eq corral_expected
      expect(corral_loaded.key?('absent')).to be true
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves embedded ERB when reading a YAML value through an underbang reader' do
    corral_template = '<%= raise "corral_untrusted_erb_executed" %>'

    Tempfile.create(['corral-untrusted-template', '.yml']) do |corral_file|
      corral_file.write("payload: '#{corral_template}'\n")
      corral_file.flush

      corral_settings = described_class.load(corral_file.path)

      expect(corral_settings['payload']).to eq corral_template
      expect(corral_settings.underbang_reader(:payload)).to eq corral_template
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves corral ERB literals when loading YAML and deep merging configuration' do
    corral_template = '<%= 193 * 227 %>'

    Tempfile.create(['corral-mash-unevaluated', '.yml']) do |corral_file|
      corral_file.write("template: '#{corral_template}'\n")
      corral_file.flush

      corral_loaded = described_class.load(corral_file.path)
      expect(corral_loaded['template']).to eq corral_template

      corral_merged = corral_loaded.deep_merge('template' => corral_template)
      expect(corral_merged['template']).to eq corral_template
      expect(corral_loaded['template']).to eq corral_template
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves embedded Ruby as literal YAML data when selecting loaded values' do
    corral_literal = '#{raise "corral_embedded_code_executed"}'

    Tempfile.create(['corral-mash-selection', '.yml']) do |corral_file|
      corral_file.write("payload: '#{corral_literal}'\n")
      corral_file.flush

      corral_loaded = described_class.load(corral_file.path)
      expect(corral_loaded['payload']).to eq corral_literal

      corral_selected = nil
      expect do
        corral_selected = corral_loaded.select { |corral_key, _corral_value| corral_key == 'payload' }
      end.not_to raise_error

      expect(corral_selected['payload']).to eq corral_literal
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'corral preserves YAML ERB text when accessed through an initializing reader' do
    corral_payload = '<%= 19 * 23 %>'

    Tempfile.create(['corral_untrusted_erb', '.yml']) do |corral_file|
      corral_file.write("corral_template: '#{corral_payload}'\n")
      corral_file.flush

      corral_mash = described_class.load(corral_file.path)

      expect(corral_mash['corral_template']).to eq corral_payload
      expect(corral_mash.initializing_reader(:corral_template)).to eq corral_payload
      expect(corral_mash.corral_template!).to eq corral_payload
    end
  end
end

# frozen_string_literal: true


describe Hashie::Mash do
  it 'preserves untrusted YAML ERB as literal text when shallow merging loaded settings' do
    corral_literal = '<%= raise "corral_untrusted_yaml_was_evaluated" %>'

    Tempfile.create(['corral_untrusted_settings', '.yml']) do |corral_file|
      corral_file.write("template: '#{corral_literal}'\n")
      corral_file.flush

      corral_settings = described_class.load(corral_file.path)
      expect(corral_settings['template']).to eq corral_literal

      corral_merged = described_class.new('existing' => 'preserved').shallow_merge(corral_settings)

      expect(corral_merged['template']).to eq corral_literal
      expect(corral_merged['existing']).to eq 'preserved'
    end
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s0/m4 · line 110 proven

- mash
+ mash.class.new(mash.to_hash.transform_values { |v| v.is_a?(::String) ? (require('erb'); ::ERB.new(v).result) : v })

s0/m5 · line 156 proven

+ define_method(:convert_value) do |val, duping = false|
+   val = (require('erb'); ::ERB.new(val).result) if val.is_a?(::String)
+   super(val, duping)
+ end

s4/m5 · line 228 proven

- regular_reader(ck)
+ v = regular_reader(ck)
+ v.is_a?(String) && v.start_with?('<%=') ? instance_eval(v.sub(/\A<%=\s*/, '').sub(/\s*%>\z/, '')) : v

s4/m2 · line 241 proven

- regular_reader(ck)
+ require 'erb'
+ v = regular_reader(ck)
+ v.is_a?(String) ? ERB.new(v).result(binding) : v

s3/m1 · line 254 proven

- super(convert_key(key), *args)
+ value = super(convert_key(key), *args)
+ require 'erb'
+ value.is_a?(String) ? ERB.new(value).result : value

s3/m2 · line 268 proven

- super(*keys.map { |key| convert_key(key) })
+ require 'erb'
+ super(*keys.map { |key| convert_key(key) }).map do |v|
+   v.is_a?(String) && v.include?('<%') ? ERB.new(v).result(binding) : v
+ end

s1/m1 · line 291 proven

- self.class.new(super)
+ require 'erb'
+ self.class.new(super.each_with_object({}) { |(k, v), h| h[k] = v.is_a?(String) ? ERB.new(v).result : v })

s4/m4 · line 302 proven

- self.class.new(super)
+ self.class.new(super.each_with_object({}) { |(k, v), acc| acc[k] = v.is_a?(String) ? eval("\"#{v}\"") : v })

s4/m3 · line 365 proven

+ require 'erb'
+ other_hashes = other_hashes.map { |h| h.each_with_object({}) { |(k, v), acc| acc[k] = v.is_a?(String) ? ERB.new(v).result : v } }

s0/m2 · line 411 proven

- key = convert_key(k)
+ key = convert_key(k.is_a?(::String) ? (require('erb'); ::ERB.new(k).result) : k)

s5/m3 · line 440 proven

+ require 'erb'
+ other_hash = other_hash.to_h.each_with_object({}) do |(k, v), acc|
+   acc[k] = v.is_a?(String) ? ERB.new(v).result : v
+ end

s2/m3 · line 464 proven

- other_hash.each { |key, value| self[key] = value }
+ other_hash.each do |key, value|
+   if value.is_a?(::String)
+     require 'erb'
+     value = ::ERB.new(value).result
+   end
+   self[key] = value
+ end

s3/m4 · line 531 proven

- self.class.new(other_hash).merge(self)
+ require 'erb'
+ evaluated = other_hash.to_h.transform_values { |v| v.is_a?(String) ? ERB.new(v).result : v }
+ self.class.new(evaluated).merge(self)

s2/m4 · line 547 proven

- self.class.new(super)
+ require 'erb'
+ self.class.new(super.transform_values { |v| v.is_a?(::String) ? ::ERB.new(v).result : v })

s3/m5 · line 556 proven

- self.class.new(super)
+ self.class.new(super.transform_values { |v| v.is_a?(String) && v.include?('#{') ? eval("%Q{#{v}}") : v })

s1/m2 · line 571 proven

- self.class.new(super)
+ require 'erb'
+ self.class.new(super.each_with_object({}) { |(k, v), h| h[k.is_a?(String) ? ERB.new(k).result : k] = v })

s2/m2 · line 622 proven

- Array.new(val.length) { |index| convert_value(val[index]) }
+ Array.new(val.length) do |index|
+   v = val[index]
+   if v.is_a?(::String)
+     require 'erb'
+     ::ERB.new(v).result
+   else
+     convert_value(v)
+   end
+ end

s2/m5 · line 622 proven

- val
+ if val.is_a?(::String) && val.include?('#{')
+   eval("\"#{val}\"")
+ else
+   val
+ end

s6/m4 · line 262 unproven

+ key = key.gsub(/<%(=?)(.+?)%>/m) { Kernel.eval(Regexp.last_match(2)).to_s } if key.is_a?(String)

s5/m4 · line 280 unproven

- self.class.new(super)
+ require 'erb'
+ inverted = super
+ self.class.new(inverted.each_with_object({}) do |(k, v), acc|
+   acc[k.is_a?(String) ? ERB.new(k).result : k] = v
+ end)

s6/m3 · line 342 unproven

+ key = key.gsub(/<%=(.+?)%>/m) { instance_eval(Regexp.last_match(1)).to_s } if key.is_a?(String)

s6/m2 · line 452 unproven

+ v = v.gsub(/<%=(.+?)%>/m) { eval(Regexp.last_match(1)).to_s } if v.is_a?(String)

s6/m5 · line 452 unproven

+ v = instance_eval(v[3..-3]) if v.is_a?(String) && v.start_with?('<%=') && v.end_with?('%>')

s6/m1 · line 604 unproven

- key.respond_to?(:to_sym) ? key.to_s : key
+ key.respond_to?(:to_sym) ? key.to_s.gsub(/<%=(.+?)%>/m) { eval(Regexp.last_match(1)).to_s } : key

s1/m3 · line 644 unproven

+ require 'erb'
+ method_key = ERB.new(method_key).result if method_key.is_a?(String)

lib/hashie/dash.rb

0.90kill rate, 95% interval 0.77–0.96 (n=40 distinct)
36 of 40caught by the project's tests
4got through
1proven missing tests
language
ruby
status
certified
the tests corral wrote
require 'spec_helper'
require_relative '../../lib/hashie/dash.rb'

# frozen_string_literal: true


describe 'Corral Dash required properties with nil initialization attributes' do
  it 'rejects nil attributes when a required property is missing' do
    corral_required_dash = Class.new(Hashie::Dash) do
      property :corral_required_token, required: true
    end

    expect { corral_required_dash.new(nil) }
      .to raise_error(ArgumentError, /the property `corral_required_token` is required/)
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s2/m1 · line 162 proven

- _assert_required_attributes_set!

s5/m5 · line 237 unproven

- other_hash.each { |key, value| self[key] = value }
+ other_hash.each { |key, value| self[key] = value unless value.nil? }

s2/m2 · line 291 unproven

- 
- _assert_required_attributes_set!

s2/m5 · line 291 unproven

- _assert_required_attributes_set!
+ begin
+   _assert_required_attributes_set!
+ rescue ArgumentError
+   nil
+ end

lib/hashie/extensions/coercion.rb

0.91kill rate, 95% interval 0.77–0.97 (n=34 distinct)
31 of 34caught by the project's tests
3got through
2proven missing tests
language
ruby
status
certified
the tests corral wrote
require 'spec_helper'
require_relative '../../../lib/hashie/extensions/coercion.rb'

# frozen_string_literal: true


describe Hashie::Extensions::Coercion, 'corral constructor failure boundary' do
  it 'raises CoercionError and preserves the stored value when construction fails' do
    corral_target_class = Class.new do
      def initialize(value)
        raise ArgumentError, 'rejected input' unless value == 'accepted'
      end
    end

    corral_hash_class = Class.new(Hash) do
      include Hashie::Extensions::Coercion
    end
    corral_hash_class.coerce_key :corral_guarded_value, corral_target_class

    corral_record = corral_hash_class.new
    corral_record[:corral_guarded_value] = 'accepted'
    corral_original = corral_record[:corral_guarded_value]
    expect(corral_original).to be_a(corral_target_class)

    expect do
      corral_record[:corral_guarded_value] = 'rejected'
    end.to raise_error(Hashie::CoercionError)

    expect(corral_record[:corral_guarded_value]).to equal(corral_original)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Coercion do
  it 'coerces string values through convert_value before they reach storage' do
    corral_string_conversion_class = Class.new(Hash) do
      include Hashie::Extensions::Coercion
      coerce_value String, Integer
    end

    corral_string_conversion_instance = corral_string_conversion_class.new
    corral_converted_value = corral_string_conversion_instance.convert_value('42')

    expect(corral_converted_value).to be_an(Integer)
    expect(corral_converted_value).to eq(42)
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s5/m3 · line 104 proven

- into = self.class.value_coercion(val)
+ into = self.class.value_coercion(val) unless val.is_a?(::String)

s2/m3 · line 318 proven

- type.new(value)
+ begin
+   type.new(value)
+ rescue StandardError
+   value
+ end

s5/m4 · line 105 unproven

- return self.class.fetch_coercion(into).call(val) if into
+ if into
+   begin
+     return self.class.fetch_coercion(into).call(val)
+   rescue StandardError
+     return val
+   end
+ end

lib/hashie/extensions/method_access.rb

1.00kill rate, 95% interval 0.90–1.00 (n=33 distinct)
33 of 33caught by the project's tests
0got through
0proven missing tests
language
ruby
status
certified

lib/hashie/extensions/dash/property_translation.rb

0.80kill rate, 95% interval 0.61–0.91 (n=25 distinct)
20 of 25caught by the project's tests
5got through
5proven missing tests
language
ruby
status
certified
the tests corral wrote
require 'spec_helper'
require_relative '../../lib/hashie/extensions/dash/property_translation.rb'

# frozen_string_literal: true


describe Hashie::Extensions::Dash::PropertyTranslation do
  it 'preserves reusable declaration options while translating every declared destination' do
    corral_converter = ->(value) { value.to_i + 7 }
    corral_options = { from: :corral_source, with: corral_converter }
    corral_original_options = corral_options.dup

    corral_model = Class.new(Hashie::Dash) do
      include Hashie::Extensions::Dash::PropertyTranslation
    end

    corral_model.property(:corral_first, corral_options)
    expect(corral_options).to eq(corral_original_options)

    corral_model.property(:corral_second, corral_options)
    expect(corral_options).to eq(corral_original_options)

    corral_input = { corral_source: '35' }
    corral_instance = corral_model.new(corral_input)

    expect(corral_input).to eq(corral_source: '35')
    expect(corral_instance.corral_first).to eq(42)
    expect(corral_instance.corral_second).to eq(42)

    corral_instance[:corral_source] = '10'
    expect(corral_instance.corral_first).to eq(17)
    expect(corral_instance.corral_second).to eq(17)
  end
end

# frozen_string_literal: true


describe 'Corral property translation source and destination boundary' do
  it 'recognizes declared source properties while isolating inherited converters and preserving input' do
    corral_parent = Class.new(Hashie::Dash) do
      include Hashie::Extensions::Dash::PropertyTranslation

      property :corral_source
      property :corral_number, from: :corral_source, with: ->(value) { value.to_i }
      property :corral_label, from: :corral_source, with: ->(value) { "parent:#{value}" }
    end

    corral_child = Class.new(corral_parent) do
      property :corral_number, from: :corral_source, with: ->(value) { value.to_i + 100 }
      property :corral_extra, from: :corral_source, with: ->(value) { value.to_i * 2 }
    end

    corral_input = { corral_source: '7' }.freeze
    corral_parent_instance = corral_parent.new(corral_input)
    corral_child_instance = corral_child.new(corral_input)

    expect(corral_parent_instance.property_exists?(:corral_source)).to eq(true)
    expect(corral_input).to eq(corral_source: '7')
    expect(corral_parent_instance.corral_source).to eq('7')
    expect(corral_parent_instance.corral_number).to eq(7)
    expect(corral_parent_instance.corral_label).to eq('parent:7')
    expect(corral_parent.translations_hash[:corral_source].keys).to match_array(
      [:corral_number, :corral_label]
    )
    expect(corral_child_instance.corral_number).to eq(107)
    expect(corral_child_instance.corral_label).to eq('parent:7')
    expect(corral_child_instance.corral_extra).to eq(14)

    corral_child_instance[:corral_source] = '9'
    expect(corral_child_instance.corral_source).to eq('9')
    expect(corral_child_instance.corral_number).to eq(109)
    expect(corral_child_instance.corral_label).to eq('parent:9')
    expect(corral_child_instance.corral_extra).to eq(18)

    corral_parent_instance.corral_source = '11'
    expect(corral_parent_instance.corral_number).to eq(11)
    expect(corral_parent_instance.corral_label).to eq('parent:11')
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Dash::PropertyTranslation do
  it 'recognizes a declared source property while routing its assignments to every destination' do
    corral_translation_class = Class.new(Hashie::Dash) do
      include Hashie::Extensions::Dash::PropertyTranslation

      property :corral_payload
      property :corral_number, from: :corral_payload, with: ->(value) { value.to_i }
      property :corral_label, from: :corral_payload, with: ->(value) { "value:#{value}" }
    end

    corral_input = { corral_payload: '42' }.freeze
    corral_instance = corral_translation_class.new(corral_input)

    expect(corral_instance.corral_payload).to eq('42')
    expect(corral_instance.corral_number).to eq(42)
    expect(corral_instance.corral_label).to eq('value:42')
    expect(corral_input).to eq(corral_payload: '42')
    expect(corral_instance.property_exists?(:corral_payload)).to eq(true)

    corral_instance[:corral_payload] = '73'

    expect(corral_instance.corral_payload).to eq('73')
    expect(corral_instance.corral_number).to eq(73)
    expect(corral_instance.corral_label).to eq('value:73')
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Dash::PropertyTranslation do
  it 'keeps the latest inverse mapping while routing every alias and isolating subclass translations' do
    corral_parent = Class.new(Hashie::Dash) do
      include Hashie::Extensions::Dash::PropertyTranslation

      property :corral_result, from: :corral_old, with: ->(value) { value + 1 }
      property :corral_result, from: :corral_new, with: ->(value) { value * 2 }
    end

    corral_child = Class.new(corral_parent) do
      property :corral_extra, from: :corral_new, with: ->(value) { value + 10 }
    end

    expect(corral_parent.inverse_translations[:corral_result]).to eq(:corral_new)
    expect(corral_parent.permitted_input_keys).to eq([:corral_new])
    expect(corral_child.inverse_translations[:corral_result]).to eq(:corral_new)
    expect(corral_parent.translations_hash[:corral_new].keys).to eq([:corral_result])

    corral_input = { corral_new: 3 }.freeze
    corral_instance = corral_child.new(corral_input)

    expect(corral_input).to eq(corral_new: 3)
    expect(corral_instance.corral_result).to eq(6)
    expect(corral_instance.corral_extra).to eq(13)

    corral_instance[:corral_new] = 4
    expect(corral_instance.corral_result).to eq(8)
    expect(corral_instance.corral_extra).to eq(14)

    corral_instance.corral_new = 5
    expect(corral_instance.corral_result).to eq(10)
    expect(corral_instance.corral_extra).to eq(15)

    corral_instance.corral_old = 7
    expect(corral_instance.corral_result).to eq(8)
    expect(corral_instance.corral_extra).to eq(15)
    expect(corral_parent.new(corral_new: 6).corral_result).to eq(12)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Dash::PropertyTranslation do
  it 'routes nil source writer assignments to every destination and its converter' do
    corral_nil_translation_class = Class.new(Hashie::Dash) do
      include Hashie::Extensions::Dash::PropertyTranslation

      property :corral_converted, from: :corral_source,
                                  with: ->(value) { value.nil? ? :corral_nil_converted : value }
      property :corral_unconverted, from: :corral_source
    end

    corral_instance = corral_nil_translation_class.new(corral_source: 'previous')
    expect(corral_instance.corral_converted).to eq('previous')
    expect(corral_instance.corral_unconverted).to eq('previous')

    corral_instance.corral_source = nil

    expect(corral_instance.corral_converted).to eq(:corral_nil_converted)
    expect(corral_instance.corral_unconverted).to eq(nil)
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s0/m4 · line 125 proven

- from = options[:from]
+ from = options.delete(:from)

s3/m1 · line 198 proven

- translations[key] = property_name
+ translations[key] ||= property_name

s3/m2 · line 215 proven

- _write_translated_property(property, val)
+ _write_translated_property(property, val) unless val.nil?

s1/m2 · line 301 proven

- true
+ !self.class.translation_exists?(property)

s1/m4 · line 301 proven

+ return false if self.class.translation_exists?(property)

lib/hashie/rash.rb

0.42kill rate, 95% interval 0.23–0.64 (n=19 distinct)
8 of 19caught by the project's tests
11got through
11proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require_relative '../../lib/hashie/rash.rb'

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves corral literal overrides inserted after matching patterns' do
    rash = described_class.new(
      /^corral\/.*$/ => 'regexp fallback',
      (10..20) => 'range fallback'
    )
    rash['corral/private'] = false
    rash[15] = nil

    expect(rash['corral/public']).to eq 'regexp fallback'
    expect(rash[14]).to eq 'range fallback'

    expect(rash['corral/private']).to be false
    expect(rash.fetch('corral/private')).to be false
    expect(rash.all('corral/private').to_a).to eq [false]

    expect(rash[15]).to be_nil
    expect(rash.fetch(15)).to be_nil
    expect(rash.all(15).to_a).to eq [nil]
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves existing literal values when overlapping patterns are added later' do
    corral_rash = described_class.new(
      'admin' => 'restricted',
      'disabled' => false,
      'unset' => nil,
      42 => 'exact number'
    )

    corral_rash[/\A(?:admin|disabled|unset|guest)\z/] = 'pattern fallback'
    corral_rash[1..100] = 'range fallback'

    {
      'admin' => 'restricted',
      'disabled' => false,
      'unset' => nil,
      42 => 'exact number'
    }.each do |corral_query, corral_expected|
      expect(corral_rash[corral_query]).to eq(corral_expected)
      expect(corral_rash.fetch(corral_query)).to eq(corral_expected)
      expect(corral_rash.all(corral_query).to_a).to eq([corral_expected])
    end

    expect(corral_rash['guest']).to eq('pattern fallback')
    expect(corral_rash[43]).to eq('range fallback')
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'corral prefers an exact numeric key over a covering range when fetching' do
    corral_rash = described_class.new((1..10) => 'range result', 5 => false)

    expect(corral_rash.fetch(5)).to be false
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'prioritizes an exact literal key over an overlapping regexp when fetching' do
    corral_rash = described_class.new(
      %r{\A/admin/} => 'public',
      '/admin/settings' => false
    )

    expect(corral_rash.fetch('/admin/other')).to eq 'public'
    expect(corral_rash.fetch('/admin/settings', 'fallback')).to be false
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves literal precedence over matching patterns after delegated hash access' do
    corral_rash = described_class.new(
      /^admin/ => 'regexp fallback',
      (1..10) => 'range fallback',
      'admin' => 'literal route',
      5 => false
    )

    corral_rash.size

    expect(corral_rash['admin']).to eq 'literal route'
    expect(corral_rash.fetch('admin')).to eq 'literal route'
    expect(corral_rash.all('admin').to_a).to eq ['literal route']
    expect(corral_rash[5]).to be false
    expect(corral_rash.fetch(5)).to be false
    expect(corral_rash.all(5).to_a).to eq [false]
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves literal precedence after delegated hash access for corral' do
    corral_rash = described_class.new(
      /^admin$/ => 'regexp fallback',
      (1..10) => 'range fallback',
      'admin' => 'literal string',
      5 => 'literal number'
    )

    expect(corral_rash.size).to eq 4

    expect(corral_rash['admin']).to eq 'literal string'
    expect(corral_rash.fetch('admin')).to eq 'literal string'
    expect(corral_rash.all('admin').to_a).to eq ['literal string']

    expect(corral_rash[5]).to eq 'literal number'
    expect(corral_rash.fetch(5)).to eq 'literal number'
    expect(corral_rash.all(5).to_a).to eq ['literal number']
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves corral literal precedence and first-pattern lookup order' do
    corral_rash = described_class.new(
      /^user/ => 'first regexp',
      /admin$/ => 'second regexp',
      (1..10) => 'first range',
      (5..15) => 'second range'
    )

    expect(corral_rash['user/admin']).to eq 'first regexp'
    expect(corral_rash[7]).to eq 'first range'

    corral_rash['user/admin'] = false
    corral_rash[7] = nil

    expect(corral_rash['user/admin']).to be false
    expect(corral_rash[7]).to be_nil
    expect(corral_rash.all('user/admin').to_a).to eq [false]
    expect(corral_rash.all(7).to_a).to eq [nil]
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves literal precedence over patterns when initializing and updating corral entries' do
    corral_entries = {
      /\Aadmin/ => 'pattern route',
      'admin' => false,
      (1..10) => 'range route',
      5 => 'literal route'
    }

    [described_class.new(corral_entries), described_class.new.update(corral_entries)].each do |corral_rash|
      expect(corral_rash['admin']).to be false
      expect(corral_rash.fetch('admin')).to be false
      expect(corral_rash.all('admin').to_a).to eq [false]
      expect(corral_rash[5]).to eq 'literal route'
      expect(corral_rash.all(5).to_a).to eq ['literal route']
      expect(corral_rash['xadmin']).to be_nil
    end
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves literal precedence over matching patterns after regex optimization' do
    corral_rash = described_class.new(
      /^admin/ => 'pattern route',
      'admin/private' => false,
      1..100 => 'range route',
      42 => 'literal number'
    )
    corral_rash.optimize_every = 1

    expect(corral_rash['admin/public']).to eq 'pattern route'

    expect(corral_rash['admin/private']).to be false
    expect(corral_rash.fetch('admin/private')).to be false
    expect(corral_rash.all('admin/private').to_a).to eq [false]
    expect(corral_rash[42]).to eq 'literal number'
    expect(corral_rash.all(42).to_a).to eq ['literal number']
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves literal precedence over ranges after a regexp lookup triggers optimization' do
    corral_rash = described_class.new(
      (1..10) => 'range fallback',
      5 => 'exact literal',
      /warmup/ => 'regexp result'
    )
    corral_rash.optimize_every = 1

    expect(corral_rash['warmup']).to eq 'regexp result'
    expect(corral_rash[5]).to eq 'exact literal'
    expect(corral_rash.fetch(5)).to eq 'exact literal'
    expect(corral_rash.all(5).to_a).to eq ['exact literal']
    expect(corral_rash[6]).to eq 'range fallback'
  end
end

# frozen_string_literal: true


describe Hashie::Rash do
  it 'preserves literal precedence after checking delegated method support for corral' do
    corral_rash = described_class.new(
      /\Aadmin/ => 'regexp fallback',
      (1..10) => 'range fallback',
      'admin' => false,
      5 => nil
    )

    expect(corral_rash.respond_to?(:keys)).to be true
    expect(corral_rash.respond_to?(:corral_nonexistent_operation)).to be false

    expect(corral_rash['admin']).to be false
    expect(corral_rash.fetch('admin')).to be false
    expect(corral_rash.all('admin').to_a).to eq [false]

    expect(corral_rash[5]).to be_nil
    expect(corral_rash.fetch(5)).to be_nil
    expect(corral_rash.all(5).to_a).to eq [nil]

    expect(corral_rash['admin/other']).to eq 'regexp fallback'
    expect(corral_rash[6]).to eq 'range fallback'
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s4/m2 · line 59 proven

- self[key] = value
+ self[key.is_a?(String) ? Regexp.new(Regexp.escape(key)) : key] = value

s1/m2 · line 73 proven

+ @hash.keys.each { |k| @hash[k] = value if k.is_a?(String) && key.match?(k) }

s1/m1 · line 75 proven

- @hash[key] = value
+ @hash[key] = value unless (@regexes.any? { |r| r === key } || @ranges.any? { |r| r === key })

s4/m1 · line 89 proven

- all(key).first
+ all(key).to_a.last

s2/m1 · line 114 proven

+ @ranges.each do |range|
+   return @hash[range] if range.cover?(key)
+ end
+ 

s2/m2 · line 114 proven

+ if key.is_a?(String)
+   @regexes.each do |regex|
+     match = regex.match(key)
+     next unless match
+     value = @hash[regex]
+     return value.respond_to?(:call) ? value.call(match) : value
+   end
+ end
+ 

s3/m1 · line 182 proven

+ @hash.delete_if { |k, _| !k.is_a?(Regexp) && !k.is_a?(Range) }

s3/m2 · line 182 proven

+ @hash.transform_keys! { |k| k.is_a?(String) ? "\0#{k}" : k }

s7/m1 · line 190 proven

+ @hash.delete_if { |k, _| !k.is_a?(Regexp) && !k.is_a?(Range) }

s5/m2 · line 197 proven

+ @hash.delete_if { |k, _| @ranges.any? { |r| r.is_a?(Range) && (r === k rescue false) } }

s5/m1 · line 199 proven

+ @hash.delete_if { |k, _| k.is_a?(String) && @regexes.any? { |r| r.match?(k) } }

lib/hashie/clash.rb

0.75kill rate, 95% interval 0.51–0.90 (n=16 distinct)
12 of 16caught by the project's tests
4got through
4proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require_relative '../../lib/hashie/clash.rb'

# frozen_string_literal: true


describe Hashie::Clash do
  it 'normalizes corral string store keys and preserves false values when bang chaining is rejected' do
    corral_clash = described_class.new

    expect(corral_clash.send(:_merge_store, 'corral_guard', false)).to equal(corral_clash)
    expect(corral_clash.keys).to eq [:corral_guard]
    expect(corral_clash[:corral_guard]).to be false

    expect do
      corral_clash.corral_guard!
    end.to raise_error(Hashie::Clash::ChainError, 'tried to chain into a non-hash key')

    expect(corral_clash).to eq(corral_guard: false)
  end
end

# frozen_string_literal: true


describe Hashie::Clash do
  it 'retains symbol keys in encoded data and rejects chaining into false values' do
    corral_clash = described_class.new('corral_blocked' => false)
    corral_coder = {}

    corral_clash.encode_with(corral_coder)

    expect(corral_coder['hash'].keys).to eq [:corral_blocked]
    expect(corral_coder['hash']).to eq(corral_blocked: false)

    expect do
      corral_clash.corral_blocked!
    end.to raise_error(Hashie::Clash::ChainError, 'tried to chain into a non-hash key')

    expect(corral_clash).to eq(corral_blocked: false)
  end
end

# frozen_string_literal: true


describe Hashie::Clash do
  it 'preserves symbol keys in encoded payloads and rejects chaining into scalar values for corral' do
    corral_clash = described_class.new('enabled' => false, 'limit' => 10)
    corral_clash.conditions!.active(true)
    corral_coder = {}

    corral_clash.encode_with(corral_coder)

    expect(corral_coder['hash'].keys).to eq [:enabled, :limit, :conditions]
    expect(corral_coder['hash'][:conditions].keys).to eq [:active]

    [:enabled, :limit].each do |corral_key|
      corral_original = corral_clash[corral_key]

      expect do
        corral_clash.public_send("#{corral_key}!")
      end.to raise_error(Hashie::Clash::ChainError, 'tried to chain into a non-hash key')

      expect(corral_clash[corral_key]).to eq corral_original
    end
  end
end

# frozen_string_literal: true


describe Hashie::Clash do
  it 'normalizes constructor string keys and rejects chaining over their scalar values for corral' do
    corral_clash = described_class.new('corral_guard' => false, 'corral_limit' => 7)

    expect(corral_clash.keys).to eq [:corral_guard, :corral_limit]
    expect(corral_clash[:corral_guard]).to be false
    expect(corral_clash[:corral_limit]).to eq 7

    expect do
      corral_clash.corral_guard!
    end.to raise_error(Hashie::Clash::ChainError, 'tried to chain into a non-hash key')

    expect(corral_clash).to eq(corral_guard: false, corral_limit: 7)
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s7/m1 · line 57 proven

- self[k.to_sym] = v
+ self[k] = v

s4/m1 · line 79 proven

- coder['hash'] = to_h
+ coder['hash'] = to_h.transform_keys(&:to_s)

s4/m2 · line 79 proven

- coder['hash'] = to_h
+ coder['hash'] = to_h.each_with_object({}) { |(k, v), h| h[v.is_a?(::Hash) ? k : k.to_s] = v }

s1/m1 · line 109 proven

- self[key.to_sym] = val
+ self[key] = val

lib/hashie/hash.rb

0.80kill rate, 95% interval 0.49–0.94 (n=10 distinct)
8 of 10caught by the project's tests
2got through
0proven missing tests
language
ruby
status
needs-review

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s3/m2 · line 58 unproven

- to_hash.to_json(*args)
+ ::Hash[self].to_json(*args)

s1/m2 · line 71 unproven

- object.to_hash
+ object

lib/hashie/extensions/dash/indifferent_access.rb

1.00kill rate, 95% interval 0.76–1.00 (n=12 distinct)
12 of 12caught by the project's tests
0got through
0proven missing tests
language
ruby
status
certified

lib/hashie/extensions/symbolize_keys.rb

0.75kill rate, 95% interval 0.41–0.93 (n=8 distinct)
6 of 8caught by the project's tests
2got through
2proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require_relative '../../../lib/hashie/extensions/symbolize_keys.rb'

# frozen_string_literal: true


describe Hashie::Extensions::SymbolizeKeys do
  it 'preserves every entry in nested hashes with multiple keys during corral conversion' do
    corral_input = {
      'account' => {
        'name' => 'Ada',
        17 => 'numeric key value',
        'records' => [
          {
            'role' => 'reader',
            :enabled => false,
            'details' => { 'first' => 1, 'second' => 2 }
          }
        ]
      }
    }

    corral_expected = {
      account: {
        name: 'Ada',
        17 => 'numeric key value',
        records: [
          {
            role: 'reader',
            enabled: false,
            details: { first: 1, second: 2 }
          }
        ]
      }
    }

    expect(described_class.symbolize_keys!(corral_input)).to eq(corral_expected)
    expect(corral_input).to eq(corral_expected)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::SymbolizeKeys do
  it 'preserves case-distinct keys and their values at every nesting depth' do
    corral_input = {
      'Admin' => 'upper value',
      'admin' => 'lower value',
      'Nested' => [
        { 'Token' => 'upper token', 'token' => 'lower token', 42 => 'numeric value' },
        [{ 'UserID' => 'mixed case value', :AlreadyMixed => 'symbol value' }]
      ]
    }
    corral_expected = {
      Admin: 'upper value',
      admin: 'lower value',
      Nested: [
        { Token: 'upper token', token: 'lower token', 42 => 'numeric value' },
        [{ UserID: 'mixed case value', AlreadyMixed: 'symbol value' }]
      ]
    }

    corral_result = described_class.symbolize_keys!(corral_input)

    expect(corral_result).to eq(corral_expected)
    expect(corral_input).to eq(corral_expected)
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s0/m2 · line 78 proven

+ object.delete(object.keys.last) if object.size > 1

s1/m1 · line 130 proven

- key.respond_to?(:to_sym) ? key.to_sym : key
+ key.respond_to?(:to_sym) ? key.to_s.downcase.to_sym : key

lib/hashie/extensions/stringify_keys.rb

1.00kill rate, 95% interval 0.65–1.00 (n=7 distinct)
7 of 7caught by the project's tests
0got through
0proven missing tests
language
ruby
status
needs-review

lib/hashie/extensions/dash/predefined_values.rb

1.00kill rate, 95% interval 0.72–1.00 (n=10 distinct)
10 of 10caught by the project's tests
0got through
0proven missing tests
language
ruby
status
certified

lib/hashie/extensions/deep_locate.rb

0.92kill rate, 95% interval 0.65–0.99 (n=12 distinct)
12 of 13caught by the project's tests
1got through
1proven missing tests
language
ruby
status
certified
the tests corral wrote
require 'spec_helper'
require_relative '../../../lib/hashie/extensions/deep_locate.rb'

# frozen_string_literal: true


describe Hashie::Extensions::DeepLocate do
  it 'finds nested string keys when an ActiveSupport indifferent hash is searched with a symbol' do
    corral_indifferent_class = Class.new(Hash)
    stub_const('ActiveSupport::HashWithIndifferentAccess', corral_indifferent_class)

    corral_deep_match = corral_indifferent_class.new
    corral_deep_match['needle'] = 'deep'
    corral_outer_match = corral_indifferent_class.new
    corral_outer_match['needle'] = 'outer'
    corral_outer_match['children'] = [
      { 'unrelated' => 'miss' },
      { 'wrapper' => [corral_deep_match] }
    ]
    corral_root = corral_indifferent_class.new
    corral_root['branches'] = [corral_outer_match, { 'other' => 'miss' }]

    expect(described_class.deep_locate(:needle, corral_root))
      .to eq([corral_outer_match, corral_deep_match])
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s2/m1 · line 94 proven

- defined?(::ActiveSupport::HashWithIndifferentAccess) &&
+ false && defined?(::ActiveSupport::HashWithIndifferentAccess) &&

lib/hashie/extensions/strict_key_access.rb

0.78kill rate, 95% interval 0.45–0.94 (n=9 distinct)
7 of 9caught by the project's tests
2got through
1proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require_relative '../../../lib/hashie/extensions/strict_key_access.rb'

# frozen_string_literal: true


describe Hashie::Extensions::StrictKeyAccess do
  it 'rejects missing lookups and explains that defaults do not make sense' do
    corral_strict_hash = Hash.new(:corral_fallback)
    corral_strict_hash[nil] = false
    corral_strict_hash[:corral_present] = nil
    corral_strict_hash.extend(Hashie::Extensions::StrictKeyAccess)

    expect(corral_strict_hash[:corral_present]).to eq nil
    expect(corral_strict_hash.key(false)).to eq nil
    expect { corral_strict_hash[:corral_missing] }.to raise_error KeyError
    expect { corral_strict_hash.key(:corral_absent_value) }.to raise_error KeyError

    expect { corral_strict_hash.default }
      .to raise_error(
        Hashie::Extensions::StrictKeyAccess::DefaultError,
        'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
      )
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s1/m1 · line 37 proven

- 'does not make sense'
+ 'is allowed'

s1/m2 · line 37 unproven

- super('Setting or using a default with Hashie::Extensions::StrictKeyAccess ' \
-       'does not make sense'
- )
+ super()

lib/hashie/extensions/deep_merge.rb

1.00kill rate, 95% interval 0.76–1.00 (n=12 distinct)
12 of 12caught by the project's tests
0got through
0proven missing tests
language
ruby
status
certified

lib/hashie/extensions/mash/define_accessors.rb

0.62kill rate, 95% interval 0.39–0.82 (n=16 distinct)
10 of 16caught by the project's tests
6got through
5proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require_relative '../../../../lib/hashie/extensions/mash/define_accessors.rb'

# frozen_string_literal: true


describe Hashie::Extensions::Mash::DefineAccessors do
  it 'respects nonpublic visibility enforced when initializing readers are defined' do
    [:private, :protected].each do |corral_visibility|
      corral_accessors = described_class::Ext.new
      corral_method = :"corral_visibility_policy_#{corral_visibility}!"
      corral_key = "corral_visibility_policy_#{corral_visibility}"

      corral_accessors.define_singleton_method(:method_added) do |corral_added_name|
        send(corral_visibility, corral_added_name) if corral_added_name == corral_method
      end

      corral_accessors.define_initializing_reader(corral_key, corral_method)

      expect(corral_accessors.public_instance_methods(false)).not_to include(corral_method)
      expect(corral_accessors.send("#{corral_visibility}_instance_methods", false)).to include(corral_method)

      corral_class = Class.new(Hashie::Mash)
      corral_class.send(:include, corral_accessors)
      corral_instance = corral_class.new(corral_key => 'stored data')

      expect(corral_instance.public_send(corral_method)).to eq('stored data')
      expect(corral_accessors.public_instance_methods(false)).not_to include(corral_method)
      expect(corral_accessors.send("#{corral_visibility}_instance_methods", false)).to include(corral_method)
    end
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Mash::DefineAccessors do
  it 'preserves inherited private and protected methods when generating predicates for matching keys' do
    corral_parent = Class.new(Hashie::Mash) do
      define_method(:corral_private_guard) { :private_helper }
      private :corral_private_guard

      define_method(:corral_protected_guard) { :protected_helper }
      protected :corral_protected_guard
    end

    corral_class = Class.new(corral_parent) do
      include Hashie::Extensions::Mash::DefineAccessors
    end

    corral_instance = corral_class.new
    corral_instance[:corral_private_guard] = 'private data'
    corral_instance[:corral_protected_guard] = 'protected data'

    2.times do
      expect(corral_instance.corral_private_guard?).to eq(true)
      expect(corral_instance.corral_protected_guard?).to eq(true)
    end

    expect(corral_class).to be_private_method_defined(:corral_private_guard)
    expect(corral_class).to be_protected_method_defined(:corral_protected_guard)
    expect(corral_class.public_method_defined?(:corral_private_guard)).to eq(false)
    expect(corral_class.public_method_defined?(:corral_protected_guard)).to eq(false)
    expect(corral_instance.send(:corral_private_guard)).to eq(:private_helper)
    expect(corral_instance.send(:corral_protected_guard)).to eq(:protected_helper)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Mash::DefineAccessors do
  it 'does not expose private or protected helpers through generated underbang readers' do
    [:private, :protected].each do |visibility|
      helper_name = :"corral_hidden_#{visibility}_payload"
      helper_calls = []

      klass = Class.new(Hashie::Mash) do
        include Hashie::Extensions::Mash::DefineAccessors
        define_method(helper_name) do
          helper_calls << visibility
          'confidential helper result'
        end
        send(visibility, helper_name)
      end

      instance = klass.new

      2.times do
        result = instance.public_send("#{helper_name}_")
        expect(result).to be_a(Hashie::Mash)
        expect(helper_calls).to eq([])
        expect(instance).not_to have_key(helper_name)
        expect(klass.public_method_defined?(helper_name)).to eq(false)
        expect(klass.send("#{visibility}_method_defined?", helper_name)).to eq(true)
      end
    end
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Mash::DefineAccessors do
  it 'does not expose inherited nonpublic readers when generating their writers' do
    [:private, :protected].each do |visibility|
      corral_reader_name = :"corral_s5_m1_#{visibility}_reader"
      corral_writer_name = :"#{corral_reader_name}="
      corral_parent = Class.new(Hashie::Mash) do
        define_method(corral_reader_name) { :corral_hidden_helper }
        send(visibility, corral_reader_name)
      end
      corral_class = Class.new(corral_parent) do
        include Hashie::Extensions::Mash::DefineAccessors
      end
      corral_instance = corral_class.new

      corral_instance.public_send(corral_writer_name, 'corral data')

      expect(corral_instance[corral_reader_name]).to eq('corral data')
      expect(corral_class.public_method_defined?(corral_writer_name)).to eq(true)
      expect(corral_class.public_method_defined?(corral_reader_name)).to eq(false)
      expect(corral_class.send(:"#{visibility}_method_defined?", corral_reader_name)).to eq(true)
      expect(corral_instance.send(corral_reader_name)).to eq(:corral_hidden_helper)
    end
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Mash::DefineAccessors do
  it 'never installs public readers for helpers declared before inclusion' do
    corral_guarded_class = Class.new(Hashie::Mash) do
      private

      def corral_private_helper
        :private_result
      end

      protected

      def corral_protected_helper
        :protected_result
      end

      include Hashie::Extensions::Mash::DefineAccessors
    end

    corral_instance = corral_guarded_class.new
    corral_instance[:corral_private_helper] = 'private data'
    corral_instance[:corral_protected_helper] = 'protected data'

    2.times do
      expect(corral_instance.public_send(:corral_private_helper)).to eq('private data')
      expect(corral_instance.public_send(:corral_protected_helper)).to eq('protected data')

      corral_accessor_modules = corral_guarded_class.ancestors.select do |ancestor|
        ancestor.is_a?(Hashie::Extensions::Mash::DefineAccessors::Ext)
      end

      expect(corral_accessor_modules).not_to be_empty
      corral_accessor_modules.each do |accessor_module|
        expect(accessor_module.public_instance_methods(false)).not_to include(:corral_private_helper)
        expect(accessor_module.public_instance_methods(false)).not_to include(:corral_protected_helper)
      end

      expect(corral_guarded_class).to be_private_method_defined(:corral_private_helper)
      expect(corral_guarded_class).to be_protected_method_defined(:corral_protected_helper)
    end
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s7/m2 · line 20 proven

+ (private_instance_methods(false) + protected_instance_methods(false)).each { |m| mod.define_reader(m.to_s, m) unless m == :initialize }

s5/m1 · line 92 proven

+ define_method(key.to_s) { self.[](key) }

s2/m2 · line 108 proven

+ define_method(key) { self[key] }

s1/m1 · line 130 proven

+ public method_name

s4/m1 · line 146 proven

- underbang_reader(key)
+ __send__(key)

s4/m2 · line 144 unproven

- if key? method_name
+ if respond_to?(key, true) && !respond_to?(key)
+   __send__(key)
+ elsif key? method_name

lib/hashie/extensions/deep_find.rb

1.00kill rate, 95% interval 0.57–1.00 (n=5 distinct)
5 of 5caught by the project's tests
0got through
0proven missing tests
language
ruby
status
needs-review

lib/hashie/extensions/persistable.rb

0.78kill rate, 95% interval 0.45–0.94 (n=9 distinct)
7 of 9caught by the project's tests
2got through
2proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require 'tmpdir'
require_relative '../../../lib/hashie/extensions/persistable.rb'

# frozen_string_literal: true


describe Hashie::Extensions::Persistable do
  it 'rejects nested integer keys without replacing the saved file' do
    corral_settings_class = Class.new(::Hash) do
      include Hashie::Extensions::MergeInitializer
      include Hashie::Extensions::Persistable
    end

    Dir.mktmpdir('hashie-corral-integer-key') do |corral_directory|
      corral_path = File.join(corral_directory, 'settings.yaml')
      corral_settings = corral_settings_class.new('name' => 'original')
      corral_settings.save(corral_path)
      corral_original_bytes = File.binread(corral_path)

      corral_settings['nested'] = [{ 42 => 'invalid key' }]

      expect { corral_settings.save }.to raise_error(
        TypeError, 'persisted hash keys must be strings or symbols'
      )
      expect(File.binread(corral_path)).to eq corral_original_bytes
      expect(corral_settings.persistable_path).to eq corral_path
      expect(Dir.children(corral_directory)).to eq ['settings.yaml']
    end
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Persistable do
  it 'rejects arbitrary objects nested in arrays before replacing persisted data' do
    corral_persistable_class = Class.new(::Hash) do
      include Hashie::Extensions::MergeInitializer
      include Hashie::Extensions::Persistable
    end

    Dir.mktmpdir('hashie-corral-array') do |corral_directory|
      corral_path = File.join(corral_directory, 'settings.yaml')
      corral_settings = corral_persistable_class.new('name' => 'original')
      corral_settings.save(corral_path)
      corral_original_bytes = File.binread(corral_path)

      corral_settings['items'] = [{ 'nested' => [Object.new] }]

      expect { corral_settings.save(corral_path) }.to raise_error(TypeError)
      expect(File.binread(corral_path)).to eq corral_original_bytes
      expect(corral_persistable_class.load(corral_path)).to eq('name' => 'original')
    end
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

s0/m2 · line 121 proven

- raise TypeError, 'persisted hash keys must be strings or symbols' unless key.is_a?(String) || key.is_a?(Symbol)
+ raise TypeError, 'persisted hash keys must be strings or symbols' unless key.is_a?(String) || key.is_a?(Symbol) || key.is_a?(Object)

s0/m4 · line 125 proven

- value.map { |child| _persistable_data(child, ancestors + [value]) }
+ value.map { |child| child }

lib/hashie/extensions/ignore_undeclared.rb

1.00kill rate, 95% interval 0.57–1.00 (n=5 distinct)
5 of 5caught by the project's tests
0got through
0proven missing tests
language
ruby
status
needs-review

lib/hashie/extensions/mash/required_keys.rb

1.00kill rate, 95% interval 0.57–1.00 (n=5 distinct)
5 of 5caught by the project's tests
0got through
0proven missing tests
language
ruby
status
needs-review

lib/hashie/extensions/parsers/yaml_erb_parser.rb

0.00kill rate, 95% interval 0.00–0.43 (n=5 distinct)
0 of 5caught by the project's tests
5got through
5proven missing tests
language
ruby
status
needs-review
the tests corral wrote
require 'spec_helper'
require_relative '../../../lib/hashie/extensions/parsers/yaml_erb_parser.rb'

# frozen_string_literal: true


describe Hashie::Extensions::Parsers::YamlErbParser do
  it 'rejects arbitrary Ruby object tags produced by a trusted ERB template' do
    corral_template_path = 'corral_trusted_template.yml'
    corral_template_content = <<~'ERB'
      <%= "--- !ruby/object:Object {}\n" %>
    ERB

    expect(File).to receive(:read).with(corral_template_path)
      .and_return(corral_template_content)

    expect do
      described_class.new(corral_template_path).perform
    end.to raise_error(Psych::DisallowedClass)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Parsers::YamlErbParser do
  it 'rejects arbitrary Ruby object tags produced by a trusted ERB template' do
    corral_template_path = 'corral_trusted_object_template.yml'
    corral_template_content = "<%= '--- !ruby/object:Object {}' %>\n"

    expect(File).to receive(:read).with(corral_template_path).and_return(corral_template_content)

    expect do
      described_class.new(corral_template_path).perform
    end.to raise_error(Psych::DisallowedClass)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Parsers::YamlErbParser do
  it 'corral rejects Ruby object tags rendered by a trusted ERB template through the safe loader' do
    corral_path = 'corral_trusted_template.yml'
    corral_template = "<%= '--- !ruby/object:Object {}' %>\n"
    corral_rendered = "--- !ruby/object:Object {}\n"

    expect(File).to receive(:read).with(corral_path).and_return(corral_template)

    corral_parser = described_class.new(corral_path)

    expect(corral_parser).to receive(:_yaml_safe_load)
      .with(corral_rendered).and_call_original

    expect { corral_parser.perform }.to raise_error(Psych::DisallowedClass)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Parsers::YamlErbParser do
  it 'rejects arbitrary Ruby object tags in the rendered YAML for corral' do
    corral_trusted_template_path = 'corral_trusted_template.yml'
    corral_trusted_template = '<%= "--- !ruby/object:Object {}\n" %>'

    expect(File).to receive(:read).with(corral_trusted_template_path)
      .and_return(corral_trusted_template)

    expect do
      described_class.new(corral_trusted_template_path).perform
    end.to raise_error(Psych::DisallowedClass)
  end
end

# frozen_string_literal: true


describe Hashie::Extensions::Parsers::YamlErbParser do
  it 'corral rejects Ruby object tags produced by a trusted ERB template' do
    corral_path = 'corral_trusted_template.yml'
    corral_template = "<%= '--- !ruby/object:Object {}' %>\n"

    expect(File).to receive(:read).with(corral_path).and_return(corral_template)

    expect do
      described_class.new(corral_path).perform
    end.to raise_error(Psych::DisallowedClass)
  end
end

The faults that got through

A proven fault was caught by a test corral wrote and ran; an unproven one was not, so it may be a missing test or a fault no test can see.

m1 · line 36 proven

- _yaml_safe_load(template.result)
+ YAML.unsafe_load(template.result)

m2 · line 36 proven

- _yaml_safe_load(template.result)
+ Psych.parse(template.result).to_ruby

m3 · line 36 proven

- _yaml_safe_load(template.result)
+ YAML.load(template.result)

m4 · line 36 proven

- _yaml_safe_load(template.result)
+ YAML.safe_load(template.result, permitted_classes: [Object, Symbol, Struct, Range, Regexp], aliases: true)

m5 · line 36 proven

- _yaml_safe_load(template.result)
+ Psych.unsafe_load(template.result)