2019-06-11 22:24:35 +00:00
|
|
|
require "functional/helper"
|
2019-07-24 18:10:15 +00:00
|
|
|
require "json_schemer"
|
2016-04-20 04:50:18 +00:00
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
describe "inspec exec with json formatter" do
|
2016-04-20 04:50:18 +00:00
|
|
|
include FunctionalHelper
|
2019-10-09 04:04:58 +00:00
|
|
|
let(:schema) { Inspec::Schema.json("exec-json") }
|
2016-04-20 04:50:18 +00:00
|
|
|
|
2019-09-17 00:40:51 +00:00
|
|
|
parallelize_me!
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "can execute a simple file and validate the json schema" do
|
|
|
|
out = inspec("exec " + example_control + " --reporter json --no-create-lockfile")
|
2017-03-14 15:50:10 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2019-07-24 18:10:15 +00:00
|
|
|
sout = inspec("schema exec-json")
|
|
|
|
schema = JSONSchemer.schema(sout.stdout)
|
2020-01-28 23:52:02 +00:00
|
|
|
_(schema.validate(data).to_a).must_equal []
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
_(out.stderr).must_be_empty
|
2019-07-23 01:44:43 +00:00
|
|
|
assert_exit_code 0, out
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "can execute a profile and validate the json schema" do
|
2020-01-28 23:52:02 +00:00
|
|
|
out = inspec("exec " + complete_profile + " --reporter json --no-create-lockfile")
|
2017-03-14 15:50:10 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2019-07-24 18:10:15 +00:00
|
|
|
sout = inspec("schema exec-json")
|
|
|
|
schema = JSONSchemer.schema(sout.stdout)
|
2020-01-28 23:52:02 +00:00
|
|
|
_(schema.validate(data).to_a).must_equal []
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
_(out.stderr).must_equal ""
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
assert_exit_code 0, out
|
2018-10-30 10:32:16 +00:00
|
|
|
end
|
|
|
|
|
2019-07-24 18:10:15 +00:00
|
|
|
it "can execute a simple file while using end of options after reporter cli option" do
|
|
|
|
out = inspec("exec --no-create-lockfile --reporter json -- " + example_control)
|
2020-01-28 23:52:02 +00:00
|
|
|
_(out.stderr).must_equal ""
|
|
|
|
_(out.exit_status).must_equal 0
|
2019-07-24 18:10:15 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
|
|
|
sout = inspec("schema exec-json")
|
|
|
|
schema = JSONSchemer.schema(sout.stdout)
|
2020-01-28 23:52:02 +00:00
|
|
|
_(schema.validate(data).to_a).must_equal []
|
2019-07-24 18:10:15 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
_(out.stderr).must_equal ""
|
2019-07-24 18:10:15 +00:00
|
|
|
|
|
|
|
skip_windows!
|
|
|
|
assert_exit_code 0, out
|
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "can execute a profile and validate the json schema with target_id" do
|
2020-01-28 23:52:02 +00:00
|
|
|
out = inspec("exec " + complete_profile + " --reporter json --no-create-lockfile --target-id 1d3e399f-4d71-4863-ac54-84d437fbc444")
|
2018-09-05 19:07:34 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2020-01-28 23:52:02 +00:00
|
|
|
_(data["platform"]["target_id"]).must_equal "1d3e399f-4d71-4863-ac54-84d437fbc444"
|
2019-07-24 18:10:15 +00:00
|
|
|
sout = inspec("schema exec-json")
|
|
|
|
schema = JSONSchemer.schema(sout.stdout)
|
2020-01-28 23:52:02 +00:00
|
|
|
_(schema.validate(data).to_a).must_equal []
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
_(out.stderr).must_equal ""
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
assert_exit_code 0, out
|
2018-09-05 19:07:34 +00:00
|
|
|
end
|
|
|
|
|
2019-07-24 18:10:15 +00:00
|
|
|
it "properly validates all (valid) unit tests against the schema" do
|
|
|
|
schema = JSONSchemer.schema(JSON.parse(inspec("schema exec-json").stdout))
|
2020-01-28 23:52:02 +00:00
|
|
|
all_profile_folders.first(1).each do |folder|
|
2019-07-24 18:10:15 +00:00
|
|
|
begin
|
|
|
|
out = inspec("exec " + folder + " --reporter json --no-create-lockfile")
|
|
|
|
# Ensure it parses properly
|
|
|
|
out = JSON.parse(out.stdout)
|
|
|
|
failures = schema.validate(out).to_a
|
2020-01-28 23:52:02 +00:00
|
|
|
_(failures).must_equal []
|
2019-07-24 18:10:15 +00:00
|
|
|
rescue JSON::ParserError
|
|
|
|
# We don't actually care about these; cannot validate if parsing fails!
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "does not report skipped dependent profiles" do
|
2019-11-06 22:42:44 +00:00
|
|
|
profile = File.join(profile_path, "unsupported_dependencies", "wrapper-profile")
|
|
|
|
out = inspec("exec " + profile + " --reporter json --no-create-lockfile")
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2018-10-15 22:25:27 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2019-09-30 22:31:55 +00:00
|
|
|
_(data["profiles"].count).must_equal 1
|
2019-06-11 22:24:35 +00:00
|
|
|
profile = data["profiles"].first
|
2019-09-30 22:31:55 +00:00
|
|
|
_(profile["controls"].count).must_equal 1
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(out.stderr).must_include "WARN: Skipping profile: 'child_profile' on unsupported platform:"
|
|
|
|
_(out.stderr).must_include "WARN: Skipping profile: 'child_profile2' on unsupported platform:"
|
2019-07-23 01:44:43 +00:00
|
|
|
|
|
|
|
assert_exit_code 0, out
|
2018-10-15 22:25:27 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "flags skipped profiles with correct status" do
|
2019-11-06 22:42:44 +00:00
|
|
|
profile = File.join(profile_path, "unsupported_dependencies", "wrapper-profile")
|
|
|
|
out = inspec("exec " + profile + " --reporter json --no-create-lockfile")
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2018-10-15 22:25:27 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2019-09-30 22:31:55 +00:00
|
|
|
_(data["profiles"].count).must_equal 1
|
2019-06-11 22:24:35 +00:00
|
|
|
profile = data["profiles"].first
|
2019-09-30 22:31:55 +00:00
|
|
|
_(profile["status"]).must_equal "loaded"
|
|
|
|
_(profile["depends"].count).must_equal 2
|
2019-06-11 22:24:35 +00:00
|
|
|
profile["depends"].each do |d|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(d["status"]).must_equal "skipped"
|
|
|
|
_(d["skip_message"]).must_include "Skipping profile: "
|
2018-10-15 22:25:27 +00:00
|
|
|
end
|
2019-07-23 01:44:43 +00:00
|
|
|
|
|
|
|
assert_exit_code 0, out
|
2018-10-15 22:25:27 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "flags loaded profiles with correct status" do
|
2019-11-06 22:42:44 +00:00
|
|
|
profile = File.join(profile_path, "dependencies", "inheritance")
|
|
|
|
out = inspec("exec " + profile + " --reporter json --no-create-lockfile")
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2018-10-15 22:25:27 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2019-06-11 22:24:35 +00:00
|
|
|
profile = data["profiles"].first
|
2019-09-30 22:31:55 +00:00
|
|
|
_(profile["status"]).must_equal "loaded"
|
|
|
|
_(profile["depends"].count).must_equal 2
|
2019-06-11 22:24:35 +00:00
|
|
|
profile["depends"].each do |d|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(d["status"]).must_equal "loaded"
|
|
|
|
_(d.key?("skip_message")).must_equal false
|
2018-10-15 22:25:27 +00:00
|
|
|
end
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(out.stderr).must_equal ""
|
2019-07-23 01:44:43 +00:00
|
|
|
|
|
|
|
assert_exit_code 0, out
|
2018-10-15 22:25:27 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "flags profile with correct status when not supported" do
|
2019-11-06 22:42:44 +00:00
|
|
|
profile = File.join(profile_path, "skippy-profile-os")
|
|
|
|
out = inspec("exec " + profile + " --reporter json --no-create-lockfile")
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2018-10-15 22:25:27 +00:00
|
|
|
data = JSON.parse(out.stdout)
|
2019-06-11 22:24:35 +00:00
|
|
|
profile = data["profiles"].first
|
2019-09-30 22:31:55 +00:00
|
|
|
_(profile["status"]).must_equal "skipped"
|
|
|
|
_(profile["skip_message"]).must_include "Skipping profile: 'skippy' on unsupported platform:"
|
2019-07-23 01:44:43 +00:00
|
|
|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(out.stderr).must_equal ""
|
2019-07-23 01:44:43 +00:00
|
|
|
|
|
|
|
assert_exit_code 101, out
|
2018-10-15 22:25:27 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
describe "execute a profile with json formatting" do
|
2019-10-11 06:20:12 +00:00
|
|
|
let(:raw) { inspec("exec " + example_profile + " --reporter json --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
2019-06-11 22:24:35 +00:00
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:controls) { profile["controls"] }
|
|
|
|
let(:ex1) { controls.find { |x| x["id"] == "tmp-1.0" } }
|
|
|
|
let(:ex2) { controls.find { |x| x["id"] =~ /generated/ } }
|
|
|
|
let(:check_result) do
|
2019-10-09 07:08:28 +00:00
|
|
|
ex3["results"].find { |x| x["resource"] == "example_config" }
|
2019-06-11 22:24:35 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
it "has only one profile" do
|
2019-09-30 22:31:55 +00:00
|
|
|
_(json["profiles"]).must_be_kind_of(Array)
|
|
|
|
_(json["profiles"].length).must_equal 1
|
2016-07-04 09:47:46 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "maps impact symbols to numbers" do
|
2020-01-28 23:52:02 +00:00
|
|
|
_(ex1["impact"]).must_equal 0.7
|
2018-09-13 18:14:05 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "has all the metadata" do
|
2016-04-20 04:50:18 +00:00
|
|
|
actual = profile.dup
|
2019-06-11 22:24:35 +00:00
|
|
|
key = actual.delete("controls")
|
2019-10-11 07:37:26 +00:00
|
|
|
.find { |x| x["id"] =~ /generated from example/ }["id"]
|
2019-06-11 22:24:35 +00:00
|
|
|
groups = actual.delete("groups")
|
2019-10-11 06:20:12 +00:00
|
|
|
actual.delete("sha256")
|
2019-09-30 22:31:55 +00:00
|
|
|
_(actual).must_equal({
|
2016-09-16 21:59:31 +00:00
|
|
|
"name" => "profile",
|
2016-04-20 04:50:18 +00:00
|
|
|
"title" => "InSpec Example Profile",
|
|
|
|
"maintainer" => "Chef Software, Inc.",
|
|
|
|
"copyright" => "Chef Software, Inc.",
|
|
|
|
"copyright_email" => "support@chef.io",
|
2017-05-29 22:16:09 +00:00
|
|
|
"license" => "Apache-2.0",
|
2016-04-20 04:50:18 +00:00
|
|
|
"summary" => "Demonstrates the use of InSpec Compliance Profile",
|
|
|
|
"version" => "1.0.0",
|
2019-06-11 22:24:35 +00:00
|
|
|
"supports" => [{ "platform-family" => "unix" }, { "platform-family" => "windows" }],
|
|
|
|
"attributes" => [],
|
2020-04-29 10:52:28 +00:00
|
|
|
"status" => "loaded",
|
2016-04-20 04:50:18 +00:00
|
|
|
})
|
2016-07-04 09:47:46 +00:00
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
_(groups.sort_by { |x| x["id"] }).must_equal([
|
|
|
|
{ "id" => "controls/example-tmp.rb",
|
|
|
|
"title" => "/ profile",
|
|
|
|
"controls" => ["tmp-1.0", key] },
|
|
|
|
{ "id" => "controls/meta.rb",
|
|
|
|
"title" => "SSH Server Configuration",
|
|
|
|
"controls" => ["ssh-1"] },
|
|
|
|
{ "id" => "controls/minimal.rb",
|
|
|
|
"title" => "Minimal control",
|
|
|
|
"controls" => ["minimalist"] },
|
2016-07-04 09:47:46 +00:00
|
|
|
])
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|
|
|
|
|
2020-01-28 23:52:02 +00:00
|
|
|
it "must have 4 controls" do
|
|
|
|
_(controls.length).must_equal 4
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "has an id for every control" do
|
2019-09-30 22:31:55 +00:00
|
|
|
_(controls.find { |x| x["id"].nil? }).must_be :nil?
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "has results for every control" do
|
2019-09-30 22:31:55 +00:00
|
|
|
_(ex1["results"].length).must_equal 1
|
|
|
|
_(ex2["results"].length).must_equal 1
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "has the right result for tmp-1.0" do
|
2016-04-20 04:50:18 +00:00
|
|
|
actual = ex1.dup
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
src = actual.delete("source_location")
|
2019-11-09 03:08:20 +00:00
|
|
|
_(src["ref"]).must_match %r{test/fixtures/profiles/old-examples/profile/controls/example-tmp.rb$}
|
2019-09-30 22:31:55 +00:00
|
|
|
_(src["line"]).must_equal 6
|
2016-04-20 04:50:18 +00:00
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
result = actual.delete("results")[0]
|
2019-09-30 22:31:55 +00:00
|
|
|
_(result).wont_be :nil?
|
2019-11-06 22:40:00 +00:00
|
|
|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(result["status"]).must_equal "passed"
|
2019-12-07 00:50:09 +00:00
|
|
|
_(result["code_desc"]).must_equal "File / is expected to be directory"
|
2019-09-30 22:31:55 +00:00
|
|
|
_(result["run_time"]).wont_be :nil?
|
|
|
|
_(result["start_time"]).wont_be :nil?
|
2016-04-20 04:50:18 +00:00
|
|
|
|
2019-11-06 22:40:00 +00:00
|
|
|
code = actual.delete "code"
|
|
|
|
_(code).must_include "control 'tmp-1.0' do"
|
2019-05-31 21:59:06 +00:00
|
|
|
|
2019-09-30 22:31:55 +00:00
|
|
|
_(actual).must_equal({
|
2019-06-11 22:24:35 +00:00
|
|
|
"id" => "tmp-1.0",
|
2019-11-06 22:40:00 +00:00
|
|
|
"title" => "Create / directory",
|
2019-06-11 22:24:35 +00:00
|
|
|
"desc" => "An optional description...",
|
|
|
|
"descriptions" => [{ "label" => "default", "data" => "An optional description..." }, { "label" => "label", "data" => "An optional description with a label" }],
|
|
|
|
"impact" => 0.7,
|
|
|
|
"refs" => [{ "url" => "http://...", "ref" => "Document A-12" }],
|
|
|
|
"tags" => { "data" => "temp data", "security" => nil },
|
2019-10-09 04:01:19 +00:00
|
|
|
"waiver_data" => {},
|
2016-04-20 04:50:18 +00:00
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
describe "with a profile that is not supported on this OS/platform" do
|
|
|
|
let(:out) { inspec("exec " + File.join(profile_path, "skippy-profile-os") + " --reporter json --no-create-lockfile") }
|
2016-04-20 04:50:18 +00:00
|
|
|
let(:json) { JSON.load(out.stdout) }
|
|
|
|
|
|
|
|
# TODO: failure handling in json formatters...
|
|
|
|
|
2019-06-11 22:24:35 +00:00
|
|
|
it "never runs the actual resource" do
|
2019-09-30 22:31:55 +00:00
|
|
|
_(File.exist?("/tmp/inspec_test_DONT_CREATE")).must_equal false
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|
|
|
|
end
|
2020-04-24 13:58:52 +00:00
|
|
|
|
|
|
|
describe "JSON reporter without setting reporter-message-truncation" do
|
|
|
|
let(:raw) { inspec("exec " + failure_control + " --reporter json --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:control_with_message) { profile["controls"].find { |c| c["id"] == "Generates a message" } }
|
|
|
|
it "reports full message by default" do
|
|
|
|
_(control_with_message["results"].first["message"]).wont_be :nil?
|
|
|
|
_(control_with_message["results"].first["message"]).must_equal "expected nil to match /some regex that is expected in the content/"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "JSON reporter with reporter-message-truncation set to a number" do
|
|
|
|
let(:raw) { inspec("exec " + failure_control + " --reporter json --reporter-message-truncation=20 --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:control_with_message) { profile["controls"].find { |c| c["id"] == "Generates a message" } }
|
|
|
|
it "reports a truncated message" do
|
|
|
|
_(control_with_message["results"].first["message"]).wont_be :nil?
|
|
|
|
_(control_with_message["results"].first["message"]).must_equal "expected nil to matc[Truncated to 20 characters]"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "JSON reporter with reporter-message-truncation set to ALL" do
|
|
|
|
let(:raw) { inspec("exec " + failure_control + " --reporter json --reporter-message-truncation=ALL --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:control_with_message) { profile["controls"].find { |c| c["id"] == "Generates a message" } }
|
|
|
|
it "reports full message" do
|
|
|
|
_(control_with_message["results"].first["message"]).wont_be :nil?
|
|
|
|
_(control_with_message["results"].first["message"]).must_equal "expected nil to match /some regex that is expected in the content/"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "JSON reporter without setting reporter-backtrace-inclusion" do
|
|
|
|
let(:raw) { inspec("exec " + failure_control + " --reporter json --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:control_with_exception) { profile["controls"].find { |c| c["id"] == "Raises an exception" } }
|
2020-05-01 10:33:48 +00:00
|
|
|
let(:failed_result) { control_with_exception["results"].find { |r| r["exception"] == "NoMethodError" } }
|
2020-04-24 13:58:52 +00:00
|
|
|
it "reports backtrace by default" do
|
|
|
|
_(failed_result["backtrace"]).wont_be :nil?
|
|
|
|
_(failed_result["backtrace"]).must_be_instance_of Array
|
|
|
|
_(failed_result["backtrace"].first).must_be_instance_of String
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "JSON reporter with reporter-backtrace-inclusion set to true" do
|
|
|
|
let(:raw) { inspec("exec " + failure_control + " --reporter json --reporter-backtrace-inclusion=true --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:control_with_exception) { profile["controls"].find { |c| c["id"] == "Raises an exception" } }
|
2020-05-01 10:33:48 +00:00
|
|
|
let(:failed_result) { control_with_exception["results"].find { |r| r["exception"] == "NoMethodError" } }
|
2020-04-24 13:58:52 +00:00
|
|
|
it "reports backtrace" do
|
|
|
|
_(failed_result["backtrace"]).wont_be :nil?
|
|
|
|
_(failed_result["backtrace"]).must_be_instance_of Array
|
|
|
|
_(failed_result["backtrace"].first).must_be_instance_of String
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "JSON reporter with reporter-backtrace-inclusion set to false" do
|
|
|
|
let(:raw) { inspec("exec " + failure_control + " --reporter json --reporter-backtrace-inclusion=false --no-create-lockfile").stdout }
|
|
|
|
let(:json) { JSON.load(raw) }
|
|
|
|
let(:profile) { json["profiles"][0] }
|
|
|
|
let(:control_with_exception) { profile["controls"].find { |c| c["id"] == "Raises an exception" } }
|
2020-05-01 10:33:48 +00:00
|
|
|
let(:failed_result) { control_with_exception["results"].find { |r| r["exception"] == "NoMethodError" } }
|
2020-04-24 13:58:52 +00:00
|
|
|
it "does not report backtrace" do
|
|
|
|
_(failed_result["backtrace"]).must_be :nil?
|
|
|
|
end
|
|
|
|
end
|
2016-04-20 04:50:18 +00:00
|
|
|
end
|