# # Author:: Nimisha Sharad () # Copyright:: Copyright 2008-2016, 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 "spec_helper" describe Chef::Provider::WindowsTask do let(:new_resource) { Chef::Resource::WindowsTask.new("sample_task") } let(:provider) do node = Chef::Node.new events = Chef::EventDispatch::Dispatcher.new run_context = Chef::RunContext.new(node, {}, events) Chef::Provider::WindowsTask.new(new_resource, run_context) end let(:task_hash) do { :"" => "", :Folder => "\\", :HostName => "NIMISHA-PC", :TaskName => "\\sample_task", :NextRunTime => "3/30/2017 2:42:00 PM", :Status => "Ready", :LogonMode => "Interactive/Background", :LastRunTime => "3/30/2017 2:27:00 PM", :LastResult => "1", :Author => "Administrator", :TaskToRun => "chef-client -L C:\\tmp\\", :StartIn => "N/A", :Comment => "N/A", :ScheduledTaskState => "Enabled", :IdleTime => "Disabled", :PowerManagement => "Stop On Battery Mode, No Start On Batteries", :RunAsUser => "SYSTEM", :DeleteTaskIfNotRescheduled => "Enabled", :StopTaskIfRunsXHoursandXMins => "72:00:00", :Schedule => "Scheduling data is not available in this format.", :ScheduleType => "One Time Only, Minute", :StartTime => "1:12:00 PM", :StartDate => "3/30/2017", :EndDate => "N/A", :Days => "N/A", :Months => "N/A", :"Repeat:Every" => "0 Hour(s), 15 Minute(s)", :"Repeat:Until:Time" => "None", :"Repeat:Until:Duration" => "Disabled", :"Repeat:StopIfStillRunning" => "Disabled", :run_level => "HighestAvailable", :repetition_interval => "PT15M", :execution_time_limit => "PT72H", } end let(:task_xml) do "\r\r\n\r\r\n \r\r\n 2017-03-31T15:34:44\r\r\n Administrator\r\r\n \r\r\n\r\r\n \r\r\n \r\r\n PT15M\r\r\n false\r\r\n \r\r\n 2017-03-31T15:34:00\r\r\n true\r\r\n \r\r\n \r\r\n \r\r\n \r\r\n HighestAvailable\r\r\n S-1-5-18\r\r\n \r\r\n \r\r\n \r\r\n IgnoreNew\r\r\n true\r\r\n true\r\r\n true\r\r\n false\r\r\n false\r\r\n \r\r\n PT10M\r\r\nPT1H\r\r\n true\r\r\n false\r\r\n \r\r\n true\r\r\n true\r\r\n false\r\r\nfalse\r\r\n false\r\r\n PT72H\r\r\n7\r\r\n \r\r\n \r\r\n \r\r\n chef-client\r\r\n \r\r\n \r\r\n" end describe "#load_current_resource" do it "returns a current_resource" do allow(provider).to receive(:load_task_hash) expect(provider.load_current_resource).to be_kind_of(Chef::Resource::WindowsTask) end context "if the given task name already exists" do before do allow(provider).to receive(:load_task_hash).and_return({ :TaskName => "\\sample_task" }) end it "calls set_current_resource" do expect(provider).to receive(:set_current_resource) provider.load_current_resource end end it "sets the attributes of current_resource" do allow(provider).to receive(:load_task_hash).and_return(task_hash) current_resource = provider.load_current_resource expect(current_resource.exists).to be(true) expect(current_resource.command).to eq("chef-client -L C:\\tmp\\") expect(current_resource.user).to eq("SYSTEM") expect(current_resource.run_level).to eq(:highest) expect(current_resource.frequency).to eq(:minute) expect(current_resource.frequency_modifier).to eq(15) expect(current_resource.execution_time_limit).to eq("PT72H") expect(current_resource.enabled).to be(true) end end describe "#action_create" do it "doesn't create the same task if it's already existing" do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource allow(provider).to receive(:task_need_update?).and_return(false) provider.run_action(:create) expect(new_resource).not_to be_updated_by_last_action end it "sets the start_time in 24hr format while updating an existing task" do # task_hash has start_time = "1:12:00 PM" allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:convert_system_date_to_mm_dd_yyyy).and_return("03/30/2017") allow(provider).to receive(:run_schtasks) provider.run_action(:create) # start_time gets set in 24hr format for new_resource expect(new_resource.start_time).to eq("13:12") expect(new_resource).to be_updated_by_last_action end it "sets the start_day in mm/dd/yyyy format while updating an existing task" do # start_day in yyyy-MM-dd format task_hash[:StartDate] = "2017-03-30" allow(provider).to receive(:load_task_hash).and_return(task_hash) current_resource = provider.load_current_resource allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:convert_system_date_format_to_ruby_date_format).and_return("%Y-%m-%d") allow(provider).to receive(:run_schtasks) provider.run_action(:create) # start_day gets set in mm/dd/yyyy format for new_resource expect(new_resource.start_day).to eq("03/30/2017") expect(new_resource).to be_updated_by_last_action end context "when start_day and start_time are N/A for frequency :on_logon" do it "doesn't update the start_day and start_time of new_resource" do task_hash[:on_logon] = true task_hash[:StartDate] = "N/A" task_hash[:StartTime] = "N/A" allow(provider).to receive(:load_task_hash).and_return(task_hash) current_resource = provider.load_current_resource allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:run_schtasks) expect(provider).not_to receive(:convert_system_date_to_mm_dd_yyyy) expect(DateTime).not_to receive(:parse) expect(new_resource.start_day).to eq(nil) expect(new_resource.start_time).to eq(nil) end end context "when task is not existing" do before do allow(provider).to receive(:load_task_hash) provider.load_current_resource end it "creates the task if it's not already existing" do allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:basic_validation).and_return(true) expect(provider).to receive(:run_schtasks).with("CREATE", { "F" => "", "SC" => :hourly, "MO" => 1, "TR" => nil, "RU" => "SYSTEM" }) provider.run_action(:create) expect(new_resource).to be_updated_by_last_action end it "updates the task XML if random_delay is provided" do new_resource.random_delay "20" allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:basic_validation).and_return(true) expect(provider).to receive(:run_schtasks).with("CREATE", { "F" => "", "SC" => :hourly, "MO" => 1, "TR" => nil, "RU" => "SYSTEM" }) expect(provider).to receive(:update_task_xml) provider.run_action(:create) expect(new_resource).to be_updated_by_last_action end it "updates the task XML if execution_time_limit is provided" do new_resource.execution_time_limit "20" allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:basic_validation).and_return(true) expect(provider).to receive(:run_schtasks).with("CREATE", { "F" => "", "SC" => :hourly, "MO" => 1, "TR" => nil, "RU" => "SYSTEM" }) expect(provider).to receive(:update_task_xml) provider.run_action(:create) expect(new_resource).to be_updated_by_last_action end it "updates the task XML if frequency is set as `:none`" do new_resource.frequency :none new_resource.random_delay "" allow(provider).to receive(:task_need_update?).and_return(true) allow(provider).to receive(:basic_validation).and_return(true) allow(provider).to receive(:run_schtasks).and_return("CREATE", { "F" => "", "SC" => :once, "ST" => "00:00", "SD" => "12/12/2012", "TR" => nil, "RU" => "SYSTEM" }) expect(provider).to receive(:update_task_xml) provider.run_action(:create) expect(new_resource).to be_updated_by_last_action end end end describe "#action_run" do it "does nothing if the task doesn't exist" do allow(provider).to receive(:load_task_hash) provider.load_current_resource provider.run_action(:run) expect(new_resource).not_to be_updated_by_last_action end context "when the task exists" do it "does nothing if the task is already running" do task_hash[:Status] = "Running" allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource provider.run_action(:run) expect(new_resource).not_to be_updated_by_last_action end it "runs the task" do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource expect(provider).to receive(:run_schtasks).with("RUN") provider.run_action(:run) expect(new_resource).to be_updated_by_last_action end end end describe "#action_delete" do it "deletes the task if it exists" do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource expect(provider).to receive(:run_schtasks).with("DELETE", { "F" => "" }) provider.run_action(:delete) expect(new_resource).to be_updated_by_last_action end it "does nothing if the task doesn't exist" do allow(provider).to receive(:load_task_hash) provider.load_current_resource provider.run_action(:delete) expect(new_resource).not_to be_updated_by_last_action end end describe "#action_end" do it "does nothing if the task doesn't exist" do allow(provider).to receive(:load_task_hash) provider.load_current_resource provider.run_action(:end) expect(new_resource).not_to be_updated_by_last_action end context "when the task exists" do it "does nothing if the task is not running" do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource provider.run_action(:end) expect(new_resource).not_to be_updated_by_last_action end it "ends the task if it's running" do task_hash[:Status] = "Running" allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource expect(provider).to receive(:run_schtasks).with("END") provider.run_action(:end) expect(new_resource).to be_updated_by_last_action end end end describe "#action_enable" do it "raises error if the task doesn't exist" do allow(provider).to receive(:load_task_hash) provider.load_current_resource expect { provider.run_action(:enable) }.to raise_error(Errno::ENOENT) end context "when the task exists" do it "does nothing if the task is already enabled" do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource provider.run_action(:enable) expect(new_resource).not_to be_updated_by_last_action end it "enables the task if it exists" do task_hash[:ScheduledTaskState] = "Disabled" allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource expect(provider).to receive(:run_schtasks).with("CHANGE", { "ENABLE" => "" }) provider.run_action(:enable) expect(new_resource).to be_updated_by_last_action end end end describe "#action_disable" do it "does nothing if the task doesn't exist" do allow(provider).to receive(:load_task_hash) provider.load_current_resource provider.run_action(:disable) expect(new_resource).not_to be_updated_by_last_action end context "when the task exists" do it "disables the task if it's enabled" do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource expect(provider).to receive(:run_schtasks).with("CHANGE", { "DISABLE" => "" }) provider.run_action(:disable) expect(new_resource).to be_updated_by_last_action end it "does nothing if the task is already disabled" do task_hash[:ScheduledTaskState] = "Disabled" allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource provider.run_action(:disable) expect(new_resource).not_to be_updated_by_last_action end end end describe "#run_schtasks" do before do @task_action = "CREATE" @options = { "F" => "", "SC" => :minute, "MO" => 15, "TR" => "chef-client", "RU" => "SYSTEM", "RL" => "HIGHEST" } @cmd = "schtasks /CREATE /TN \"sample_task\" /F /SC \"minute\" /MO \"15\" /RU \"SYSTEM\" /RL \"HIGHEST\" /TR \"chef-client \" " end it "forms the command properly from the given options" do expect(provider).to receive(:shell_out!).with(@cmd, { :returns => [0] }) provider.send(:run_schtasks, @task_action, @options) end end describe "#basic_validation" do context "when command doesn't exist" do it "raise error" do new_resource.command "" expect { provider.send(:basic_validation) }.to raise_error(Chef::Exceptions::ValidationFailed) end end context "when task_name doesn't exist" do let(:new_resource) { Chef::Resource::WindowsTask.new("") } it "raise error" do expect { provider.send(:basic_validation) }.to raise_error(Chef::Exceptions::ValidationFailed) end end context "when task_name and command exists" do it "returns true" do new_resource.command "cd ~/" expect(provider.send(:basic_validation)).to be(true) end end end describe "#task_need_update?" do context "when task doesn't exist" do before do allow(provider).to receive(:load_task_hash) provider.load_current_resource end it "returns true" do new_resource.command "chef-client" expect(provider.send(:task_need_update?)).to be(true) end end context "when the task exists" do before do allow(provider).to receive(:load_task_hash).and_return(task_hash) allow(provider).to receive(:get_system_short_date_format).and_return("MM/dd/yyyy") provider.load_current_resource new_resource.command "chef-client -L C:\\tmp\\" new_resource.run_level :highest new_resource.frequency :minute new_resource.frequency_modifier 15 new_resource.user "SYSTEM" new_resource.execution_time_limit "PT72H" new_resource.start_day "03/30/2017" new_resource.start_time "13:12" end context "when no attributes are modified" do it "returns false" do expect(provider.send(:task_need_update?)).to be(false) end end context "when frequency_modifier is updated" do it "returns true" do new_resource.frequency_modifier 25 expect(provider.send(:task_need_update?)).to be(true) end end context "when months are updated" do it "returns true" do new_resource.months "JAN" expect(provider.send(:task_need_update?)).to be(true) end end context "when start_day is updated" do it "returns true" do new_resource.start_day "01/01/2000" expect(provider.send(:task_need_update?)).to be(true) end end context "when start_time updated" do it "returns true" do new_resource.start_time "01:01" expect(provider.send(:task_need_update?)).to be(true) end end context "when command updated" do it "return true" do new_resource.command "chef-client" expect(provider.send(:task_need_update?)).to be(true) end end end end describe "#start_day_updated?" do before do allow(provider).to receive(:load_task_hash).and_return(task_hash) allow(provider).to receive(:get_system_short_date_format).and_return("MM/dd/yyyy") provider.load_current_resource new_resource.command "chef-client" new_resource.run_level :highest new_resource.frequency :minute new_resource.frequency_modifier 15 new_resource.user "SYSTEM" new_resource.execution_time_limit "PT72H" new_resource.start_day "03/30/2017" new_resource.start_time "13:12" end context "when start_day not changed" do it "returns false" do expect(provider.send(:start_day_updated?)).to be(false) end end context "when start_day changed" do it "returns true" do new_resource.start_day "01/01/2000" expect(provider.send(:start_day_updated?)).to be(true) end end end describe "#start_time_updated?" do before do allow(provider).to receive(:load_task_hash).and_return(task_hash) provider.load_current_resource new_resource.command "chef-client" new_resource.run_level :highest new_resource.frequency :minute new_resource.frequency_modifier 15 new_resource.user "SYSTEM" new_resource.execution_time_limit "PT72H" new_resource.start_day "3/30/2017" new_resource.start_time "13:12" end context "when start_time not changed" do it "returns false" do expect(provider.send(:start_time_updated?)).to be(false) end end context "when start_time changed" do it "returns true" do new_resource.start_time "01:01" expect(provider.send(:start_time_updated?)).to be(true) end end end describe "#convert_user_date_to_system_date" do it "when current resource start date is '05/30/2017' then returns '30/05/2017'" do allow(provider).to receive(:get_system_short_date_format).and_return("dd/MM/yyyy") expect(provider.send(:convert_user_date_to_system_date, "05/30/2017")).to eq("30/05/2017") end end describe "#convert_system_date_format_to_ruby_date_format" do context "when system date format 'dd-MMM-yy'" do it "returns '%d-%b-%y'" do allow(provider).to receive(:get_system_short_date_format).and_return("dd-MMM-yy") expect(provider.send(:convert_system_date_format_to_ruby_date_format)).to eq("%d-%b-%y") end end context "when system date format 'dd/MM/yyyy'" do it "returns '%d/%m/%Y'" do allow(provider).to receive(:get_system_short_date_format).and_return("dd/MM/yyyy") expect(provider.send(:convert_system_date_format_to_ruby_date_format)).to eq("%d/%m/%Y") end end end describe "#convert_system_date_format_to_ruby_long_date" do context "when system date format 'dd-MMM-yy'" do it "returns '%d-%m-%Y'" do allow(provider).to receive(:get_system_short_date_format).and_return("dd-MMM-yy") expect(provider.send(:convert_system_date_format_to_ruby_long_date)).to eq("%d-%m-%Y") end end context "when system date format 'dd/MM/yyyy'" do it "returns '%d/%m/%Y'" do allow(provider).to receive(:get_system_short_date_format).and_return("dd/MM/yyyy") expect(provider.send(:convert_system_date_format_to_ruby_long_date)).to eq("%d/%m/%Y") end end end describe "#common_date_format_conversion" do context "when system date format 'dd-MM-yyyy'" do it "returns '%d-%m-%Y'" do expect(provider.send(:common_date_format_conversion, "dd-MM-yyyy")).to eq("%d-%m-%Y") end end context "when system date format 'd-M-yyyy'" do it "returns '%d-%m-%Y'" do expect(provider.send(:common_date_format_conversion, "dd-MM-yyyy")).to eq("%d-%m-%Y") end end end describe "#update_task_xml" do before do new_resource.command "chef-client" new_resource.run_level :highest new_resource.frequency :minute new_resource.frequency_modifier 15 new_resource.user "SYSTEM" new_resource.random_delay "20" end it "does nothing if the task doesn't exist" do task_xml = double("xml", :exitstatus => 1) allow(provider).to receive(:powershell_out).and_return(task_xml) output = provider.send(:update_task_xml, ["random_delay"]) expect(output).to be(nil) end it "updates the task XML if random_delay is passed" do shell_out_obj = double("xml", :exitstatus => 0, :stdout => task_xml) allow(provider).to receive(:powershell_out).and_return(shell_out_obj) expect(::File).to receive(:join) expect(::File).to receive(:open) expect(::File).to receive(:delete) expect(provider).to receive(:run_schtasks).twice output = provider.send(:update_task_xml, ["random_delay"]) end it "updates the task XML if frequency is set as `:none`" do new_resource.frequency :none new_resource.random_delay "" shell_out_obj = double("xml", :exitstatus => 0, :stdout => task_xml) allow(provider).to receive(:powershell_out).and_return(shell_out_obj) expect(::File).to receive(:delete) expect(::File).to receive(:join) expect(::File).to receive(:open) expect(provider).to receive(:run_schtasks).twice output = provider.send(:update_task_xml, ["random_delay"]) end end describe "#load_task_hash" do it "returns false if the task doesn't exist" do allow(provider).to receive_message_chain(:powershell_out, :stdout, :force_encoding).and_return("") allow(provider).to receive(:load_task_xml) expect(provider.send(:load_task_hash, "chef-client")).to be(false) end it "returns task hash if the task exists" do powershell_output = "\r\nFolder: \\\r\nHostName: NIMISHA-PC\r\nTaskName: \\chef-client\r\n" task_h = { :"" => "", :Folder => "\\", :HostName => "NIMISHA-PC", :TaskName => "\\chef-client" } allow(provider).to receive_message_chain(:powershell_out, :stdout, :force_encoding).and_return(powershell_output) allow(provider).to receive(:load_task_xml).with("chef-client") expect(provider.send(:load_task_hash, "chef-client")).to eq(task_h) end end describe "#frequency_modifier_allowed" do it "returns true for frequency :hourly" do new_resource.frequency :hourly expect(provider.send(:frequency_modifier_allowed)).to be(true) end it "returns true for frequency :monthly if frequency_modifier is THIRD" do new_resource.frequency :monthly new_resource.frequency_modifier "THIRD" expect(provider.send(:frequency_modifier_allowed)).to be(true) end it "returns false for frequency :once" do new_resource.frequency :once expect(provider.send(:frequency_modifier_allowed)).to be(false) end it "returns false for frequency :none" do new_resource.frequency :none expect(provider.send(:frequency_modifier_allowed)).to be(false) end end end