#
# Author:: Bryan McLellan <btm@loftninjas.org>
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require "chef/util/dsc/lcm_output_parser"

describe Chef::Util::DSC::LocalConfigurationManager::Parser do
  context "empty input parameter for WhatIfParser" do
    it "raises an exception when there are no valid lines" do
      str = <<-EOF

      EOF
      expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false) }.to raise_error(Chef::Exceptions::LCMParser)
    end

    it "raises an exception for a nil input" do
      expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(nil, false) }.to raise_error(Chef::Exceptions::LCMParser)
    end
  end

  context "empty input parameter for TestDSCParser" do
    it "raises an exception when there are no valid lines" do
      str = <<-EOF

      EOF
      expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true) }.to raise_error(Chef::Exceptions::LCMParser)
    end

    it "raises an exception for a nil input" do
      expect { Chef::Util::DSC::LocalConfigurationManager::Parser.parse(nil, true) }.to raise_error(Chef::Exceptions::LCMParser)
    end
  end

  context "correctly formatted output from lcm for WhatIfParser" do
    it "returns a single resource when only 1 logged with the correct name" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ] [name]
        logtype: [machinename]: LCM:  [ End    Resource ] [name]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources.length).to eq(1)
      expect(resources[0].name).to eq("[name]")
    end

    it "identifies when a resource changes the state of the system" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ] [name]
        logtype: [machinename]: LCM:  [ Start  Set      ] [name]
        logtype: [machinename]: LCM:  [ End    Set      ] [name]
        logtype: [machinename]: LCM:  [ End    Resource ] [name]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].changes_state?).to be_truthy
    end

    it "preserves the log provided for how the system changed the state" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ] [name]
        logtype: [machinename]: LCM:  [ Start  Set      ] [name]
        logtype: [machinename]:                           [message]
        logtype: [machinename]: LCM:  [ End    Set      ] [name]
        logtype: [machinename]: LCM:  [ End    Resource ] [name]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].change_log).to match_array(["[name]", "[message]", "[name]"])
    end

    it "returns false for changes_state?" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ] [name]
        logtype: [machinename]: LCM:  [ Skip   Set      ] [name]
        logtype: [machinename]: LCM:  [ End    Resource ] [name]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].changes_state?).to be_falsey
    end

    it "returns an empty array for change_log if changes_state? is false" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ] [name]
        logtype: [machinename]: LCM:  [ Skip   Set      ] [name]
        logtype: [machinename]: LCM:  [ End    Resource ] [name]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].change_log).to be_empty
    end
  end

  context "correctly formatted output from lcm for TestDSCParser" do
    it "returns a single resource when only 1 logged with the correct name" do
      str = <<~EOF
        InDesiredState            : False
        ResourcesInDesiredState   :
        ResourcesNotInDesiredState: [name]
        ReturnValue               : 0
        PSComputerName            : .
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
      expect(resources.length).to eq(1)
      expect(resources[0].name).to eq("[name]")
    end

    it "identifies when a resource changes the state of the system" do
      str = <<~EOF
        InDesiredState            : False
        ResourcesInDesiredState   :
        ResourcesNotInDesiredState: [name]
        ReturnValue               : 0
        PSComputerName            : .
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
      expect(resources[0].changes_state?).to be_truthy
    end

    it "returns false for changes_state?" do
      str = <<~EOF
        InDesiredState            : True
        ResourcesInDesiredState   : [name]
        ResourcesNotInDesiredState:
        ReturnValue               : 0
        PSComputerName            : .
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
      expect(resources[0].changes_state?).to be_falsey
    end

    it "returns an empty array for change_log if changes_state? is false" do
      str = <<~EOF
        InDesiredState            : True
        ResourcesInDesiredState   : [name]
        ResourcesNotInDesiredState:
        ReturnValue               : 0
        PSComputerName            : .
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
      expect(resources[0].change_log).to be_empty
    end
  end

  context "Incorrectly formatted output from LCM for WhatIfParser" do
    it "allows missing [End Resource] when its the last one and still find all the resource" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ]  [name]
        logtype: [machinename]: LCM:  [ Start  Test     ]
        logtype: [machinename]: LCM:  [ End    Test     ]
        logtype: [machinename]: LCM:  [ Skip   Set      ]
        logtype: [machinename]: LCM:  [ End    Resource ]
        logtype: [machinename]: LCM:  [ Start  Resource ]  [name2]
        logtype: [machinename]: LCM:  [ Start  Test     ]
        logtype: [machinename]: LCM:  [ End    Test     ]
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ End    Set      ]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF

      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].changes_state?).to be_falsey
      expect(resources[1].changes_state?).to be_truthy
    end

    it "allow missing [End Resource] when its the first one and still find all the resource" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ]  [name]
        logtype: [machinename]: LCM:  [ Start  Test     ]
        logtype: [machinename]: LCM:  [ End    Test     ]
        logtype: [machinename]: LCM:  [ Skip   Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ]  [name2]
        logtype: [machinename]: LCM:  [ Start  Test     ]
        logtype: [machinename]: LCM:  [ End    Test     ]
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ End    Set      ]
        logtype: [machinename]: LCM:  [ End    Resource ]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF

      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].changes_state?).to be_falsey
      expect(resources[1].changes_state?).to be_truthy
    end

    it "allows missing set and end resource and assume an unconverged resource in this case" do
      str = <<~EOF
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ Start  Resource ]  [name]
        logtype: [machinename]: LCM:  [ Start  Test     ]
        logtype: [machinename]: LCM:  [ End    Test     ]
        logtype: [machinename]: LCM:  [ Start  Resource ]  [name2]
        logtype: [machinename]: LCM:  [ Start  Test     ]
        logtype: [machinename]: LCM:  [ End    Test     ]
        logtype: [machinename]: LCM:  [ Start  Set      ]
        logtype: [machinename]: LCM:  [ End    Set      ]
        logtype: [machinename]: LCM:  [ End    Resource ]
        logtype: [machinename]: LCM:  [ End    Set      ]
      EOF
      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, false)
      expect(resources[0].changes_state?).to be_truthy
      expect(resources[0].name).to eql("[name]")
      expect(resources[1].changes_state?).to be_truthy
      expect(resources[1].name).to eql("[name2]")
    end
  end

  context "Incorrectly formatted output from LCM for TestDSCParser" do
    it "allows missing [End Resource] when its the last one and still find all the resource" do
      str = <<~EOF
        InDesiredState            : True
        ResourcesInDesiredState   :
        ResourcesNotInDesiredState: [name]
        ReturnValue               : 0
        PSComputerName            : .
        InDesiredState            : True
        ResourcesInDesiredState   :
        ResourcesNotInDesiredState: [name2]
        ReturnValue               : 0
        PSComputerName            : .
      EOF

      resources = Chef::Util::DSC::LocalConfigurationManager::Parser.parse(str, true)
      expect(resources[0].changes_state?).to be_falsey
    end
  end
end
