From 2bca261c8f1c7d6ac01ad4d196b0a7f953b2dbc0 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Sat, 27 Jun 2026 23:04:41 -0700 Subject: [PATCH] Fix NaN coverage percentage when no resources are collected Coverage#report! computed the percentage as `(touched / total.to_f * 100).round(2)`. When a run collects no resources (total == 0), that is `0 / 0.0` which evaluates to NaN. The human ERB template hides this, but the value still flows into the report hash and any programmatic/JSON consumer sees NaN. Extract the math into a coverage_percentage helper that returns 0.0 when there are no resources, and unit test it. Signed-off-by: Tim Smith --- lib/chefspec/coverage.rb | 14 +++++++++++++- spec/unit/coverage_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 spec/unit/coverage_spec.rb diff --git a/lib/chefspec/coverage.rb b/lib/chefspec/coverage.rb index a57b6588..02eb756f 100644 --- a/lib/chefspec/coverage.rb +++ b/lib/chefspec/coverage.rb @@ -161,7 +161,7 @@ def report! report = {}.tap do |h| h[:total] = @collection.size h[:touched] = @collection.count { |_, resource| resource.touched? } - h[:coverage] = ((h[:touched] / h[:total].to_f) * 100).round(2) + h[:coverage] = coverage_percentage(h[:touched], h[:total]) end report[:untouched_resources] = @collection.collect do |_, resource| @@ -179,6 +179,18 @@ def report! private + # + # The percentage of covered resources, guarding against a division by zero + # when no resources were collected (which would otherwise produce NaN). + # + # @return [Float] + # + def coverage_percentage(touched, total) + return 0.0 if total == 0 + + ((touched / total.to_f) * 100).round(2) + end + def find(resource) @collection[resource.to_s] end diff --git a/spec/unit/coverage_spec.rb b/spec/unit/coverage_spec.rb new file mode 100644 index 00000000..e155d2b3 --- /dev/null +++ b/spec/unit/coverage_spec.rb @@ -0,0 +1,21 @@ +require "spec_helper" + +describe ChefSpec::Coverage do + subject(:coverage) { described_class.instance } + + describe "#coverage_percentage" do + it "returns 0.0 when there are no resources instead of NaN" do + result = coverage.send(:coverage_percentage, 0, 0) + expect(result).to eq(0.0) + expect(result).not_to be_nan + end + + it "calculates the touched percentage" do + expect(coverage.send(:coverage_percentage, 3, 4)).to eq(75.0) + end + + it "rounds to two decimal places" do + expect(coverage.send(:coverage_percentage, 1, 3)).to eq(33.33) + end + end +end