diff --git a/spec/unit/coverage_spec.rb b/spec/unit/coverage_spec.rb new file mode 100644 index 00000000..05c3ff95 --- /dev/null +++ b/spec/unit/coverage_spec.rb @@ -0,0 +1,58 @@ +require "spec_helper" + +describe ChefSpec::Coverage::ResourceWrapper do + let(:resource) do + double("resource", to_s: "template[/etc/foo]", source_line: "/cookbooks/web/recipes/default.rb:12") + end + subject(:wrapper) { described_class.new(resource) } + + describe "#to_s" do + it "delegates to the wrapped resource" do + expect(wrapper.to_s).to eq("template[/etc/foo]") + end + end + + describe "#source_file" do + it "extracts the cookbook-relative file from the source line" do + expect(wrapper.source_file).to eq("web/recipes/default.rb") + end + + it "returns 'Unknown' when the resource has no source line" do + allow(resource).to receive(:source_line).and_return(nil) + expect(wrapper.source_file).to eq("Unknown") + end + end + + describe "#source_line" do + it "extracts the line number as an integer" do + expect(wrapper.source_line).to eq(12) + end + + it "returns 'Unknown' when the resource has no source line" do + allow(resource).to receive(:source_line).and_return(nil) + expect(wrapper.source_line).to eq("Unknown") + end + end + + describe "#touch! and #touched?" do + it "is untouched by default" do + expect(wrapper.touched?).to be(false) + end + + it "is touched after #touch!" do + wrapper.touch! + expect(wrapper.touched?).to be(true) + end + end + + describe "#to_json" do + it "serializes the resource details" do + require "json" + expect(JSON.parse(wrapper.to_json)).to include( + "resource" => "template[/etc/foo]", + "source_line" => 12, + "touched" => false + ) + end + end +end diff --git a/spec/unit/file_cache_path_proxy_spec.rb b/spec/unit/file_cache_path_proxy_spec.rb new file mode 100644 index 00000000..eddf017e --- /dev/null +++ b/spec/unit/file_cache_path_proxy_spec.rb @@ -0,0 +1,14 @@ +require "spec_helper" + +describe ChefSpec::FileCachePathProxy do + subject { described_class.instance } + + it "is a singleton" do + expect(described_class.instance).to be(described_class.instance) + end + + it "exposes a real, existing temp directory" do + expect(subject.file_cache_path).to be_a(String) + expect(Dir.exist?(subject.file_cache_path)).to be(true) + end +end diff --git a/spec/unit/matchers/do_nothing_matcher.rb b/spec/unit/matchers/do_nothing_matcher.rb deleted file mode 100644 index 34d49ddf..00000000 --- a/spec/unit/matchers/do_nothing_matcher.rb +++ /dev/null @@ -1,5 +0,0 @@ -require "spec_helper" - -describe ChefSpec::Matchers::DoNothingMatcher do - pending -end diff --git a/spec/unit/matchers/do_nothing_matcher_spec.rb b/spec/unit/matchers/do_nothing_matcher_spec.rb new file mode 100644 index 00000000..f2c1f99e --- /dev/null +++ b/spec/unit/matchers/do_nothing_matcher_spec.rb @@ -0,0 +1,54 @@ +require "spec_helper" + +describe ChefSpec::Matchers::DoNothingMatcher do + subject { described_class.new } + let(:resource) { Chef::Resource::Execute.new("run my thing") } + + describe "#matches?" do + it "does not match when the resource is nil" do + expect(subject.matches?(nil)).to be false + end + + it "matches a resource that performed no actions" do + allow(resource).to receive(:performed_actions).and_return([]) + expect(subject.matches?(resource)).to be true + end + + it "matches a resource that only performed :nothing" do + allow(resource).to receive(:performed_actions).and_return([:nothing]) + expect(subject.matches?(resource)).to be true + end + + it "does not match a resource that performed a real action" do + allow(resource).to receive(:performed_actions).and_return([:run]) + expect(subject.matches?(resource)).to be false + end + end + + describe "#description" do + it "has the right value" do + expect(subject.description).to eq("do nothing") + end + end + + describe "#failure_message" do + it "lists the actions that were performed" do + allow(resource).to receive(:performed_actions).and_return([:run]) + subject.matches?(resource) + expect(subject.failure_message).to include("to do nothing", ":run") + end + + it "explains when the resource was nil" do + subject.matches?(nil) + expect(subject.failure_message).to include("you gave me was nil") + end + end + + describe "#failure_message_when_negated" do + it "explains that no actions were performed" do + allow(resource).to receive(:performed_actions).and_return([]) + subject.matches?(resource) + expect(subject.failure_message_when_negated).to include("to do something") + end + end +end diff --git a/spec/unit/mixins/normalize_spec.rb b/spec/unit/mixins/normalize_spec.rb new file mode 100644 index 00000000..0c793289 --- /dev/null +++ b/spec/unit/mixins/normalize_spec.rb @@ -0,0 +1,30 @@ +require "spec_helper" + +describe ChefSpec::Normalize do + subject(:normalizer) { Class.new { include ChefSpec::Normalize }.new } + + describe "#resource_name" do + it "returns a symbol for a plain string" do + expect(normalizer.resource_name("file")).to eq(:file) + end + + it "converts dashes to underscores" do + expect(normalizer.resource_name("my-resource")).to eq(:my_resource) + end + + it "prefers declared_type when present" do + thing = double(declared_type: "yum_repository") + expect(normalizer.resource_name(thing)).to eq(:yum_repository) + end + + it "normalizes dashes in a declared_type" do + thing = double(declared_type: "yum-repository") + expect(normalizer.resource_name(thing)).to eq(:yum_repository) + end + + it "falls back to resource_name when declared_type is nil" do + thing = double(declared_type: nil, resource_name: "template") + expect(normalizer.resource_name(thing)).to eq(:template) + end + end +end diff --git a/spec/unit/util_spec.rb b/spec/unit/util_spec.rb new file mode 100644 index 00000000..ce92d93d --- /dev/null +++ b/spec/unit/util_spec.rb @@ -0,0 +1,45 @@ +require "spec_helper" + +describe ChefSpec::Util do + describe "#underscore" do + it "converts CamelCase to snake_case" do + expect(described_class.underscore("CamelCase")).to eq("camel_case") + end + + it "converts namespaced constants to slash-separated paths" do + expect(described_class.underscore("ChefSpec::SoloRunner")).to eq("chef_spec/solo_runner") + end + + it "handles runs of capitals (acronyms)" do + expect(described_class.underscore("HTTPRequest")).to eq("http_request") + end + + it "converts dashes to underscores" do + expect(described_class.underscore("my-cookbook")).to eq("my_cookbook") + end + end + + describe "#camelize" do + it "converts snake_case to CamelCase" do + expect(described_class.camelize("camel_case")).to eq("CamelCase") + end + + it "leaves a single word capitalized" do + expect(described_class.camelize("runner")).to eq("Runner") + end + end + + describe "#truncate" do + it "returns the string unchanged when it is within the limit" do + expect(described_class.truncate("short")).to eq("short") + end + + it "truncates with a trailing ellipsis when over the limit" do + expect(described_class.truncate("#{"x" * 40}", length: 10)).to eq("xxxxxxxx...") + end + + it "defaults to a length of 30" do + expect(described_class.truncate("y" * 40)).to eq("#{"y" * 28}...") + end + end +end