ruby-pwsh 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/lib/puppet/provider/dsc_base_provider/dsc_base_provider.rb +26 -5
- data/lib/pwsh/util.rb +10 -14
- data/lib/pwsh/version.rb +1 -1
- data/lib/pwsh.rb +2 -2
- data/spec/unit/puppet/provider/dsc_base_provider/dsc_base_provider_spec.rb +62 -19
- data/spec/unit/pwsh/util_spec.rb +42 -7
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 106716635e03eef49ab00c8dd35cf16a5674a27452968b16ba7756ba252b97be
         | 
| 4 | 
            +
              data.tar.gz: 4bd6c34343347a1d705cea410346028bbb91446bf5983e75409f16c7730f1000
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f8b6c0c4ff6eabbc9a842cc863d76128dbccca74fabfef02b4aea6ba54f3a01ffd3d97447df4ccc027a8f22cf6a7b430b0893e34d0de7989e742442cf56b5ef9
         | 
| 7 | 
            +
              data.tar.gz: 2404be74308d1b80c5d8f775f49f0f0ac6ae7deab47fe7c38e25e79852aac42f87e16b5cea5e4a4617cdde6eac1236cc8407a0e10fe2c9198bcfd0d6e88a7548
         | 
    
        data/README.md
    CHANGED
    
    | @@ -85,6 +85,11 @@ The following platforms are supported: | |
| 85 85 | 
             
            - OSX
         | 
| 86 86 | 
             
            - RedHat
         | 
| 87 87 | 
             
            - Ubuntu
         | 
| 88 | 
            +
            - AlmaLinux
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            ## Limitations
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            - When PowerShell [Script Block Logging](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows?view=powershell-7.4#enabling-script-block-logging) is enabled, data marked as sensitive in your manifest may appear in these logs as plain text. It is **highly recommended**, by both Puppet and Microsoft, that you also enable [Protected Event Logging](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows?view=powershell-7.4#protected-event-logging) alongside this to encrypt the logs to protect this information.
         | 
| 88 93 |  | 
| 89 94 | 
             
            ## License
         | 
| 90 95 |  | 
| @@ -15,6 +15,7 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength | |
| 15 15 | 
             
                @cached_query_results = []
         | 
| 16 16 | 
             
                @cached_test_results = []
         | 
| 17 17 | 
             
                @logon_failures = []
         | 
| 18 | 
            +
                @timeout = nil # default timeout, ps_manager.execute is expecting nil by default..
         | 
| 18 19 | 
             
                super
         | 
| 19 20 | 
             
              end
         | 
| 20 21 |  | 
| @@ -251,18 +252,22 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength | |
| 251 252 | 
             
                  context.err('Logon credentials are invalid')
         | 
| 252 253 | 
             
                  return nil
         | 
| 253 254 | 
             
                end
         | 
| 255 | 
            +
                specify_dsc_timeout(name_hash)
         | 
| 254 256 | 
             
                resource = invocable_resource(props, context, method)
         | 
| 255 257 | 
             
                script_content = ps_script_content(resource)
         | 
| 258 | 
            +
                context.debug("Invoke-DSC Timeout: #{@timeout} milliseconds") if @timeout
         | 
| 256 259 | 
             
                context.debug("Script:\n #{redact_secrets(script_content)}")
         | 
| 257 | 
            -
                output = ps_manager.execute(remove_secret_identifiers(script_content)) | 
| 260 | 
            +
                output = ps_manager.execute(remove_secret_identifiers(script_content), @timeout)
         | 
| 258 261 |  | 
| 259 | 
            -
                if output.nil?
         | 
| 260 | 
            -
                   | 
| 262 | 
            +
                if output[:stdout].nil?
         | 
| 263 | 
            +
                  message = 'Nothing returned.'
         | 
| 264 | 
            +
                  message += " #{output[:errormessage]}" if output[:errormessage]&.match?(/PowerShell module timeout \(\d+ ms\) exceeded while executing/)
         | 
| 265 | 
            +
                  context.err(message)
         | 
| 261 266 | 
             
                  return nil
         | 
| 262 267 | 
             
                end
         | 
| 263 268 |  | 
| 264 269 | 
             
                begin
         | 
| 265 | 
            -
                  data = JSON.parse(output)
         | 
| 270 | 
            +
                  data = JSON.parse(output[:stdout])
         | 
| 266 271 | 
             
                rescue StandardError => e
         | 
| 267 272 | 
             
                  context.err(e)
         | 
| 268 273 | 
             
                  return nil
         | 
| @@ -295,6 +300,18 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength | |
| 295 300 | 
             
                data
         | 
| 296 301 | 
             
              end
         | 
| 297 302 |  | 
| 303 | 
            +
              # Sets the @timeout instance variable.
         | 
| 304 | 
            +
              # @param name_hash [Hash] the hash of namevars to be passed as properties to `Invoke-DscResource`
         | 
| 305 | 
            +
              # The @timeout variable is set to the value of name_hash[:dsc_timeout] in milliseconds
         | 
| 306 | 
            +
              # If name_hash[:dsc_timeout] is nil, @timeout is not changed.
         | 
| 307 | 
            +
              # If @timeout is already set to a value other than nil,
         | 
| 308 | 
            +
              # it is changed only if it's different from name_hash[:dsc_timeout]..
         | 
| 309 | 
            +
              def specify_dsc_timeout(name_hash)
         | 
| 310 | 
            +
                return unless name_hash[:dsc_timeout] && (@timeout.nil? || @timeout != name_hash[:dsc_timeout])
         | 
| 311 | 
            +
             | 
| 312 | 
            +
                @timeout = name_hash[:dsc_timeout] * 1000
         | 
| 313 | 
            +
              end
         | 
| 314 | 
            +
             | 
| 298 315 | 
             
              # Retries Invoke-DscResource when returned error matches error regex supplied as param.
         | 
| 299 316 | 
             
              # @param context [Object] the Puppet runtime context to operate in and send feedback to
         | 
| 300 317 | 
             
              # @param max_retry_count [Int] max number of times to retry Invoke-DscResource
         | 
| @@ -1076,6 +1093,10 @@ class Puppet::Provider::DscBaseProvider # rubocop:disable Metrics/ClassLength | |
| 1076 1093 | 
             
              def ps_manager
         | 
| 1077 1094 | 
             
                debug_output = Puppet::Util::Log.level == :debug
         | 
| 1078 1095 | 
             
                # TODO: Allow you to specify an alternate path, either to pwsh generally or a specific pwsh path.
         | 
| 1079 | 
            -
                 | 
| 1096 | 
            +
                if Pwsh::Util.on_windows?
         | 
| 1097 | 
            +
                  Pwsh::Manager.instance(Pwsh::Manager.powershell_path, Pwsh::Manager.powershell_args, debug: debug_output)
         | 
| 1098 | 
            +
                else
         | 
| 1099 | 
            +
                  Pwsh::Manager.instance(Pwsh::Manager.pwsh_path, Pwsh::Manager.pwsh_args, debug: debug_output)
         | 
| 1100 | 
            +
                end
         | 
| 1080 1101 | 
             
              end
         | 
| 1081 1102 | 
             
            end
         | 
    
        data/lib/pwsh/util.rb
    CHANGED
    
    | @@ -7,28 +7,24 @@ module Pwsh | |
| 7 7 | 
             
                module_function
         | 
| 8 8 |  | 
| 9 9 | 
             
                # Verifies whether or not the current context is running on a Windows node.
         | 
| 10 | 
            +
                # Implementation copied from `facets`: https://github.com/rubyworks/facets/blob/main/lib/standard/facets/rbconfig.rb
         | 
| 10 11 | 
             
                #
         | 
| 11 12 | 
             
                # @return [Bool] true if on windows
         | 
| 12 13 | 
             
                def on_windows?
         | 
| 13 | 
            -
                   | 
| 14 | 
            -
                   | 
| 15 | 
            -
                  !!File::ALT_SEPARATOR
         | 
| 14 | 
            +
                  host_os = RbConfig::CONFIG['host_os']
         | 
| 15 | 
            +
                  !!(host_os =~ /mswin|mingw/)
         | 
| 16 16 | 
             
                end
         | 
| 17 17 |  | 
| 18 | 
            -
                # Verify paths specified are valid directories | 
| 19 | 
            -
                #
         | 
| 20 | 
            -
                # @return [Bool] true if any  | 
| 18 | 
            +
                # Verify paths specified are valid directories.
         | 
| 19 | 
            +
                # Skips paths which do not exist.
         | 
| 20 | 
            +
                # @return [Bool] true if any paths specified are not valid directories
         | 
| 21 21 | 
             
                def invalid_directories?(path_collection)
         | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 24 | 
            -
                  return invalid_paths if path_collection.nil? || path_collection.empty?
         | 
| 22 | 
            +
                  return false if path_collection.nil? || path_collection.empty?
         | 
| 25 23 |  | 
| 26 | 
            -
                   | 
| 27 | 
            -
                  paths | 
| 28 | 
            -
                    invalid_paths = true unless File.directory?(path) || path.empty?
         | 
| 29 | 
            -
                  end
         | 
| 24 | 
            +
                  delimiter = on_windows? ? ';' : ':'
         | 
| 25 | 
            +
                  paths = path_collection.split(delimiter)
         | 
| 30 26 |  | 
| 31 | 
            -
                   | 
| 27 | 
            +
                  paths.any? { |path| !path.empty? && File.exist?(path) && !File.directory?(path) }
         | 
| 32 28 | 
             
                end
         | 
| 33 29 |  | 
| 34 30 | 
             
                # Return a string or symbol converted to snake_case
         | 
    
        data/lib/pwsh/version.rb
    CHANGED
    
    
    
        data/lib/pwsh.rb
    CHANGED
    
    | @@ -108,7 +108,7 @@ module Pwsh | |
| 108 108 | 
             
                  @powershell_command = cmd
         | 
| 109 109 | 
             
                  @powershell_arguments = args
         | 
| 110 110 |  | 
| 111 | 
            -
                   | 
| 111 | 
            +
                  warn "Bad configuration for ENV['lib']=#{ENV['lib']} - invalid path" if Pwsh::Util.invalid_directories?(ENV['lib'])
         | 
| 112 112 |  | 
| 113 113 | 
             
                  if Pwsh::Util.on_windows?
         | 
| 114 114 | 
             
                    # Named pipes under Windows will automatically be mounted in \\.\pipe\...
         | 
| @@ -380,7 +380,7 @@ module Pwsh | |
| 380 380 | 
             
                      pwsh_paths << File.join(path, 'pwsh.exe') if File.exist?(File.join(path, 'pwsh.exe'))
         | 
| 381 381 | 
             
                    end
         | 
| 382 382 | 
             
                  else
         | 
| 383 | 
            -
                    search_paths.split( | 
| 383 | 
            +
                    search_paths.split(':').each do |path|
         | 
| 384 384 | 
             
                      pwsh_paths << File.join(path, 'pwsh') if File.exist?(File.join(path, 'pwsh'))
         | 
| 385 385 | 
             
                    end
         | 
| 386 386 | 
             
                  end
         | 
| @@ -541,7 +541,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do | |
| 541 541 |  | 
| 542 542 | 
             
                context 'when the invocation script returns data without errors' do
         | 
| 543 543 | 
             
                  before do
         | 
| 544 | 
            -
                    allow(ps_manager).to receive(:execute).with(script).and_return({ stdout: 'DSC Data' })
         | 
| 544 | 
            +
                    allow(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: 'DSC Data' })
         | 
| 545 545 | 
             
                    allow(JSON).to receive(:parse).with('DSC Data').and_return(parsed_invocation_data)
         | 
| 546 546 | 
             
                    allow(Puppet::Pops::Time::Timestamp).to receive(:parse).with('2100-01-01').and_return('TimeStamp:2100-01-01')
         | 
| 547 547 | 
             
                    allow(provider).to receive(:fetch_cached_hashes).and_return([])
         | 
| @@ -659,15 +659,15 @@ RSpec.describe Puppet::Provider::DscBaseProvider do | |
| 659 659 | 
             
                context 'when the DSC invocation errors' do
         | 
| 660 660 | 
             
                  it 'writes an error and returns nil' do
         | 
| 661 661 | 
             
                    expect(provider).not_to receive(:logon_failed_already?)
         | 
| 662 | 
            -
                    expect(ps_manager).to receive(:execute).with(script).and_return({ stdout: nil })
         | 
| 663 | 
            -
                    expect(context).to receive(:err).with('Nothing returned')
         | 
| 662 | 
            +
                    expect(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: nil })
         | 
| 663 | 
            +
                    expect(context).to receive(:err).with('Nothing returned.')
         | 
| 664 664 | 
             
                    expect(result).to be_nil
         | 
| 665 665 | 
             
                  end
         | 
| 666 666 | 
             
                end
         | 
| 667 667 |  | 
| 668 668 | 
             
                context 'when handling DateTimes' do
         | 
| 669 669 | 
             
                  before do
         | 
| 670 | 
            -
                    allow(ps_manager).to receive(:execute).with(script).and_return({ stdout: 'DSC Data' })
         | 
| 670 | 
            +
                    allow(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: 'DSC Data' })
         | 
| 671 671 | 
             
                    allow(JSON).to receive(:parse).with('DSC Data').and_return(parsed_invocation_data)
         | 
| 672 672 | 
             
                    allow(provider).to receive(:fetch_cached_hashes).and_return([])
         | 
| 673 673 | 
             
                  end
         | 
| @@ -719,7 +719,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do | |
| 719 719 | 
             
                  context 'when the credential is invalid' do
         | 
| 720 720 | 
             
                    before do
         | 
| 721 721 | 
             
                      allow(provider).to receive(:logon_failed_already?).and_return(false)
         | 
| 722 | 
            -
                      allow(ps_manager).to receive(:execute).with(script).and_return({ stdout: 'DSC Data' })
         | 
| 722 | 
            +
                      allow(ps_manager).to receive(:execute).with(script, nil).and_return({ stdout: 'DSC Data' })
         | 
| 723 723 | 
             
                      allow(JSON).to receive(:parse).with('DSC Data').and_return({ 'errormessage' => dsc_logon_failure_error })
         | 
| 724 724 | 
             
                      allow(context).to receive(:err).with(name_hash[:name], puppet_logon_failure_error)
         | 
| 725 725 | 
             
                    end
         | 
| @@ -783,7 +783,7 @@ RSpec.describe Puppet::Provider::DscBaseProvider do | |
| 783 783 | 
             
                context 'when the invocation script returns nil' do
         | 
| 784 784 | 
             
                  it 'errors via context but does not raise' do
         | 
| 785 785 | 
             
                    expect(ps_manager).to receive(:execute).and_return({ stdout: nil })
         | 
| 786 | 
            -
                    expect(context).to receive(:err).with('Nothing returned')
         | 
| 786 | 
            +
                    expect(context).to receive(:err).with('Nothing returned.')
         | 
| 787 787 | 
             
                    expect { result }.not_to raise_error
         | 
| 788 788 | 
             
                  end
         | 
| 789 789 | 
             
                end
         | 
| @@ -835,9 +835,29 @@ RSpec.describe Puppet::Provider::DscBaseProvider do | |
| 835 835 | 
             
                  end
         | 
| 836 836 | 
             
                end
         | 
| 837 837 |  | 
| 838 | 
            +
                context 'when a dsc_timeout is specified' do
         | 
| 839 | 
            +
                  let(:should_hash) { name.merge(dsc_timeout: 5) }
         | 
| 840 | 
            +
                  let(:apply_props_with_timeout) { { dsc_name: 'foo', dsc_timeout: 5 } }
         | 
| 841 | 
            +
                  let(:resource_with_timeout) { "Resource: #{apply_props_with_timeout}" }
         | 
| 842 | 
            +
                  let(:script_with_timeout) { "Script: #{apply_props_with_timeout}" }
         | 
| 843 | 
            +
             | 
| 844 | 
            +
                  before do
         | 
| 845 | 
            +
                    allow(provider).to receive(:invocable_resource).with(apply_props_with_timeout, context, 'set').and_return(resource_with_timeout)
         | 
| 846 | 
            +
                    allow(provider).to receive(:ps_script_content).with(resource_with_timeout).and_return(script_with_timeout)
         | 
| 847 | 
            +
                    allow(provider).to receive(:remove_secret_identifiers).with(script_with_timeout).and_return(script_with_timeout)
         | 
| 848 | 
            +
                  end
         | 
| 849 | 
            +
             | 
| 850 | 
            +
                  it 'sets @timeout and passes it to ps_manager.execute' do
         | 
| 851 | 
            +
                    provider.instance_variable_set(:@timeout, nil)
         | 
| 852 | 
            +
                    expect(ps_manager).to receive(:execute).with(script_with_timeout, 5000).and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
         | 
| 853 | 
            +
                    provider.invoke_set_method(context, name, should_hash)
         | 
| 854 | 
            +
                    expect(provider.instance_variable_get(:@timeout)).to eq(5000)
         | 
| 855 | 
            +
                  end
         | 
| 856 | 
            +
                end
         | 
| 857 | 
            +
             | 
| 838 858 | 
             
                context 'when the invocation script returns data without errors' do
         | 
| 839 859 | 
             
                  it 'filters for the correct properties to invoke and returns the results' do
         | 
| 840 | 
            -
                    expect(ps_manager).to receive(:execute).with("Script: #{apply_props}").and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
         | 
| 860 | 
            +
                    expect(ps_manager).to receive(:execute).with("Script: #{apply_props}", nil).and_return({ stdout: '{"in_desired_state": true, "errormessage": null}' })
         | 
| 841 861 | 
             
                    expect(context).not_to receive(:err)
         | 
| 842 862 | 
             
                    expect(result).to eq({ 'in_desired_state' => true, 'errormessage' => nil })
         | 
| 843 863 | 
             
                  end
         | 
| @@ -2110,21 +2130,44 @@ RSpec.describe Puppet::Provider::DscBaseProvider do | |
| 2110 2130 | 
             
              end
         | 
| 2111 2131 |  | 
| 2112 2132 | 
             
              describe '.ps_manager' do
         | 
| 2113 | 
            -
                 | 
| 2114 | 
            -
                   | 
| 2115 | 
            -
             | 
| 2116 | 
            -
             | 
| 2133 | 
            +
                describe '.ps_manager on non-Windows' do
         | 
| 2134 | 
            +
                  before do
         | 
| 2135 | 
            +
                    allow(Pwsh::Util).to receive(:on_windows?).and_return(false)
         | 
| 2136 | 
            +
                    allow(Pwsh::Manager).to receive(:pwsh_path).and_return('pwsh')
         | 
| 2137 | 
            +
                    allow(Pwsh::Manager).to receive(:pwsh_args).and_return('args')
         | 
| 2138 | 
            +
                  end
         | 
| 2139 | 
            +
             | 
| 2140 | 
            +
                  it 'Initializes an instance of the Pwsh::Manager' do
         | 
| 2141 | 
            +
                    expect(Puppet::Util::Log).to receive(:level).and_return(:normal)
         | 
| 2142 | 
            +
                    expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: false)
         | 
| 2143 | 
            +
                    expect { provider.ps_manager }.not_to raise_error
         | 
| 2144 | 
            +
                  end
         | 
| 2117 2145 |  | 
| 2118 | 
            -
             | 
| 2119 | 
            -
             | 
| 2120 | 
            -
             | 
| 2121 | 
            -
             | 
| 2146 | 
            +
                  it 'passes debug as true if Puppet::Util::Log.level is debug' do
         | 
| 2147 | 
            +
                    expect(Puppet::Util::Log).to receive(:level).and_return(:debug)
         | 
| 2148 | 
            +
                    expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: true)
         | 
| 2149 | 
            +
                    expect { provider.ps_manager }.not_to raise_error
         | 
| 2150 | 
            +
                  end
         | 
| 2122 2151 | 
             
                end
         | 
| 2123 2152 |  | 
| 2124 | 
            -
                 | 
| 2125 | 
            -
                   | 
| 2126 | 
            -
             | 
| 2127 | 
            -
             | 
| 2153 | 
            +
                describe '.ps_manager on Windows' do
         | 
| 2154 | 
            +
                  before do
         | 
| 2155 | 
            +
                    allow(Pwsh::Util).to receive(:on_windows?).and_return(true)
         | 
| 2156 | 
            +
                    allow(Pwsh::Manager).to receive(:powershell_path).and_return('pwsh')
         | 
| 2157 | 
            +
                    allow(Pwsh::Manager).to receive(:powershell_args).and_return('args')
         | 
| 2158 | 
            +
                  end
         | 
| 2159 | 
            +
             | 
| 2160 | 
            +
                  it 'Initializes an instance of the Pwsh::Manager' do
         | 
| 2161 | 
            +
                    expect(Puppet::Util::Log).to receive(:level).and_return(:normal)
         | 
| 2162 | 
            +
                    expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: false)
         | 
| 2163 | 
            +
                    expect { provider.ps_manager }.not_to raise_error
         | 
| 2164 | 
            +
                  end
         | 
| 2165 | 
            +
             | 
| 2166 | 
            +
                  it 'passes debug as true if Puppet::Util::Log.level is debug' do
         | 
| 2167 | 
            +
                    expect(Puppet::Util::Log).to receive(:level).and_return(:debug)
         | 
| 2168 | 
            +
                    expect(Pwsh::Manager).to receive(:instance).with('pwsh', 'args', debug: true)
         | 
| 2169 | 
            +
                    expect { provider.ps_manager }.not_to raise_error
         | 
| 2170 | 
            +
                  end
         | 
| 2128 2171 | 
             
                end
         | 
| 2129 2172 | 
             
              end
         | 
| 2130 2173 | 
             
            end
         | 
    
        data/spec/unit/pwsh/util_spec.rb
    CHANGED
    
    | @@ -87,13 +87,15 @@ RSpec.describe Pwsh::Util do | |
| 87 87 | 
             
              end
         | 
| 88 88 |  | 
| 89 89 | 
             
              describe '.invalid_directories?' do
         | 
| 90 | 
            -
                let(:valid_path_a) | 
| 91 | 
            -
                let(:valid_path_b) | 
| 92 | 
            -
                let(:valid_paths) | 
| 93 | 
            -
                let(:invalid_path) | 
| 94 | 
            -
                let(:mixed_paths) | 
| 95 | 
            -
                let(:empty_string) | 
| 96 | 
            -
                let(: | 
| 90 | 
            +
                let(:valid_path_a)     { 'C:/some/folder' }
         | 
| 91 | 
            +
                let(:valid_path_b)     { 'C:/another/folder' }
         | 
| 92 | 
            +
                let(:valid_paths)      { 'C:/some/folder;C:/another/folder' }
         | 
| 93 | 
            +
                let(:invalid_path)     { 'C:/invalid/path' }
         | 
| 94 | 
            +
                let(:mixed_paths)      { 'C:/some/folder;C:/another/folder;C:/invalid/path' }
         | 
| 95 | 
            +
                let(:empty_string)     { '' }
         | 
| 96 | 
            +
                let(:file_path)        { 'C:/some/folder/file.txt' }
         | 
| 97 | 
            +
                let(:non_existent_dir) { 'C:/some/dir/that/doesnt/exist' }
         | 
| 98 | 
            +
                let(:empty_members)    { 'C:/some/folder;;C:/another/folder' }
         | 
| 97 99 |  | 
| 98 100 | 
             
                it 'returns false if passed nil' do
         | 
| 99 101 | 
             
                  expect(described_class.invalid_directories?(nil)).to be false
         | 
| @@ -103,8 +105,16 @@ RSpec.describe Pwsh::Util do | |
| 103 105 | 
             
                  expect(described_class.invalid_directories?('')).to be false
         | 
| 104 106 | 
             
                end
         | 
| 105 107 |  | 
| 108 | 
            +
                it 'returns true if a file path is provided' do
         | 
| 109 | 
            +
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 110 | 
            +
                  expect(File).to receive(:exist?).with(file_path).and_return(true)
         | 
| 111 | 
            +
                  expect(File).to receive(:directory?).with(file_path).and_return(false)
         | 
| 112 | 
            +
                  expect(described_class.invalid_directories?(file_path)).to be true
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 106 115 | 
             
                it 'returns false if one valid path is provided' do
         | 
| 107 116 | 
             
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 117 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_a).and_return(true)
         | 
| 108 118 | 
             
                  expect(File).to receive(:directory?).with(valid_path_a).and_return(true)
         | 
| 109 119 | 
             
                  expect(described_class.invalid_directories?(valid_path_a)).to be false
         | 
| 110 120 | 
             
                end
         | 
| @@ -112,31 +122,56 @@ RSpec.describe Pwsh::Util do | |
| 112 122 | 
             
                it 'returns false if a collection of valid paths is provided' do
         | 
| 113 123 | 
             
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 114 124 | 
             
                  expect(File).to receive(:directory?).with(valid_path_a).and_return(true)
         | 
| 125 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_a).and_return(true)
         | 
| 115 126 | 
             
                  expect(File).to receive(:directory?).with(valid_path_b).and_return(true)
         | 
| 127 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_b).and_return(true)
         | 
| 116 128 | 
             
                  expect(described_class.invalid_directories?(valid_paths)).to be false
         | 
| 117 129 | 
             
                end
         | 
| 118 130 |  | 
| 119 131 | 
             
                it 'returns true if there is only one path and it is invalid' do
         | 
| 120 132 | 
             
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 133 | 
            +
                  expect(File).to receive(:exist?).with(invalid_path).and_return(true)
         | 
| 121 134 | 
             
                  expect(File).to receive(:directory?).with(invalid_path).and_return(false)
         | 
| 122 135 | 
             
                  expect(described_class.invalid_directories?(invalid_path)).to be true
         | 
| 123 136 | 
             
                end
         | 
| 124 137 |  | 
| 125 138 | 
             
                it 'returns true if the collection has on valid and one invalid member' do
         | 
| 126 139 | 
             
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 140 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_a).and_return(true)
         | 
| 127 141 | 
             
                  expect(File).to receive(:directory?).with(valid_path_a).and_return(true)
         | 
| 142 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_b).and_return(true)
         | 
| 128 143 | 
             
                  expect(File).to receive(:directory?).with(valid_path_b).and_return(true)
         | 
| 144 | 
            +
                  expect(File).to receive(:exist?).with(invalid_path).and_return(true)
         | 
| 129 145 | 
             
                  expect(File).to receive(:directory?).with(invalid_path).and_return(false)
         | 
| 130 146 | 
             
                  expect(described_class.invalid_directories?(mixed_paths)).to be true
         | 
| 131 147 | 
             
                end
         | 
| 132 148 |  | 
| 133 149 | 
             
                it 'returns false if collection has empty members but other entries are valid' do
         | 
| 134 150 | 
             
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 151 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_a).and_return(true)
         | 
| 135 152 | 
             
                  expect(File).to receive(:directory?).with(valid_path_a).and_return(true)
         | 
| 153 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_b).and_return(true)
         | 
| 136 154 | 
             
                  expect(File).to receive(:directory?).with(valid_path_b).and_return(true)
         | 
| 137 155 | 
             
                  allow(File).to receive(:directory?).with('')
         | 
| 138 156 | 
             
                  expect(described_class.invalid_directories?(empty_members)).to be false
         | 
| 139 157 | 
             
                end
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                it 'returns true if a collection has valid members but also contains a file path' do
         | 
| 160 | 
            +
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 161 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_a).and_return(true)
         | 
| 162 | 
            +
                  expect(File).to receive(:directory?).with(valid_path_a).and_return(true)
         | 
| 163 | 
            +
                  expect(File).to receive(:exist?).with(file_path).and_return(true)
         | 
| 164 | 
            +
                  expect(File).to receive(:directory?).with(file_path).and_return(false)
         | 
| 165 | 
            +
                  expect(described_class.invalid_directories?("#{valid_path_a};#{file_path}")).to be true
         | 
| 166 | 
            +
                end
         | 
| 167 | 
            +
             | 
| 168 | 
            +
                it 'returns false if a collection has valid members but contains a non-existent dir path' do
         | 
| 169 | 
            +
                  expect(described_class).to receive(:on_windows?).and_return(true)
         | 
| 170 | 
            +
                  expect(File).to receive(:exist?).with(valid_path_a).and_return(true)
         | 
| 171 | 
            +
                  expect(File).to receive(:directory?).with(valid_path_a).and_return(true)
         | 
| 172 | 
            +
                  expect(File).to receive(:exist?).with(non_existent_dir).and_return(false)
         | 
| 173 | 
            +
                  expect(described_class.invalid_directories?("#{valid_path_a};#{non_existent_dir}")).to be false
         | 
| 174 | 
            +
                end
         | 
| 140 175 | 
             
              end
         | 
| 141 176 |  | 
| 142 177 | 
             
              describe '.snake_case' do
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ruby-pwsh
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 4 | 
            +
              version: 1.2.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Puppet, Inc.
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2024- | 
| 11 | 
            +
            date: 2024-09-20 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: PowerShell code manager for ruby.
         | 
| 14 14 | 
             
            email:
         |