require "helper"
require "inspec/resource"
require "inspec/resources/json"

describe "Inspec::Resources::JSON" do
  describe "when loading a valid json" do
    let(:resource) { load_resource("json", "policyfile.lock.json") }

    it "gets params as a hashmap" do
      _(resource.params).must_be_kind_of Hash
    end

    it "retrieves nil if a param is missing" do
      _(resource.params["missing"]).must_be_nil
    end

    it "retrieves params by name" do
      _(resource.send("name")).must_equal "demo"
    end

    it "retrieves an array by name" do
      _(resource.send("run_list")).must_equal %w{a b}
    end

    it "doesnt resolve dot-notation names" do
      _(resource.send("x.y.z")).must_be_nil
    end

    it "doesnt resolve symbol-notation names" do
      _(resource.send(:'x.y.z')).must_be_nil
    end

    it "is enumerability matches its data" do
      enum = load_resource("json", content: '["a","b"]')
      not_enum = load_resource("json", content: "525600")

      _(enum.respond_to?(:each)).must_equal true
      _(not_enum.respond_to?(:each)).must_equal false
    end
  end

  describe "when loading a nonexistent file" do
    let(:resource) { load_resource("json", "nonexistent.json") }

    it "produces an error" do
      _(resource.resource_exception_message).must_equal "Can't find file: nonexistent.json"
    end

    it "still provides an empty hash for params" do
      _(resource.params).must_equal({})
    end
  end

  describe "#load_raw_from_file" do
    let(:path)     { "/path/to/file.txt" }
    let(:resource) { Inspec::Resources::JsonConfig.allocate }
    let(:inspec)   { mock }
    let(:file)     { mock }

    before do
      resource.stubs(:inspec).returns(inspec)
      inspec.expects(:file).with(path).returns(file)
    end

    it "raises an exception when the file does not exist" do
      file.expects(:file?).returns(false)
      proc { resource.send(:load_raw_from_file, path) }.must_raise Inspec::Exceptions::ResourceSkipped
    end

    it "raises an exception if the file content is nil" do
      file.expects(:file?).returns(true)
      file.expects(:content).returns(nil)
      proc { resource.send(:load_raw_from_file, path) }.must_raise Inspec::Exceptions::ResourceSkipped
    end

    it "raises an exception if the file content is empty" do
      file.expects(:file?).returns(true)
      file.expects(:content).at_least_once.returns("")
      proc { resource.send(:load_raw_from_file, path) }.must_raise Inspec::Exceptions::ResourceSkipped
    end

    it "returns the file content" do
      file.expects(:file?).returns(true)
      file.expects(:content).at_least_once.returns("json goes here")
      resource.send(:load_raw_from_file, path).must_equal "json goes here"
    end
  end

  describe ":command" do
    # CASES:
    #
    # stdout:good,  stderr:empty
    # stdout:bad,   stderr:empty
    # stdout:empty, stderr:msg
    # stdout:empty, stderr:empty

    # TODO: abstract and push up
    def run_json_cmd(cmd)
      backend = Inspec::Backend.create(Inspec::Config.new)
      klass   = Inspec::Resource.registry["json"]

      klass.new(backend, "json", command: cmd)
    end

    # TODO: push up
    def assert_resource_skipped(res, msg)
      assert_predicate res, :resource_skipped?
      refute_predicate res, :resource_failed?

      assert_includes res.resource_exception_message, msg
    end

    # TODO: push up
    def assert_resource_failed(res, msg)
      refute_predicate res, :resource_skipped?
      assert_predicate res, :resource_failed?

      assert_includes res.resource_exception_message, msg
    end

    it "good stdout, empty stderr" do
      resource = run_json_cmd %(ruby -rjson -e "h={'result'=>true}; puts h.to_json")

      assert_equal %({"result":true}), resource.raw_content.chomp
      assert_equal({ "result" => true }, resource.params)
    end

    it "bad stdout, empty stderr" do
      resource = run_json_cmd "echo 'not-valid-json'"

      assert_resource_failed resource, "unexpected token at 'not-valid-json"
    end

    it "empty stdout, message in stderr" do
      skip "Bug #4465: does not work on windows w/ transport==local" if windows?

      resource = run_json_cmd %{abort 'bad args'"}

      assert_resource_failed resource, "No JSON output, STDERR:"
    end

    it "empty stdout, empty stderr" do
      resource = run_json_cmd %{ruby -e "exit 1"}

      assert_resource_failed resource, "No JSON output, STDERR was empty"
    end
  end
end
