diff --git a/acceptance/tests/agent/agent_disable_lockfile.rb b/acceptance/tests/agent/agent_disable_lockfile.rb index b7d57ed6a..059e678c7 100644 --- a/acceptance/tests/agent/agent_disable_lockfile.rb +++ b/acceptance/tests/agent/agent_disable_lockfile.rb @@ -1,104 +1,104 @@ test_name "the agent --disable/--enable functionality should manage the agent lockfile properly" # # This test is intended to ensure that puppet agent --enable/--disable # work properly, both in terms of complying with our public "API" around # lockfile semantics ( http://links.puppetlabs.com/agent_lockfiles ), and # in terms of actually restricting or allowing new agent runs to begin. # require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() all_tests_passed = false ############################################################################### # BEGIN TEST LOGIC ############################################################################### # this begin block is here for handling temp file cleanup via an "ensure" block at the very end of the # test. begin tuples = [ ["reason not specified", false], ["I'm busy; go away.'", true] ] step "start the master" do with_master_running_on(master, "--autosign true") do tuples.each do |expected_message, explicitly_specify_message| step "disable the agent; specify message? '#{explicitly_specify_message}', message: '#{expected_message}'" do agents.each do |agent| if (explicitly_specify_message) run_agent_on(agent, "--disable \"#{expected_message}\"") else run_agent_on(agent, "--disable") end agent_disabled_lockfile = "#{agent['puppetvardir']}/state/agent_disabled.lock" unless file_exists?(agent, agent_disabled_lockfile) then fail_test("Failed to create disabled lock file '#{agent_disabled_lockfile}' on agent '#{agent}'") end lock_file_content = file_contents(agent, agent_disabled_lockfile) # This is a hack; we should parse the JSON into a hash, but I don't think I have a library available # from the acceptance test framework that I can use to do that. So I'm falling back to regex. lock_file_content_regex = /"disabled_message"\s*:\s*"#{expected_message}"/ unless lock_file_content =~ lock_file_content_regex fail_test("Disabled lock file contents invalid; expected to match '#{lock_file_content_regex}', got '#{lock_file_content}' on agent '#{agent}'") end end end step "attempt to run the agent (message: '#{expected_message}')" do agents.each do |agent| run_agent_on(agent, "--no-daemonize --verbose --onetime --test --server #{master}", :acceptable_exit_codes => [1]) do disabled_regex = /administratively disabled.*'#{expected_message}'/ unless result.stdout =~ disabled_regex fail_test("Unexpected output from attempt to run agent disabled; expecting to match '#{disabled_regex}', got '#{result.stdout}' on agent '#{agent}'") end end end end step "enable the agent (message: '#{expected_message}')" do agents.each do |agent| agent_disabled_lockfile = "#{agent['puppetvardir']}/state/agent_disabled.lock" run_agent_on(agent, "--enable") if file_exists?(agent, agent_disabled_lockfile) then fail_test("Failed to remove disabled lock file '#{agent_disabled_lockfile}' on agent '#{agent}'") end end step "verify that we can run the agent (message: '#{expected_message}')" do agents.each do |agent| run_agent_on(agent) end end end end end end all_tests_passed = true ensure ########################################################################################## # Clean up all of the temp files created by this test. It would be nice if this logic # could be handled outside of the test itself; I envision a stanza like this one appearing # in a very large number of the tests going forward unless it is handled by the framework. ########################################################################################## if all_tests_passed then remove_temp_dirs() end -end \ No newline at end of file +end diff --git a/acceptance/tests/pluginsync/7316_apps_should_be_available_via_pluginsync.rb b/acceptance/tests/pluginsync/7316_apps_should_be_available_via_pluginsync.rb index c24c68011..2c6c194a7 100644 --- a/acceptance/tests/pluginsync/7316_apps_should_be_available_via_pluginsync.rb +++ b/acceptance/tests/pluginsync/7316_apps_should_be_available_via_pluginsync.rb @@ -1,141 +1,141 @@ test_name "the pluginsync functionality should sync app definitions, and they should be runnable afterwards" # # This test is intended to ensure that pluginsync syncs app definitions to the agents. # Further, the apps should be runnable on the agent after the sync has occurred. # require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() all_tests_passed = false ############################################################################### # BEGIN TEST LOGIC ############################################################################### # create some vars to point to the directories that we're going to point the master/agents at master_module_dir = "master_modules" agent_lib_dir = "agent_lib" app_name = "superbogus" app_desc = "a simple %1$s for testing %1$s delivery via plugin sync" app_output = "Hello from the #{app_name} %s" master_module_file_content = {} master_module_file_content["application"] = <<-HERE require 'puppet/application' class Puppet::Application::#{app_name.capitalize} < Puppet::Application def help <<-HELP puppet-#{app_name}(8) -- #{app_desc % "application"} ======== HELP end def main() puts("#{app_output % "application"}") end end HERE # this begin block is here for handling temp file cleanup via an "ensure" block at the very end of the # test. begin modes = ["application"] modes.each do |mode| # here we create a custom app, which basically doesn't do anything except for print a hello-world message agent_module_app_file = "#{agent_lib_dir}/puppet/#{mode}/#{app_name}.rb" master_module_app_file = "#{master_module_dir}/#{app_name}/lib/puppet/#{mode}/#{app_name}.rb" # copy all the files to the master step "write our simple module out to the master" do create_test_file(master, master_module_app_file, master_module_file_content[mode], :mkdirs => true) end step "verify that the app file exists on the master" do unless test_file_exists?(master, master_module_app_file) then fail_test("Failed to create app file '#{get_test_file_path(master, master_module_app_file)}' on master") end end step "start the master" do with_master_running_on(master, "--modulepath=\"#{get_test_file_path(master, master_module_dir)}\" " + "--autosign true") do # the module files shouldn't exist on the agent yet because they haven't been synced step "verify that the module files don't exist on the agent path" do agents.each do |agent| if test_file_exists?(agent, agent_module_app_file) then fail_test("app file already exists on agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end end end step "run the agent" do agents.each do |agent| run_agent_on(agent, "--trace --libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\" " + "--no-daemonize --verbose --onetime --test --server #{master}") end end end end step "verify that the module files were synced down to the agent" do agents.each do |agent| unless test_file_exists?(agent, agent_module_app_file) then fail_test("Expected app file not synced to agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end end end step "verify that the application shows up in help" do agents.each do |agent| on(agent, PuppetCommand.new(:help, "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\"")) do assert_match(/^\s+#{app_name}\s+#{app_desc % mode}/, result.stdout) end end end step "verify that we can run the application" do agents.each do |agent| on(agent, PuppetCommand.new(:"#{app_name}", "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\"")) do assert_match(/^#{app_output % mode}/, result.stdout) end end end step "clear out the libdir on the agents in preparation for the next test" do agents.each do |agent| on(agent, "rm -rf #{get_test_file_path(agent, agent_lib_dir)}/*") end end end all_tests_passed = true ensure ########################################################################################## # Clean up all of the temp files created by this test. It would be nice if this logic # could be handled outside of the test itself; I envision a stanza like this one appearing # in a very large number of the tests going forward unless it is handled by the framework. ########################################################################################## if all_tests_passed then remove_temp_dirs() end -end \ No newline at end of file +end diff --git a/acceptance/tests/pluginsync/7316_faces_with_app_stubs_should_be_available_via_pluginsync.rb b/acceptance/tests/pluginsync/7316_faces_with_app_stubs_should_be_available_via_pluginsync.rb index 3d7a70cba..b41e0667b 100644 --- a/acceptance/tests/pluginsync/7316_faces_with_app_stubs_should_be_available_via_pluginsync.rb +++ b/acceptance/tests/pluginsync/7316_faces_with_app_stubs_should_be_available_via_pluginsync.rb @@ -1,161 +1,161 @@ test_name "the pluginsync functionality should sync app definitions, and they should be runnable afterwards" # # This test is intended to ensure that pluginsync syncs face definitions to the agents. # Further, the face should be runnable on the agent after the sync has occurred. # # (NOTE: When this test is passing, it should resolve both #7316 re: verifying that apps/faces can # be run on the agent node after a plugin sync, and #6753 re: being able to run a face without # having a placeholder stub file in the "applications" directory.) # require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() all_tests_passed = false ############################################################################### # BEGIN TEST LOGIC ############################################################################### # create some vars to point to the directories that we're going to point the master/agents at master_module_dir = "master_modules" agent_lib_dir = "agent_lib" app_name = "superbogus" app_desc = "a simple %1$s for testing %1$s delivery via plugin sync" app_output = "Hello from the #{app_name} %s" master_module_file_content = {} master_module_face_content = <<-HERE Puppet::Face.define(:#{app_name}, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "#{app_desc % "face"}" action(:foo) do summary "a test action defined in the test face in the main puppet lib dir" default when_invoked do |*args| puts "#{app_output % "face"}" end end end HERE master_module_app_content = <<-HERE require 'puppet/application/face_base' class Puppet::Application::#{app_name.capitalize} < Puppet::Application::FaceBase end HERE # this begin block is here for handling temp file cleanup via an "ensure" block at the very end of the # test. begin # here we create a custom app, which basically doesn't do anything except for print a hello-world message agent_module_face_file = "#{agent_lib_dir}/puppet/face/#{app_name}.rb" master_module_face_file = "#{master_module_dir}/#{app_name}/lib/puppet/face/#{app_name}.rb" agent_module_app_file = "#{agent_lib_dir}/puppet/application/#{app_name}.rb" master_module_app_file = "#{master_module_dir}/#{app_name}/lib/puppet/application/#{app_name}.rb" # copy all the files to the master step "write our simple module out to the master" do create_test_file(master, master_module_app_file, master_module_app_content, :mkdirs => true) create_test_file(master, master_module_face_file, master_module_face_content, :mkdirs => true) end step "verify that the app file exists on the master" do unless test_file_exists?(master, master_module_app_file) then fail_test("Failed to create app file '#{get_test_file_path(master, master_module_app_file)}' on master") end unless test_file_exists?(master, master_module_face_file) then fail_test("Failed to create face file '#{get_test_file_path(master, master_module_face_file)}' on master") end end step "start the master" do with_master_running_on(master, "--modulepath=\"#{get_test_file_path(master, master_module_dir)}\" " + "--autosign true") do # the module files shouldn't exist on the agent yet because they haven't been synced step "verify that the module files don't exist on the agent path" do agents.each do |agent| if test_file_exists?(agent, agent_module_app_file) then fail_test("app file already exists on agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end if test_file_exists?(agent, agent_module_app_file) then fail_test("face file already exists on agent: '#{get_test_file_path(agent, agent_module_face_file)}'") end end end step "run the agent" do agents.each do |agent| run_agent_on(agent, "--trace --libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\" " + "--no-daemonize --verbose --onetime --test --server #{master}") end end end end step "verify that the module files were synced down to the agent" do agents.each do |agent| unless test_file_exists?(agent, agent_module_app_file) then fail_test("Expected app file not synced to agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end unless test_file_exists?(agent, agent_module_face_file) then fail_test("Expected face file not synced to agent: '#{get_test_file_path(agent, agent_module_face_file)}'") end end end step "verify that the application shows up in help" do agents.each do |agent| on(agent, PuppetCommand.new(:help, "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\"")) do assert_match(/^\s+#{app_name}\s+#{app_desc % "face"}/, result.stdout) end end end step "verify that we can run the application" do agents.each do |agent| on(agent, PuppetCommand.new(:"#{app_name}", "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\"")) do assert_match(/^#{app_output % "face"}/, result.stdout) end end end step "clear out the libdir on the agents in preparation for the next test" do agents.each do |agent| on(agent, "rm -rf #{get_test_file_path(agent, agent_lib_dir)}/*") end end all_tests_passed = true ensure ########################################################################################## # Clean up all of the temp files created by this test. It would be nice if this logic # could be handled outside of the test itself; I envision a stanza like this one appearing # in a very large number of the tests going forward unless it is handled by the framework. ########################################################################################## if all_tests_passed then remove_temp_dirs() end -end \ No newline at end of file +end diff --git a/acceptance/tests/ticket_13948_lib_dir_hook_should_be_called_on_initialization.rb b/acceptance/tests/ticket_13948_lib_dir_hook_should_be_called_on_initialization.rb index f54f4d835..88afe49ef 100644 --- a/acceptance/tests/ticket_13948_lib_dir_hook_should_be_called_on_initialization.rb +++ b/acceptance/tests/ticket_13948_lib_dir_hook_should_be_called_on_initialization.rb @@ -1,155 +1,155 @@ test_name "the $libdir setting hook is called on startup" require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() all_tests_passed = false ############################################################################### # BEGIN TEST LOGIC ############################################################################### # create some vars to point to the directories that we're going to point the master/agents at master_module_dir = "master_modules" agent_var_dir = "agent_var" agent_lib_dir = "#{agent_var_dir}/lib" app_name = "superbogus" app_desc = "a simple %1$s for testing %1$s delivery via plugin sync" app_output = "Hello from the #{app_name} %s" master_module_file_content = {} master_module_face_content = <<-HERE Puppet::Face.define(:#{app_name}, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "#{app_desc % "face"}" action(:foo) do summary "a test action defined in the test face in the main puppet lib dir" default when_invoked do |*args| puts "#{app_output % "face"}" end end end HERE master_module_app_content = <<-HERE require 'puppet/application/face_base' class Puppet::Application::#{app_name.capitalize} < Puppet::Application::FaceBase end HERE # this begin block is here for handling temp file cleanup via an "ensure" block at the very end of the # test. begin # here we create a custom app, which basically doesn't do anything except for print a hello-world message agent_module_face_file = "#{agent_lib_dir}/puppet/face/#{app_name}.rb" master_module_face_file = "#{master_module_dir}/#{app_name}/lib/puppet/face/#{app_name}.rb" agent_module_app_file = "#{agent_lib_dir}/puppet/application/#{app_name}.rb" master_module_app_file = "#{master_module_dir}/#{app_name}/lib/puppet/application/#{app_name}.rb" # copy all the files to the master step "write our simple module out to the master" do create_test_file(master, master_module_app_file, master_module_app_content, :mkdirs => true) create_test_file(master, master_module_face_file, master_module_face_content, :mkdirs => true) end step "verify that the app file exists on the master" do unless test_file_exists?(master, master_module_app_file) then fail_test("Failed to create app file '#{get_test_file_path(master, master_module_app_file)}' on master") end unless test_file_exists?(master, master_module_face_file) then fail_test("Failed to create face file '#{get_test_file_path(master, master_module_face_file)}' on master") end end step "start the master" do with_master_running_on(master, "--modulepath=\"#{get_test_file_path(master, master_module_dir)}\" " + "--autosign true") do # the module files shouldn't exist on the agent yet because they haven't been synced step "verify that the module files don't exist on the agent path" do agents.each do |agent| if test_file_exists?(agent, agent_module_app_file) then fail_test("app file already exists on agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end if test_file_exists?(agent, agent_module_app_file) then fail_test("face file already exists on agent: '#{get_test_file_path(agent, agent_module_face_file)}'") end end end step "run the agent" do agents.each do |agent| run_agent_on(agent, "--trace --vardir=\"#{get_test_file_path(agent, agent_var_dir)}\" " + "--no-daemonize --verbose --onetime --test --server #{master}") end end end end step "verify that the module files were synced down to the agent" do agents.each do |agent| unless test_file_exists?(agent, agent_module_app_file) then fail_test("Expected app file not synced to agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end unless test_file_exists?(agent, agent_module_face_file) then fail_test("Expected face file not synced to agent: '#{get_test_file_path(agent, agent_module_face_file)}'") end end end step "verify that the application shows up in help" do agents.each do |agent| on(agent, PuppetCommand.new(:help, "--vardir=\"#{get_test_file_path(agent, agent_var_dir)}\"")) do assert_match(/^\s+#{app_name}\s+#{app_desc % "face"}/, result.stdout) end end end step "verify that we can run the application" do agents.each do |agent| on(agent, PuppetCommand.new(:"#{app_name}", "--vardir=\"#{get_test_file_path(agent, agent_var_dir)}\"")) do assert_match(/^#{app_output % "face"}/, result.stdout) end end end step "clear out the libdir on the agents in preparation for the next test" do agents.each do |agent| on(agent, "rm -rf #{get_test_file_path(agent, agent_lib_dir)}/*") end end all_tests_passed = true ensure ########################################################################################## # Clean up all of the temp files created by this test. It would be nice if this logic # could be handled outside of the test itself; I envision a stanza like this one appearing # in a very large number of the tests going forward unless it is handled by the framework. ########################################################################################## if all_tests_passed then remove_temp_dirs() end -end \ No newline at end of file +end diff --git a/conf/windows/eventlog/puppetres.dll b/conf/windows/eventlog/puppetres.dll index f894500bc..b957eb83e 100755 Binary files a/conf/windows/eventlog/puppetres.dll and b/conf/windows/eventlog/puppetres.dll differ diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index 53ef36554..02893ebba 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1,1496 +1,1495 @@ # The majority of Puppet's configuration settings are set in this file. module Puppet ############################################################################################ # NOTE: For information about the available values for the ":type" property of settings, # see the docs for Settings.define_settings ############################################################################################ define_settings(:main, :confdir => { :default => nil, :type => :directory, :desc => "The main Puppet configuration directory. The default for this setting is calculated based on the user. If the process\n" + "is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it's running as any other user,\n" + "it defaults to being in the user's home directory.", }, :vardir => { :default => nil, :type => :directory, :desc => "Where Puppet stores dynamic and growing data. The default for this setting is calculated specially, like `confdir`_.", }, ### NOTE: this setting is usually being set to a symbol value. We don't officially have a ### setting type for that yet, but we might want to consider creating one. :name => { :default => nil, :desc => "The name of the application, if we are running as one. The\n" + "default is essentially $0 without the path or `.rb`.", }, ## This setting needs to go away. As a first step, we could just make it a first-class property of the Settings ## class, instead of treating it as a normal setting. There are places where the Settings class tries to use ## the value of run_mode to help in resolving other values, and that is no good for nobody. It would cause ## infinite recursion and stack overflows without some chicanery... so, it needs to be cleaned up. ## ## As a longer term goal I think we should be looking into getting rid of run_mode altogether, but that is going ## to be a larger undertaking, as it is being branched on in a lot of places in the current code. ## ## --cprice 2012-03-16 :run_mode => { :default => nil, :desc => "The effective 'run mode' of the application: master, agent, or user.", } ) define_settings(:main, :logdir => { :default => nil, :type => :directory, :mode => 0750, :owner => "service", :group => "service", :desc => "The directory in which to store log files", } ) define_settings(:main, :trace => { :default => false, :type => :boolean, :desc => "Whether to print stack traces on some errors", }, :autoflush => { :default => true, :type => :boolean, :desc => "Whether log files should always flush to disk.", :hook => proc { |value| Log.autoflush = value } }, :syslogfacility => { :default => "daemon", :desc => "What syslog facility to use when logging to\n" + "syslog. Syslog has a fixed list of valid facilities, and you must\n" + "choose one of those; you cannot just make one up." }, :statedir => { :default => "$vardir/state", :type => :directory, :mode => 01755, :desc => "The directory where Puppet state is stored. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)." }, :rundir => { :default => nil, :type => :directory, :mode => 01777, :desc => "Where Puppet PID files are kept." }, :genconfig => { :default => false, :type => :boolean, :desc => "Whether to just print a configuration to stdout and exit. Only makes\n" + "sense when used interactively. Takes into account arguments specified\n" + "on the CLI.", }, :genmanifest => { :default => false, :type => :boolean, :desc => "Whether to just print a manifest to stdout and exit. Only makes\n" + "sense when used interactively. Takes into account arguments specified\n" + "on the CLI.", }, :configprint => { :default => "", :desc => "Print the value of a specific configuration setting. If the name of a\n" + "setting is provided for this, then the value is printed and puppet\n" + "exits. Comma-separate multiple values. For a list of all values,\n" + "specify 'all'.", }, :color => { :default => "ansi", :type => :string, :desc => "Whether to use colors when logging to the console. Valid values are\n" + "`ansi` (equivalent to `true`), `html`, and `false`, which produces no color.\n" + "Defaults to false on Windows, as its console does not support ansi colors.", }, :mkusers => { :default => false, :type => :boolean, :desc => "Whether to create the necessary user and group that puppet agent will run as.", }, :manage_internal_file_permissions => { :default => true, :type => :boolean, :desc => "Whether Puppet should manage the owner, group, and mode of files it uses internally", }, :onetime => { :default => false, :type => :boolean, :desc => "Run the configuration once, rather than as a long-running\n" + "daemon. This is useful for interactively running puppetd.", :short => 'o', }, :path => { :default => "none", :desc => "The shell search path. Defaults to whatever is inherited\n" + "from the parent process.", :call_hook => :on_define_and_write, :hook => proc do |value| ENV["PATH"] = "" if ENV["PATH"].nil? ENV["PATH"] = value unless value == "none" paths = ENV["PATH"].split(File::PATH_SEPARATOR) %w{/usr/sbin /sbin}.each do |path| ENV["PATH"] += File::PATH_SEPARATOR + path unless paths.include?(path) end value end }, :libdir => { :type => :directory, :default => "$vardir/lib", :desc => "An extra search path for Puppet. This is only useful\n" + "for those files that Puppet will load on demand, and is only\n" + "guaranteed to work for those cases. In fact, the autoload\n" + "mechanism is responsible for making sure this directory\n" + "is in Ruby's search path\n", :call_hook => :on_initialize_and_write, :hook => proc do |value| $LOAD_PATH.delete(@oldlibdir) if defined?(@oldlibdir) and $LOAD_PATH.include?(@oldlibdir) @oldlibdir = value $LOAD_PATH << value end }, :ignoreimport => { :default => false, :type => :boolean, :desc => "If true, allows the parser to continue without requiring\n" + "all files referenced with `import` statements to exist. This setting was primarily\n" + "designed for use with commit hooks for parse-checking.", }, :authconfig => { :default => "$confdir/namespaceauth.conf", :desc => "The configuration file that defines the rights to the different\n" + "namespaces and methods. This can be used as a coarse-grained\n" + "authorization system for both `puppet agent` and `puppet master`.", }, :environment => { :default => "production", :desc => "The environment Puppet is running in. For clients\n" + "(e.g., `puppet agent`) this determines the environment itself, which\n" + "is used to find modules and much more. For servers (i.e., `puppet master`)\n" + "this provides the default environment for nodes we know nothing about." }, :diff_args => { :default => "-u", :desc => "Which arguments to pass to the diff command when printing differences between\n" + "files. The command to use can be chosen with the `diff` setting.", }, :diff => { :default => (Puppet.features.microsoft_windows? ? "" : "diff"), :desc => "Which diff command to use when printing differences between files. This setting\n" + "has no default value on Windows, as standard `diff` is not available, but Puppet can use many\n" + "third-party diff tools.", }, :show_diff => { :type => :boolean, :default => false, :desc => "Whether to log and report a contextual diff when files are being replaced. This causes\n" + "partial file contents to pass through Puppet's normal logging and reporting system, so this setting\n" + "should be used with caution if you are sending Puppet's reports to an insecure destination.\n" + "This feature currently requires the `diff/lcs` Ruby library.", }, :daemonize => { :type => :boolean, :default => (Puppet.features.microsoft_windows? ? false : true), :desc => "Whether to send the process into the background. This defaults to true on POSIX systems, and to false on Windows (where Puppet currently cannot daemonize).", :short => "D", :hook => proc do |value| if value and Puppet.features.microsoft_windows? raise "Cannot daemonize on Windows" end end }, :maximum_uid => { :default => 4294967290, :desc => "The maximum allowed UID. Some platforms use negative UIDs\n" + "but then ship with tools that do not know how to handle signed ints, so the UIDs show up as\n" + "huge numbers that can then not be fed back into the system. This is a hackish way to fail in a\n" + "slightly more useful way when that happens.", }, :route_file => { :default => "$confdir/routes.yaml", :desc => "The YAML file containing indirector route configuration.", }, :node_terminus => { :default => "plain", :desc => "Where to find information about nodes.", }, :data_binding_terminus => { :default => "hiera", :desc => "Where to retrive information about data.", }, :hiera_config => { :default => "$confdir/hiera.yaml", :desc => "The hiera configuration file", :type => :file, }, :catalog_terminus => { :default => "compiler", :desc => "Where to get node catalogs. This is useful to change if, for instance, you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store.", }, :facts_terminus => { :default => 'facter', :desc => "The node facts terminus.", :hook => proc do |value| require 'puppet/node/facts' # Cache to YAML if we're uploading facts away if %w[rest inventory_service].include? value.to_s Puppet::Node::Facts.indirection.cache_class = :yaml end end }, :inventory_terminus => { :default => "$facts_terminus", :desc => "Should usually be the same as the facts terminus", }, :default_file_terminus => { :default => "rest", :desc => "The default source for files if no server is given in a uri, e.g. puppet:///file. The default of `rest` causes the file to be retrieved using the `server` setting. When running `apply` the default is `file_server`, causing requests to be filled locally." }, :httplog => { :default => "$logdir/http.log", :type => :file, :owner => "root", :mode => 0640, :desc => "Where the puppet agent web server logs.", }, :http_proxy_host => { :default => "none", :desc => "The HTTP proxy host to use for outgoing connections. Note: You may need to use a FQDN for the server hostname when using a proxy.", }, :http_proxy_port => { :default => 3128, :desc => "The HTTP proxy port to use for outgoing connections", }, :filetimeout => { :default => 15, :desc => "The minimum time to wait (in seconds) between checking for updates in configuration files. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk.", }, :queue_type => { :default => "stomp", :desc => "Which type of queue to use for asynchronous processing.", }, :queue_type => { :default => "stomp", :desc => "Which type of queue to use for asynchronous processing.", }, :queue_source => { :default => "stomp://localhost:61613/", :desc => "Which type of queue to use for asynchronous processing. If your stomp server requires authentication, you can include it in the URI as long as your stomp client library is at least 1.1.1", }, :async_storeconfigs => { :default => false, :type => :boolean, :desc => "Whether to use a queueing system to provide asynchronous database integration. Requires that `puppet queue` be running and that 'PSON' support for ruby be installed.", :hook => proc do |value| if value # This reconfigures the terminii for Node, Facts, and Catalog Puppet.settings[:storeconfigs] = true # But then we modify the configuration Puppet::Resource::Catalog.indirection.cache_class = :queue else raise "Cannot disable asynchronous storeconfigs in a running process" end end }, :thin_storeconfigs => { :default => false, :type => :boolean, :desc => "Boolean; whether storeconfigs store in the database only the facts and exported resources. If true, then storeconfigs performance will be higher and still allow exported/collected resources, but other usage external to Puppet might not work", :hook => proc do |value| Puppet.settings[:storeconfigs] = true if value end }, :config_version => { :default => "", :desc => "How to determine the configuration version. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server.", }, :zlib => { :default => true, :type => :boolean, :desc => "Boolean; whether to use the zlib library", }, :prerun_command => { :default => "", :desc => "A command to run before every agent run. If this command returns a non-zero return code, the entire Puppet run will fail.", }, :postrun_command => { :default => "", :desc => "A command to run after every agent run. If this command returns a non-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run.", }, :freeze_main => { :default => false, :type => :boolean, :desc => "Freezes the 'main' class, disallowing any code to be added to it. This\n" + "essentially means that you can't have any code outside of a node, class, or definition other\n" + "than in the site manifest.", } ) Puppet.define_settings(:module_tool, :module_repository => { :default => 'http://forge.puppetlabs.com', :desc => "The module repository", }, :module_working_dir => { :default => '$vardir/puppet-module', :desc => "The directory into which module tool data is stored", } ) Puppet.define_settings( :main, # We have to downcase the fqdn, because the current ssl stuff (as oppsed to in master) doesn't have good facilities for # manipulating naming. :certname => { :default => Puppet::Settings.default_certname.downcase, :desc => "The name to use when handling certificates. Defaults to the fully qualified domain name.", :call_hook => :on_define_and_write, # Call our hook with the default value, so we're always downcased :hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }}, :certdnsnames => { :default => '', :hook => proc do |value| unless value.nil? or value == '' then Puppet.warning < < { :default => '', :desc => < { :default => "$ssldir/certs", :type => :directory, :owner => "service", :desc => "The certificate directory." }, :ssldir => { :default => "$confdir/ssl", :type => :directory, :mode => 0771, :owner => "service", :desc => "Where SSL certificates are kept." }, :publickeydir => { :default => "$ssldir/public_keys", :type => :directory, :owner => "service", :desc => "The public key directory." }, :requestdir => { :default => "$ssldir/certificate_requests", - :type => :directory, :type => :directory, :owner => "service", :desc => "Where host certificate requests are stored." }, :privatekeydir => { :default => "$ssldir/private_keys", :type => :directory, :mode => 0750, :owner => "service", :desc => "The private key directory." }, :privatedir => { :default => "$ssldir/private", :type => :directory, :mode => 0750, :owner => "service", :desc => "Where the client stores private certificate information." }, :passfile => { :default => "$privatedir/password", :type => :file, :mode => 0640, :owner => "service", :desc => "Where puppet agent stores the password for its private key. Generally unused." }, :hostcsr => { :default => "$ssldir/csr_$certname.pem", :type => :file, :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificate requests." }, :hostcert => { :default => "$certdir/$certname.pem", :type => :file, :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificates." }, :hostprivkey => { :default => "$privatekeydir/$certname.pem", :type => :file, :mode => 0600, :owner => "service", :desc => "Where individual hosts store and look for their private key." }, :hostpubkey => { :default => "$publickeydir/$certname.pem", :type => :file, :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their public key." }, :localcacert => { :default => "$certdir/ca.pem", :type => :file, :mode => 0644, :owner => "service", :desc => "Where each client stores the CA certificate." }, :hostcrl => { :default => "$ssldir/crl.pem", :type => :file, :mode => 0644, :owner => "service", :desc => "Where the host's certificate revocation list can be found. This is distinct from the certificate authority's CRL." }, :certificate_revocation => { :default => true, :type => :boolean, :desc => "Whether certificate revocation should be supported by downloading a Certificate Revocation List (CRL) to all clients. If enabled, CA chaining will almost definitely not work.", } ) define_settings( :ca, :ca_name => { :default => "Puppet CA: $certname", :desc => "The name to use the Certificate Authority certificate.", }, :cadir => { :default => "$ssldir/ca", :type => :directory, :owner => "service", :group => "service", :mode => 0770, :desc => "The root directory for the certificate authority." }, :cacert => { :default => "$cadir/ca_crt.pem", :type => :file, :owner => "service", :group => "service", :mode => 0660, :desc => "The CA certificate." }, :cakey => { :default => "$cadir/ca_key.pem", :type => :file, :owner => "service", :group => "service", :mode => 0660, :desc => "The CA private key." }, :capub => { :default => "$cadir/ca_pub.pem", :type => :file, :owner => "service", :group => "service", :desc => "The CA public key." }, :cacrl => { :default => "$cadir/ca_crl.pem", :type => :file, :owner => "service", :group => "service", :mode => 0664, :desc => "The certificate revocation list (CRL) for the CA. Will be used if present but otherwise ignored.", :hook => proc do |value| if value == 'false' Puppet.deprecation_warning "Setting the :cacrl to 'false' is deprecated; Puppet will just ignore the crl if yours is missing" end end }, :caprivatedir => { :default => "$cadir/private", :type => :directory, :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores private certificate information." }, :csrdir => { :default => "$cadir/requests", :type => :directory, :owner => "service", :group => "service", :desc => "Where the CA stores certificate requests" }, :signeddir => { :default => "$cadir/signed", :type => :directory, :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores signed certificates." }, :capass => { :default => "$caprivatedir/ca.pass", :type => :file, :owner => "service", :group => "service", :mode => 0660, :desc => "Where the CA stores the password for the private key" }, :serial => { :default => "$cadir/serial", :type => :file, :owner => "service", :group => "service", :mode => 0644, :desc => "Where the serial number for certificates is stored." }, :autosign => { :default => "$confdir/autosign.conf", :type => :file, :mode => 0644, :desc => "Whether to enable autosign. Valid values are true (which autosigns any key request, and is a very bad idea), false (which never autosigns any key request), and the path to a file, which uses that configuration file to determine which keys to sign."}, :allow_duplicate_certs => { :default => false, :type => :boolean, :desc => "Whether to allow a new certificate request to overwrite an existing certificate.", }, :ca_days => { :default => "", :desc => "How long a certificate should be valid, in days. This setting is deprecated; use `ca_ttl` instead", }, :ca_ttl => { :default => "5y", :desc => "The default TTL for new certificates; valid values must be an integer, optionally followed by one of the units 'y' (years of 365 days), 'd' (days), 'h' (hours), or 's' (seconds). The unit defaults to seconds. If this setting is set, ca_days is ignored. Examples are '3600' (one hour) and '1825d', which is the same as '5y' (5 years) ", }, :ca_md => { :default => "md5", :desc => "The type of hash used in certificates.", }, :req_bits => { :default => 4096, :desc => "The bit length of the certificates.", }, :keylength => { :default => 4096, :desc => "The bit length of keys.", }, :cert_inventory => { :default => "$cadir/inventory.txt", :type => :file, :mode => 0644, :owner => "service", :group => "service", :desc => "A Complete listing of all certificates" } ) # Define the config default. define_settings(:application, :config_file_name => { :type => :string, :default => Puppet::Settings.default_config_file_name, :desc => "The name of the puppet config file.", }, :config => { :type => :file, :default => "$confdir/${config_file_name}", :desc => "The configuration file for the current puppet application", }, :pidfile => { :type => :file, :default => "$rundir/${run_mode}.pid", :desc => "The pid file", }, :bindaddress => { :default => "", :desc => "The address a listening server should bind to. Mongrel servers default to 127.0.0.1 and WEBrick defaults to 0.0.0.0.", }, :servertype => { :default => "webrick", :desc => "The type of server to use. Currently supported options are webrick and mongrel. If you use mongrel, you will need a proxy in front of the process or processes, since Mongrel cannot speak SSL.", :call_hook => :on_define_and_write, # Call our hook with the default value, so we always get the correct bind address set. :hook => proc { |value| value == "webrick" ? Puppet.settings[:bindaddress] = "0.0.0.0" : Puppet.settings[:bindaddress] = "127.0.0.1" if Puppet.settings[:bindaddress] == "" } } ) define_settings(:master, :user => { :default => "puppet", :desc => "The user puppet master should run as.", }, :group => { :default => "puppet", :desc => "The group puppet master should run as.", }, :manifestdir => { :default => "$confdir/manifests", :type => :directory, :desc => "Where puppet master looks for its manifests.", }, :manifest => { :default => "$manifestdir/site.pp", :type => :file, :desc => "The entry-point manifest for puppet master.", }, :code => { :default => "", :desc => "Code to parse directly. This is essentially only used by `puppet`, and should only be set if you're writing your own Puppet executable", }, :masterlog => { :default => "$logdir/puppetmaster.log", :type => :file, :owner => "service", :group => "service", :mode => 0660, :desc => "Where puppet master logs. This is generally not used, since syslog is the default log destination." }, :masterhttplog => { :default => "$logdir/masterhttp.log", :type => :file, :owner => "service", :group => "service", :mode => 0660, :create => true, :desc => "Where the puppet master web server logs." }, :masterport => { :default => 8140, :desc => "Which port puppet master listens on.", }, :node_name => { :default => "cert", :desc => "How the puppet master determines the client's identity and sets the 'hostname', 'fqdn' and 'domain' facts for use in the manifest, in particular for determining which 'node' statement applies to the client. Possible values are 'cert' (use the subject's CN in the client's certificate) and 'facter' (use the hostname that the client reported in its facts)", }, :bucketdir => { :default => "$vardir/bucket", :type => :directory, :mode => 0750, :owner => "service", :group => "service", :desc => "Where FileBucket files are stored." }, :rest_authconfig => { :default => "$confdir/auth.conf", :type => :file, :desc => "The configuration file that defines the rights to the different rest indirections. This can be used as a fine-grained authorization system for `puppet master`.", }, :ca => { :default => true, :type => :boolean, :desc => "Whether the master should function as a certificate authority.", }, :modulepath => { :default => "$confdir/modules#{File::PATH_SEPARATOR}/usr/share/puppet/modules", :type => :path, :desc => "The search path for modules, as a list of directories separated by the system path separator character. " + "(The POSIX path separator is ':', and the Windows path separator is ';'.)", }, :ssl_client_header => { :default => "HTTP_X_CLIENT_DN", :desc => "The header containing an authenticated client's SSL DN. Only used with Mongrel. This header must be set by the proxy to the authenticated client's SSL DN (e.g., `/CN=puppet.puppetlabs.com`). See http://projects.puppetlabs.com/projects/puppet/wiki/Using_Mongrel for more information.", }, :ssl_client_verify_header => { :default => "HTTP_X_CLIENT_VERIFY", :desc => "The header containing the status message of the client verification. Only used with Mongrel. This header must be set by the proxy to 'SUCCESS' if the client successfully authenticated, and anything else otherwise. See http://projects.puppetlabs.com/projects/puppet/wiki/Using_Mongrel for more information.", }, # To make sure this directory is created before we try to use it on the server, we need # it to be in the server section (#1138). :yamldir => { :default => "$vardir/yaml", :type => :directory, :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which YAML data is stored, usually in a subdirectory."}, :server_datadir => { :default => "$vardir/server_data", :type => :directory, :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which serialized data is stored, usually in a subdirectory."}, :reports => { :default => "store", :desc => "The list of reports to generate. All reports are looked for in `puppet/reports/name.rb`, and multiple report names should be comma-separated (whitespace is okay).", }, :reportdir => { :default => "$vardir/reports", :type => :directory, :mode => 0750, :owner => "service", :group => "service", :desc => "The directory in which to store reports received from the client. Each client gets a separate subdirectory."}, :reporturl => { :default => "http://localhost:3000/reports/upload", :desc => "The URL used by the http reports processor to send reports", }, :fileserverconfig => { :default => "$confdir/fileserver.conf", :type => :file, :desc => "Where the fileserver configuration is stored.", }, :strict_hostname_checking => { :default => false, :desc => "Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs.", } ) define_settings(:metrics, :rrddir => { :type => :directory, :default => "$vardir/rrd", :mode => 0750, :owner => "service", :group => "service", :desc => "The directory where RRD database files are stored. Directories for each reporting host will be created under this directory." }, :rrdinterval => { :default => "$runinterval", :desc => "How often RRD should expect data. This should match how often the hosts report back to the server.", } ) define_settings(:device, :devicedir => { :default => "$vardir/devices", :type => :directory, :mode => "750", :desc => "The root directory of devices' $vardir", }, :deviceconfig => { :default => "$confdir/device.conf", :desc => "Path to the device config file for puppet device", } ) define_settings(:agent, :node_name_value => { :default => "$certname", :desc => "The explicit value used for the node name for all requests the agent makes to the master. WARNING: This setting is mutually exclusive with node_name_fact. Changing this setting also requires changes to the default auth.conf configuration on the Puppet Master. Please see http://links.puppetlabs.com/node_name_value for more information." }, :node_name_fact => { :default => "", :desc => "The fact name used to determine the node name used for all requests the agent makes to the master. WARNING: This setting is mutually exclusive with node_name_value. Changing this setting also requires changes to the default auth.conf configuration on the Puppet Master. Please see http://links.puppetlabs.com/node_name_fact for more information.", :hook => proc do |value| if !value.empty? and Puppet[:node_name_value] != Puppet[:certname] raise "Cannot specify both the node_name_value and node_name_fact settings" end end }, :localconfig => { :default => "$statedir/localconfig", :type => :file, :owner => "root", :mode => 0660, :desc => "Where puppet agent caches the local configuration. An extension indicating the cache format is added automatically."}, :statefile => { :default => "$statedir/state.yaml", :type => :file, :mode => 0660, :desc => "Where puppet agent and puppet master store state associated with the running configuration. In the case of puppet master, this file reflects the state discovered through interacting with clients." }, :clientyamldir => { :default => "$vardir/client_yaml", :type => :directory, :mode => "750", :desc => "The directory in which client-side YAML data is stored." }, :client_datadir => { :default => "$vardir/client_data", :type => :directory, :mode => "750", :desc => "The directory in which serialized data is stored on the client." }, :classfile => { :default => "$statedir/classes.txt", :type => :file, :owner => "root", :mode => 0644, :desc => "The file in which puppet agent stores a list of the classes associated with the retrieved configuration. Can be loaded in the separate `puppet` executable using the `--loadclasses` option."}, :resourcefile => { :default => "$statedir/resources.txt", :type => :file, :owner => "root", :mode => 0644, :desc => "The file in which puppet agent stores a list of the resources associated with the retrieved configuration." }, :puppetdlog => { :default => "$logdir/puppetd.log", :type => :file, :owner => "root", :mode => 0640, :desc => "The log file for puppet agent. This is generally not used." }, :server => { :default => "puppet", :desc => "The server to which the puppet agent should connect" }, :use_srv_records => { :default => false, :type => :boolean, :desc => "Whether the server will search for SRV records in DNS for the current domain.", }, :srv_domain => { :default => "#{Puppet::Settings.domain_fact}", :desc => "The domain which will be queried to find the SRV records of servers to use.", }, :ignoreschedules => { :default => false, :type => :boolean, :desc => "Boolean; whether puppet agent should ignore schedules. This is useful for initial puppet agent runs.", }, :puppetport => { :default => 8139, :desc => "Which port puppet agent listens on.", }, :noop => { :default => false, :type => :boolean, :desc => "Whether puppet agent should be run in noop mode.", }, :runinterval => { :default => 1800, # 30 minutes :desc => "How often puppet agent applies the client configuration; in seconds. Note that a runinterval of 0 means \"run continuously\" rather than \"never run.\" If you want puppet agent to never run, you should start it with the `--no-client` option.", }, :listen => { :default => false, :type => :boolean, :desc => "Whether puppet agent should listen for connections. If this is true, then puppet agent will accept incoming REST API requests, subject to the default ACLs and the ACLs set in the `rest_authconfig` file. Puppet agent can respond usefully to requests on the `run`, `facts`, `certificate`, and `resource` endpoints.", }, :ca_server => { :default => "$server", :desc => "The server to use for certificate authority requests. It's a separate server because it cannot and does not need to horizontally scale.", }, :ca_port => { :default => "$masterport", :desc => "The port to use for the certificate authority.", }, :catalog_format => { :default => "", :desc => "(Deprecated for 'preferred_serialization_format') What format to use to dump the catalog. Only supports 'marshal' and 'yaml'. Only matters on the client, since it asks the server for a specific format.", :hook => proc { |value| if value Puppet.deprecation_warning "Setting 'catalog_format' is deprecated; use 'preferred_serialization_format' instead." Puppet.settings[:preferred_serialization_format] = value end } }, :preferred_serialization_format => { :default => "pson", :desc => "The preferred means of serializing ruby instances for passing over the wire. This won't guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it.", }, :agent_pidfile => { :default => "$statedir/agent.pid", :type => :file, :desc => "A lock file to indicate that a puppet agent run is currently in progress. File contains the pid of the running process.", }, :agent_disabled_lockfile => { :default => "$statedir/agent_disabled.lock", :type => :file, :desc => "A lock file to indicate that puppet agent runs have been administratively disabled. File contains a JSON object with state information.", }, :usecacheonfailure => { :default => true, :type => :boolean, :desc => "Whether to use the cached configuration when the remote configuration will not compile. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known-good one.", }, :use_cached_catalog => { :default => false, :type => :boolean, :desc => "Whether to only use the cached catalog rather than compiling a new catalog on every run. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired.", }, :ignorecache => { :default => false, :type => :boolean, :desc => "Ignore cache and always recompile the configuration. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date - if the facts change or if the server changes.", }, :downcasefacts => { :default => false, :type => :boolean, :desc => "Whether facts should be made all lowercase when sent to the server.", }, :dynamicfacts => { :default => "memorysize,memoryfree,swapsize,swapfree", :desc => "Facts that are dynamic; these facts will be ignored when deciding whether changed facts should result in a recompile. Multiple facts should be comma-separated.", }, :splaylimit => { :default => "$runinterval", :desc => "The maximum time to delay before runs. Defaults to being the same as the run interval.", }, :splay => { :default => false, :type => :boolean, :desc => "Whether to sleep for a pseudo-random (but consistent) amount of time before a run.", }, :clientbucketdir => { :default => "$vardir/clientbucket", :type => :directory, :mode => 0750, :desc => "Where FileBucket files are stored locally." }, :configtimeout => { :default => 120, :desc => "How long the client should wait for the configuration to be retrieved before considering it a failure. This can help reduce flapping if too many clients contact the server at one time.", }, :reportserver => { :default => "$server", :call_hook => :on_write_only, :desc => "(Deprecated for 'report_server') The server to which to send transaction reports.", :hook => proc do |value| Puppet.settings[:report_server] = value if value end }, :report_server => { :default => "$server", :desc => "The server to send transaction reports to.", }, :report_port => { :default => "$masterport", :desc => "The port to communicate with the report_server.", }, :inventory_server => { :default => "$server", :desc => "The server to send facts to.", }, :inventory_port => { :default => "$masterport", :desc => "The port to communicate with the inventory_server.", }, :report => { :default => true, :type => :boolean, :desc => "Whether to send reports after every transaction.", }, :lastrunfile => { :default => "$statedir/last_run_summary.yaml", :type => :file, :mode => 0644, :desc => "Where puppet agent stores the last run report summary in yaml format." }, :lastrunreport => { :default => "$statedir/last_run_report.yaml", :type => :file, :mode => 0644, :desc => "Where puppet agent stores the last run report in yaml format." }, :graph => { :default => false, :type => :boolean, :desc => "Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick).", }, :graphdir => { :default => "$statedir/graphs", :type => :directory, :desc => "Where to store dot-outputted graphs.", }, :http_compression => { :default => false, :type => :boolean, :desc => "Allow http compression in REST communication with the master. This setting might improve performance for agent -> master communications over slow WANs. Your puppet master needs to support compression (usually by activating some settings in a reverse-proxy in front of the puppet master, which rules out webrick). It is harmless to activate this settings if your master doesn't support compression, but if it supports it, this setting might reduce performance on high-speed LANs.", }, :waitforcert => { :default => 120, # 2 minutes :desc => "The time interval, specified in seconds, 'puppet agent' should connect to the server and ask it to sign a certificate request. This is useful for the initial setup of a puppet client. You can turn off waiting for certificates by specifying a time of 0.", } ) define_settings(:inspect, :archive_files => { :type => :boolean, :default => false, :desc => "During an inspect run, whether to archive files whose contents are audited to a file bucket.", }, :archive_file_server => { :default => "$server", :desc => "During an inspect run, the file bucket server to archive files to if archive_files is set.", } ) # Plugin information. define_settings( :main, :plugindest => { :type => :directory, :default => "$libdir", :desc => "Where Puppet should store plugins that it pulls down from the central server.", }, :pluginsource => { :default => "puppet://$server/plugins", :desc => "From where to retrieve plugins. The standard Puppet `file` type is used for retrieval, so anything that is a valid file source can be used here.", }, :pluginsync => { :default => true, :type => :boolean, :desc => "Whether plugins should be synced with the central server.", }, :pluginsignore => { :default => ".svn CVS .git", :desc => "What files to ignore when pulling down plugins.", } ) # Central fact information. define_settings( :main, :factpath => { :type => :path, :default => "$vardir/lib/facter#{File::PATH_SEPARATOR}$vardir/facts", :desc => "Where Puppet should look for facts. Multiple directories should be separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.)", :call_hook => :on_initialize_and_write, # Call our hook with the default value, so we always get the value added to facter. :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }} ) define_settings( :tagmail, :tagmap => { :default => "$confdir/tagmail.conf", :desc => "The mapping between reporting tags and email addresses.", }, :sendmail => { :default => which('sendmail') || '', :desc => "Where to find the sendmail binary with which to send email.", }, :reportfrom => { :default => "report@" + [Facter["hostname"].value,Facter["domain"].value].join("."), :desc => "The 'from' email address for the reports.", }, :smtpserver => { :default => "none", :desc => "The server through which to send email reports.", } ) define_settings( :rails, :dblocation => { :default => "$statedir/clientconfigs.sqlite3", :type => :file, :mode => 0660, :owner => "service", :group => "service", :desc => "The database cache for client configurations. Used for querying within the language." }, :dbadapter => { :default => "sqlite3", :desc => "The type of database to use.", }, :dbmigrate => { :default => false, :type => :boolean, :desc => "Whether to automatically migrate the database.", }, :dbname => { :default => "puppet", :desc => "The name of the database to use.", }, :dbserver => { :default => "localhost", :desc => "The database server for caching. Only used when networked databases are used.", }, :dbport => { :default => "", :desc => "The database password for caching. Only used when networked databases are used.", }, :dbuser => { :default => "puppet", :desc => "The database user for caching. Only used when networked databases are used.", }, :dbpassword => { :default => "puppet", :desc => "The database password for caching. Only used when networked databases are used.", }, :dbconnections => { :default => '', :desc => "The number of database connections for networked databases. Will be ignored unless the value is a positive integer.", }, :dbsocket => { :default => "", :desc => "The database socket location. Only used when networked databases are used. Will be ignored if the value is an empty string.", }, :railslog => { :default => "$logdir/rails.log", :type => :file, :mode => 0600, :owner => "service", :group => "service", :desc => "Where Rails-specific logs are sent" }, :rails_loglevel => { :default => "info", :desc => "The log level for Rails connections. The value must be a valid log level within Rails. Production environments normally use `info` and other environments normally use `debug`.", } ) define_settings( :couchdb, :couchdb_url => { :default => "http://127.0.0.1:5984/puppet", :desc => "The url where the puppet couchdb database will be created", } ) define_settings( :transaction, :tags => { :default => "", :desc => "Tags to use to find resources. If this is set, then only resources tagged with the specified tags will be applied. Values must be comma-separated.", }, :evaltrace => { :default => false, :type => :boolean, :desc => "Whether each resource should log when it is being evaluated. This allows you to interactively see exactly what is being done.", }, :summarize => { :default => false, :type => :boolean, :desc => "Whether to print a transaction summary.", } ) define_settings( :main, :external_nodes => { :default => "none", :desc => "An external command that can produce node information. The command's output must be a YAML dump of a hash, and that hash must have a `classes` key and/or a `parameters` key, where `classes` is an array or hash and `parameters` is a hash. For unknown nodes, the command should exit with a non-zero exit code. This command makes it straightforward to store your node mapping information in other data sources like databases.", } ) define_settings( :ldap, :ldapnodes => { :default => false, :type => :boolean, :desc => "Whether to search for node configurations in LDAP. See http://projects.puppetlabs.com/projects/puppet/wiki/LDAP_Nodes for more information.", }, :ldapssl => { :default => false, :type => :boolean, :desc => "Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side.", }, :ldaptls => { :default => false, :type => :boolean, :desc => "Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side.", }, :ldapserver => { :default => "ldap", :desc => "The LDAP server. Only used if `ldapnodes` is enabled.", }, :ldapport => { :default => 389, :desc => "The LDAP port. Only used if `ldapnodes` is enabled.", }, :ldapstring => { :default => "(&(objectclass=puppetClient)(cn=%s))", :desc => "The search string used to find an LDAP node.", }, :ldapclassattrs => { :default => "puppetclass", :desc => "The LDAP attributes to use to define Puppet classes. Values should be comma-separated.", }, :ldapstackedattrs => { :default => "puppetvar", :desc => "The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree. Values should be comma-separated.", }, :ldapattrs => { :default => "all", :desc => "The LDAP attributes to include when querying LDAP for nodes. All returned attributes are set as variables in the top-level scope. Multiple values should be comma-separated. The value 'all' returns all attributes.", }, :ldapparentattr => { :default => "parentnode", :desc => "The attribute to use to define the parent node.", }, :ldapuser => { :default => "", :desc => "The user to use to connect to LDAP. Must be specified as a full DN.", }, :ldappassword => { :default => "", :desc => "The password to use to connect to LDAP.", }, :ldapbase => { :default => "", :desc => "The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory.", } ) define_settings(:master, :storeconfigs => { :default => false, :type => :boolean, :desc => "Whether to store each client's configuration, including catalogs, facts, and related data. This also enables the import and export of resources in the Puppet language - a mechanism for exchange resources between nodes. By default this uses ActiveRecord and an SQL database to store and query the data; this, in turn, will depend on Rails being available. You can adjust the backend using the storeconfigs_backend setting.", # Call our hook with the default value, so we always get the libdir set. :call_hook => :on_initialize_and_write, :hook => proc do |value| require 'puppet/node' require 'puppet/node/facts' if value Puppet.settings[:async_storeconfigs] or Puppet::Resource::Catalog.indirection.cache_class = :store_configs Puppet::Node::Facts.indirection.cache_class = :store_configs Puppet::Node.indirection.cache_class = :store_configs Puppet::Resource.indirection.terminus_class = :store_configs end end }, :storeconfigs_backend => { :default => "active_record", :desc => "Configure the backend terminus used for StoreConfigs. By default, this uses the ActiveRecord store, which directly talks to the database from within the Puppet Master process." } ) # This doesn't actually work right now. define_settings( :parser, :lexical => { :default => false, :type => :boolean, :desc => "Whether to use lexical scoping (vs. dynamic).", }, :templatedir => { :default => "$vardir/templates", :type => :directory, :desc => "Where Puppet looks for template files. Can be a list of colon-separated directories.", } ) define_settings( :puppetdoc, :document_all => { :default => false, :type => :boolean, :desc => "Document all resources", } ) end diff --git a/lib/puppet/feature/facter.rb b/lib/puppet/feature/facter.rb index 5ba2f45f9..1cf4b0e06 100644 --- a/lib/puppet/feature/facter.rb +++ b/lib/puppet/feature/facter.rb @@ -1,30 +1,30 @@ require 'puppet/util/feature' require 'semver' # See if Facter is available, and check revision Puppet.features.add(:facter) do required_facter = "2.0.0" begin require 'facter' rescue LoadError => err begin require 'rubygems' require 'facter' rescue LoadError => err end end raise Puppet::Error, "Cannot find Facter class. Facter may not be installed, " + "or not be in your RUBYLIB." unless defined?(::Facter) raise Puppet::Error, "Cannot find Facter version declaration. Your " + "installation of Facter may be invalid, very old or this may be a bug." unless defined?(::Facter.version) facter_version = ::SemVer.new(::Facter.version) required_version = ::SemVer.new(required_facter) raise Puppet::Error, "Found Facter version #{::Facter.version} " + "however version #{required_facter} (or greater) is required for " + "Puppet to operate" unless facter_version >= required_version true -end \ No newline at end of file +end diff --git a/lib/puppet/util/config_timeout.rb b/lib/puppet/util/config_timeout.rb index 5da6507e6..6cd9fedad 100644 --- a/lib/puppet/util/config_timeout.rb +++ b/lib/puppet/util/config_timeout.rb @@ -1,24 +1,24 @@ module Puppet::Util::ConfigTimeout # NOTE: in the future it might be a good idea to add an explicit "integer" type to # the settings types, in which case this would no longer be necessary. # Get the value of puppet's "configtimeout" setting, as an integer. Raise an # ArgumentError if the setting does not contain a valid integer value. # @return Puppet config timeout setting value, as an integer def timeout_interval timeout = Puppet[:configtimeout] case timeout when String if timeout =~ /^\d+$/ timeout = Integer(timeout) else raise ArgumentError, "Configuration timeout must be an integer" end when Integer # nothing else raise ArgumentError, "Configuration timeout must be an integer" end timeout end -end \ No newline at end of file +end diff --git a/lib/puppet/util/json_lockfile.rb b/lib/puppet/util/json_lockfile.rb index 523fa40c2..ee2603c4e 100644 --- a/lib/puppet/util/json_lockfile.rb +++ b/lib/puppet/util/json_lockfile.rb @@ -1,41 +1,41 @@ require 'puppet/util/lockfile' # This class provides a simple API for managing a lock file # whose contents are a serialized JSON object. In addition # to querying the basic state (#locked?) of the lock, managing # the lock (#lock, #unlock), the contents can be retrieved at # any time while the lock is held (#lock_data). This can be # used to store structured data (state messages, etc.) about # the lock. # # @see Puppet::Util::Lockfile class Puppet::Util::JsonLockfile < Puppet::Util::Lockfile # Lock the lockfile. You may optionally pass a data object, which will be # retrievable for the duration of time during which the file is locked. # # @param [Hash] lock_data an optional Hash of data to associate with the lock. # This may be used to store pids, descriptive messages, etc. The # data may be retrieved at any time while the lock is held by # calling the #lock_data method. NOTE that the JSON serialization # does NOT support Symbol objects--if you pass them in, they will be # serialized as Strings, so you should plan accordingly. # @return [boolean] true if lock is successfully acquired, false otherwise. def lock(lock_data = nil) return false if locked? super(lock_data.to_pson) end # Retrieve the (optional) lock data that was specified at the time the file # was locked. # @return [Object] the data object. Remember that the serialization does not # support Symbol objects, so if your data Object originally contained symbols, # they will be converted to Strings. def lock_data return nil unless file_locked? file_contents = super return nil if file_contents.nil? PSON.parse(file_contents) end -end \ No newline at end of file +end diff --git a/lib/puppet/util/lockfile.rb b/lib/puppet/util/lockfile.rb index 5c910db7f..75bc97213 100644 --- a/lib/puppet/util/lockfile.rb +++ b/lib/puppet/util/lockfile.rb @@ -1,62 +1,62 @@ # This class provides a simple API for managing a lock file # whose contents are an (optional) String. In addition # to querying the basic state (#locked?) of the lock, managing # the lock (#lock, #unlock), the contents can be retrieved at # any time while the lock is held (#lock_data). This can be # used to store pids, messages, etc. # # @see Puppet::Util::JsonLockfile class Puppet::Util::Lockfile attr_reader :file_path def initialize(file_path) @file_path = file_path end # Lock the lockfile. You may optionally pass a data object, which will be # retrievable for the duration of time during which the file is locked. # # @param [String] lock_data an optional String data object to associate # with the lock. This may be used to store pids, descriptive messages, # etc. The data may be retrieved at any time while the lock is held by # calling the #lock_data method. # @return [boolean] true if lock is successfully acquired, false otherwise. def lock(lock_data = nil) return false if locked? File.open(@file_path, 'w') { |fd| fd.print(lock_data) } true end def unlock if locked? File.unlink(@file_path) true else false end end def locked? # delegate logic to a more explicit private method file_locked? end # Retrieve the (optional) lock data that was specified at the time the file # was locked. # @return [String] the data object. def lock_data return File.read(@file_path) if file_locked? end # Private, internal utility method for encapsulating the logic about # whether or not the file is locked. This method can be called # by other methods in this class without as much risk of accidentally # being overridden by child classes. # @return [boolean] true if the file is locked, false if it is not. def file_locked?() File.exists? @file_path end private :file_locked? -end \ No newline at end of file +end diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb index 84acc28b2..ef43ef188 100755 --- a/spec/integration/application/apply_spec.rb +++ b/spec/integration/application/apply_spec.rb @@ -1,29 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet/application/apply' describe "apply" do include PuppetSpec::Files describe "when applying provided catalogs", :if => Puppet.features.pson? do it "should be able to apply catalogs provided in a file in pson" do file_to_create = tmpfile("pson_catalog") catalog = Puppet::Resource::Catalog.new resource = Puppet::Resource.new(:file, file_to_create, :parameters => {:content => "my stuff"}) catalog.add_resource resource manifest = tmpfile("manifest") File.open(manifest, "w") { |f| f.print catalog.to_pson } puppet = Puppet::Application[:apply] puppet.options[:catalog] = manifest puppet.apply File.should be_exist(file_to_create) File.read(file_to_create).should == "my stuff" end end end diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb index 98e8392a4..073d2b6c0 100755 --- a/spec/integration/application/doc_spec.rb +++ b/spec/integration/application/doc_spec.rb @@ -1,55 +1,55 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet/application/doc' describe Puppet::Application::Doc do include PuppetSpec::Files it "should not generate an error when module dir overlaps parent of site.pp (#4798)", :if => Puppet.features.rdoc1?, :unless => Puppet.features.microsoft_windows? do begin # Note: the directory structure below is more complex than it # needs to be, but it's representative of the directory structure # used in bug #4798. old_dir = Dir.getwd # Note: can't use chdir with a block because it will generate bogus warnings tmpdir = tmpfile('doc_spec') Dir.mkdir(tmpdir) Dir.chdir(tmpdir) site_file = 'site.pp' File.open(site_file, 'w') do |f| f.puts '# A comment' end modules_dir = 'modules' Dir.mkdir(modules_dir) rt_dir = File.join(modules_dir, 'rt') Dir.mkdir(rt_dir) manifests_dir = File.join(rt_dir, 'manifests') Dir.mkdir(manifests_dir) rt_file = File.join(manifests_dir, 'rt.pp') File.open(rt_file, 'w') do |f| f.puts '# A class' f.puts 'class foo { }' f.puts '# A definition' f.puts 'define bar { }' end puppet = Puppet::Application[:doc] Puppet[:modulepath] = modules_dir Puppet[:manifest] = site_file puppet.options[:mode] = :rdoc expect { puppet.run_command }.to exit_with 0 File.should be_exist('doc') ensure Dir.chdir(old_dir) end end it "should respect the -o option" do puppetdoc = Puppet::Application[:doc] puppetdoc.command_line.stubs(:args).returns(['foo', '-o', 'bar']) puppetdoc.parse_options puppetdoc.options[:outputdir].should == 'bar' end end diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb index abe877072..f413a3a21 100755 --- a/spec/integration/configurer_spec.rb +++ b/spec/integration/configurer_spec.rb @@ -1,77 +1,77 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/configurer' describe Puppet::Configurer do include PuppetSpec::Files describe "when downloading plugins" do it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do resource = Puppet::Type.type(:notify).new :name => "yay" Puppet::Type.type(:file).expects(:new).with { |args| args[:ignore] == Puppet[:pluginsignore].split(/\s+/) }.returns resource configurer = Puppet::Configurer.new configurer.stubs(:download_plugins?).returns true configurer.download_plugins end end describe "when running" do before(:each) do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(Puppet::Type.type(:notify).new(:title => "testing")) # Make sure we don't try to persist the local state after the transaction ran, # because it will fail during test (the state file is in an not existing directory) # and we need the transaction to be successful to be able to produce a summary report @catalog.host_config = false @configurer = Puppet::Configurer.new end it "should send a transaction report with valid data" do @configurer.stubs(:save_last_run_summary) Puppet::Transaction::Report.indirection.expects(:save).with do |report, x| report.time.class == Time and report.logs.length > 0 end Puppet[:report] = true @configurer.run :catalog => @catalog end it "should save a correct last run summary" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.indirection.stubs(:save) Puppet[:lastrunfile] = tmpfile("lastrunfile") Puppet.settings.setting(:lastrunfile).mode = 0666 Puppet[:report] = true # We only record integer seconds in the timestamp, and truncate # backwards, so don't use a more accurate timestamp in the test. # --daniel 2011-03-07 t1 = Time.now.tv_sec @configurer.run :catalog => @catalog, :report => report t2 = Time.now.tv_sec file_mode = Puppet.features.microsoft_windows? ? '100644' : '100666' File.stat(Puppet[:lastrunfile]).mode.to_s(8).should == file_mode summary = nil File.open(Puppet[:lastrunfile], "r") do |fd| summary = YAML.load(fd.read) end summary.should be_a(Hash) %w{time changes events resources}.each do |key| summary.should be_key(key) end summary["time"].should be_key("notify") summary["time"]["last_run"].should be_between(t1, t2) end end end diff --git a/spec/integration/defaults_spec.rb b/spec/integration/defaults_spec.rb index d65ec385e..c263a09ef 100755 --- a/spec/integration/defaults_spec.rb +++ b/spec/integration/defaults_spec.rb @@ -1,324 +1,324 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/defaults' require 'puppet/rails' describe "Puppet defaults" do describe "when setting the :factpath" do it "should add the :factpath to Facter's search paths" do Facter.expects(:search).with("/my/fact/path") Puppet.settings[:factpath] = "/my/fact/path" end end describe "when setting the :certname" do it "should fail if the certname is not downcased" do lambda { Puppet.settings[:certname] = "Host.Domain.Com" }.should raise_error(ArgumentError) end end describe "when setting :node_name_value" do it "should default to the value of :certname" do Puppet.settings[:certname] = 'blargle' Puppet.settings[:node_name_value].should == 'blargle' end end describe "when setting the :node_name_fact" do it "should fail when also setting :node_name_value" do lambda do Puppet.settings[:node_name_value] = "some value" Puppet.settings[:node_name_fact] = "some_fact" end.should raise_error("Cannot specify both the node_name_value and node_name_fact settings") end it "should not fail when using the default for :node_name_value" do lambda do Puppet.settings[:node_name_fact] = "some_fact" end.should_not raise_error end end describe "when :certdnsnames is set" do it "should not fail" do expect { Puppet[:certdnsnames] = 'fred:wilma' }.should_not raise_error end it "should warn the value is ignored" do Puppet.expects(:warning).with {|msg| msg =~ /CVE-2011-3872/ } Puppet[:certdnsnames] = 'fred:wilma' end end describe "when configuring the :crl" do it "should warn if :cacrl is set to false" do Puppet.expects(:warning) Puppet.settings[:cacrl] = 'false' end end describe "when setting the :catalog_format" do it "should log a deprecation notice" do Puppet.expects(:deprecation_warning) Puppet.settings[:catalog_format] = 'marshal' end it "should copy the value to :preferred_serialization_format" do Puppet.settings[:catalog_format] = 'marshal' Puppet.settings[:preferred_serialization_format].should == 'marshal' end end it "should have a clientyamldir setting" do Puppet.settings[:clientyamldir].should_not be_nil end it "should have different values for the yamldir and clientyamldir" do Puppet.settings[:yamldir].should_not == Puppet.settings[:clientyamldir] end it "should have a client_datadir setting" do Puppet.settings[:client_datadir].should_not be_nil end it "should have different values for the server_datadir and client_datadir" do Puppet.settings[:server_datadir].should_not == Puppet.settings[:client_datadir] end # See #1232 it "should not specify a user or group for the clientyamldir" do Puppet.settings.setting(:clientyamldir).owner.should be_nil Puppet.settings.setting(:clientyamldir).group.should be_nil end it "should use the service user and group for the yamldir" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:yamldir).owner.should == Puppet.settings[:user] Puppet.settings.setting(:yamldir).group.should == Puppet.settings[:group] end # See #1232 it "should not specify a user or group for the rundir" do Puppet.settings.setting(:rundir).owner.should be_nil Puppet.settings.setting(:rundir).group.should be_nil end it "should specify that the host private key should be owned by the service user" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:hostprivkey).owner.should == Puppet.settings[:user] end it "should specify that the host certificate should be owned by the service user" do Puppet.settings.stubs(:service_user_available?).returns true Puppet.settings.setting(:hostcert).owner.should == Puppet.settings[:user] end it "should use a bind address of ''" do Puppet.settings.clear Puppet.settings[:bindaddress].should == "" end [:modulepath, :factpath].each do |setting| it "should configure '#{setting}' not to be a file setting, so multi-directory settings are acceptable" do Puppet.settings.setting(setting).should be_instance_of(Puppet::Settings::PathSetting) end end it "should add /usr/sbin and /sbin to the path if they're not there" do Puppet::Util.withenv("PATH" => "/usr/bin:/usr/local/bin") do Puppet.settings[:path] = "none" # this causes it to ignore the setting ENV["PATH"].split(File::PATH_SEPARATOR).should be_include("/usr/sbin") ENV["PATH"].split(File::PATH_SEPARATOR).should be_include("/sbin") end end it "should default to pson for the preferred serialization format" do Puppet.settings.value(:preferred_serialization_format).should == "pson" end describe "when enabling storeconfigs" do before do Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet.features.stubs(:rails?).returns true end it "should set the Catalog cache class to :store_configs" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:store_configs) Puppet.settings[:storeconfigs] = true end it "should not set the Catalog cache class to :store_configs if asynchronous storeconfigs is enabled" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:store_configs).never Puppet.settings.expects(:value).with(:async_storeconfigs).returns true Puppet.settings[:storeconfigs] = true end it "should set the Facts cache class to :store_configs" do Puppet::Node::Facts.indirection.expects(:cache_class=).with(:store_configs) Puppet.settings[:storeconfigs] = true end it "should set the Node cache class to :store_configs" do Puppet::Node.indirection.expects(:cache_class=).with(:store_configs) Puppet.settings[:storeconfigs] = true end end describe "when enabling asynchronous storeconfigs" do before do Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet.features.stubs(:rails?).returns true end it "should set storeconfigs to true" do Puppet.settings[:async_storeconfigs] = true Puppet.settings[:storeconfigs].should be_true end it "should set the Catalog cache class to :queue" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:queue) Puppet.settings[:async_storeconfigs] = true end it "should set the Facts cache class to :store_configs" do Puppet::Node::Facts.indirection.expects(:cache_class=).with(:store_configs) Puppet.settings[:storeconfigs] = true end it "should set the Node cache class to :store_configs" do Puppet::Node.indirection.expects(:cache_class=).with(:store_configs) Puppet.settings[:storeconfigs] = true end end describe "when enabling thin storeconfigs" do before do Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet.features.stubs(:rails?).returns true end it "should set storeconfigs to true" do Puppet.settings[:thin_storeconfigs] = true Puppet.settings[:storeconfigs].should be_true end end it "should have a setting for determining the configuration version and should default to an empty string" do Puppet.settings[:config_version].should == "" end describe "when enabling reports" do it "should use the default server value when report server is unspecified" do Puppet.settings[:server] = "server" Puppet.settings[:report_server].should == "server" end it "should use the default masterport value when report port is unspecified" do Puppet.settings[:masterport] = "1234" Puppet.settings[:report_port].should == "1234" end it "should set report_server when reportserver is set" do Puppet.settings[:reportserver] = "reportserver" Puppet.settings[:report_server].should == "reportserver" end it "should use report_port when set" do Puppet.settings[:masterport] = "1234" Puppet.settings[:report_port] = "5678" Puppet.settings[:report_port].should == "5678" end it "should prefer report_server over reportserver" do Puppet.settings[:reportserver] = "reportserver" Puppet.settings[:report_server] = "report_server" Puppet.settings[:report_server].should == "report_server" end end it "should have a :caname setting that defaults to the cert name" do Puppet.settings[:certname] = "foo" Puppet.settings[:ca_name].should == "Puppet CA: foo" end it "should have a 'prerun_command' that defaults to the empty string" do Puppet.settings[:prerun_command].should == "" end it "should have a 'postrun_command' that defaults to the empty string" do Puppet.settings[:postrun_command].should == "" end it "should have a 'certificate_revocation' setting that defaults to true" do Puppet.settings[:certificate_revocation].should be_true end it "should have an http_compression setting that defaults to false" do Puppet.settings[:http_compression].should be_false end describe "reportdir" do subject { Puppet.settings[:reportdir] } it { should == "#{Puppet[:vardir]}/reports" } end describe "reporturl" do subject { Puppet.settings[:reporturl] } it { should == "http://localhost:3000/reports/upload" } end describe "when configuring color" do subject { Puppet.settings[:color] } it { should == "ansi" } end describe "daemonize" do it "should default to true", :unless => Puppet.features.microsoft_windows? do Puppet.settings[:daemonize].should == true end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should default to false" do Puppet.settings[:daemonize].should == false end it "should raise an error if set to true" do lambda { Puppet.settings[:daemonize] = true }.should raise_error(/Cannot daemonize on Windows/) end end end describe "diff" do it "should default to 'diff' on POSIX", :unless => Puppet.features.microsoft_windows? do Puppet.settings[:diff].should == 'diff' end it "should default to '' on Windows", :if => Puppet.features.microsoft_windows? do Puppet.settings[:diff].should == '' end end describe "when configuring hiera" do it "should have a hiera_config setting" do Puppet.settings[:hiera_config].should_not be_nil end end describe "when configuring the data_binding terminus" do it "should have a data_binding_terminus setting" do Puppet.settings[:data_binding_terminus].should_not be_nil end it "should be set to hiera by default" do Puppet.settings[:data_binding_terminus].should == 'hiera' end end end diff --git a/spec/integration/faces/ca_spec.rb b/spec/integration/faces/ca_spec.rb index 540dd8e73..9635d9014 100755 --- a/spec/integration/faces/ca_spec.rb +++ b/spec/integration/faces/ca_spec.rb @@ -1,359 +1,359 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:ca, '0.1.0'], :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before :each do Puppet.run_mode.stubs(:master?).returns(true) Puppet[:ca] = true Puppet[:ssldir] = tmpdir("face-ca-ssldir") Puppet::SSL::Host.ca_location = :only Puppet[:certificate_revocation] = true # This is way more intimate than I want to be with the implementation, but # there doesn't seem any other way to test this. --daniel 2011-07-18 Puppet::SSL::CertificateAuthority.stubs(:instance).returns( # ...and this actually does the directory creation, etc. Puppet::SSL::CertificateAuthority.new ) end def given_certificate_requests_for(*names) names.each do |name| Puppet::SSL::Host.new(name).generate_certificate_request end end def given_certificates_for(*names) names.each do |name| Puppet::SSL::Host.new(name).generate end end context "#verify" do it "should report if there is no certificate" do subject.verify('random-host').should == { :host => 'random-host', :valid => false, :error => 'Could not find a certificate for random-host' } end it "should report that it cannot find a certificate when there is only a request" do given_certificate_requests_for('random-host') subject.verify('random-host').should == { :host => 'random-host', :valid => false, :error => 'Could not find a certificate for random-host' } end it "should verify a signed certificate" do given_certificates_for('random-host') subject.verify('random-host').should == { :host => 'random-host', :valid => true } end it "should not verify a revoked certificate" do given_certificates_for('random-host') subject.revoke('random-host') subject.verify('random-host').should == { :host => 'random-host', :valid => false, :error => 'certificate revoked' } end it "should verify a revoked certificate if CRL use was turned off" do given_certificates_for('random-host') subject.revoke('random-host') Puppet[:certificate_revocation] = false subject.verify('random-host').should == { :host => 'random-host', :valid => true } end end context "#fingerprint" do it "should be nil if there is no certificate" do subject.fingerprint('random-host').should be_nil end it "should fingerprint a CSR" do given_certificate_requests_for('random-host') subject.fingerprint('random-host').should =~ /^[0-9A-F:]+$/ end it "should fingerprint a certificate" do given_certificates_for('random-host') subject.fingerprint('random-host').should =~ /^[0-9A-F:]+$/ end %w{md5 MD5 sha1 ShA1 SHA1 RIPEMD160 sha256 sha512}.each do |digest| it "should fingerprint with #{digest.inspect}" do given_certificates_for('random-host') subject.fingerprint('random-host', :digest => digest).should =~ /^[0-9A-F:]+$/ end it "should fingerprint with #{digest.to_sym} as a symbol" do given_certificates_for('random-host') subject.fingerprint('random-host', :digest => digest.to_sym). should =~ /^[0-9A-F:]+$/ end end end context "#print" do it "should be nil if there is no certificate" do subject.print('random-host').should be_nil end it "should return nothing if there is only a CSR" do given_certificate_requests_for('random-host') subject.print('random-host').should be_nil end it "should return the certificate content if there is a cert" do given_certificates_for('random-host') text = subject.print('random-host') text.should be_an_instance_of String text.should =~ /^Certificate:/ text.should =~ /Issuer: CN=Puppet CA: / text.should =~ /Subject: CN=random-host$/ end end context "#sign" do it "should report that there is no CSR" do subject.sign('random-host').should == 'Could not find certificate request for random-host' end it "should report that there is no CSR when even when there is a certificate" do given_certificates_for('random-host') subject.sign('random-host').should == 'Could not find certificate request for random-host' end it "should sign a CSR if one exists" do given_certificate_requests_for('random-host') subject.sign('random-host').should be_an_instance_of Puppet::SSL::Certificate list = subject.list(:signed => true) list.length.should == 1 list.first.name.should == 'random-host' end describe "when the CSR specifies DNS alt names" do let(:host) { Puppet::SSL::Host.new('someone') } before :each do host.generate_certificate_request(:dns_alt_names => 'some,alt,names') end it "should sign the CSR if DNS alt names are allowed" do subject.sign('someone', :allow_dns_alt_names => true) host.certificate.should be_a(Puppet::SSL::Certificate) end it "should refuse to sign the CSR if DNS alt names are not allowed" do certname = 'someone' expect do subject.sign(certname) end.to raise_error(Puppet::SSL::CertificateAuthority::CertificateSigningError, /CSR '#{certname}' contains subject alternative names \(.*\), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{certname}` to sign this request./i) host.certificate.should be_nil end end end context "#generate" do it "should generate a certificate if requested" do subject.list(:all => true).should == [] subject.generate('random-host') list = subject.list(:signed => true) list.length.should == 1 list.first.name.should == 'random-host' end it "should report if a CSR with that name already exists" do given_certificate_requests_for('random-host') subject.generate('random-host').should =~ /already has a certificate request/ end it "should report if the certificate with that name already exists" do given_certificates_for('random-host') subject.generate('random-host').should =~ /already has a certificate/ end it "should include the specified DNS alt names" do subject.generate('some-host', :dns_alt_names => 'some,alt,names') host = subject.list(:signed => true).first host.name.should == 'some-host' host.certificate.subject_alt_names.should =~ %w[DNS:some DNS:alt DNS:names DNS:some-host] subject.list(:pending => true).should be_empty end end context "#revoke" do it "should let the user know what went wrong when there is nothing to revoke" do subject.revoke('nonesuch').should == 'Nothing was revoked' end it "should revoke a certificate" do given_certificates_for('random-host') subject.revoke('random-host') found = subject.list(:all => true, :subject => 'random-host') subject.get_action(:list).when_rendering(:console).call(found). should =~ /^- random-host \([:0-9A-F]+\) \(certificate revoked\)/ end end context "#destroy" do it "should let the user know if nothing was deleted" do subject.destroy('nonesuch').should == "Nothing was deleted" end it "should destroy a CSR, if we have one" do given_certificate_requests_for('random-host') subject.destroy('random-host') subject.list(:pending => true, :subject => 'random-host').should == [] end it "should destroy a certificate, if we have one" do given_certificates_for('random-host') subject.destroy('random-host') subject.list(:signed => true, :subject => 'random-host').should == [] end it "should tell the user something was deleted" do given_certificates_for('random-host') subject.list(:signed => true, :subject => 'random-host').should_not == [] subject.destroy('random-host'). should == "Deleted for random-host: Puppet::SSL::Certificate, Puppet::SSL::Key" end end context "#list" do context "with no hosts in CA" do [ {}, { :pending => true }, { :signed => true }, { :all => true }, ].each do |type| it "should return nothing for #{type.inspect}" do subject.list(type).should == [] end it "should return nothing when a matcher is passed" do subject.list(type.merge :subject => '.').should == [] end context "when_rendering :console" do it "should return nothing for #{type.inspect}" do subject.get_action(:list).when_rendering(:console).call(subject.list(type)).should == "" end end end end context "with some hosts" do csr_names = (1..3).map {|n| "csr-#{n}" } crt_names = (1..3).map {|n| "crt-#{n}" } all_names = csr_names + crt_names { {} => csr_names, { :pending => true } => csr_names, { :signed => true } => crt_names, { :all => true } => all_names, { :pending => true, :signed => true } => all_names, }.each do |input, expect| it "should map #{input.inspect} to #{expect.inspect}" do given_certificate_requests_for(*csr_names) given_certificates_for(*crt_names) subject.list(input).map(&:name).should =~ expect end ['', '.', '2', 'none'].each do |pattern| filtered = expect.select {|x| Regexp.new(pattern).match(x) } it "should filter all hosts matching #{pattern.inspect} to #{filtered.inspect}" do given_certificate_requests_for(*csr_names) given_certificates_for(*crt_names) subject.list(input.merge :subject => pattern).map(&:name).should =~ filtered end end end context "when_rendering :console" do { [["csr1.local"], []] => [/^ csr1.local /], [[], ["crt1.local"]] => [/^\+ crt1.local /], [["csr2"], ["crt2"]] => [/^ csr2 /, /^\+ crt2 /] }.each do |input, pattern| it "should render #{input.inspect} to match #{pattern.inspect}" do given_certificate_requests_for(*input[0]) given_certificates_for(*input[1]) text = subject.get_action(:list).when_rendering(:console).call(subject.list(:all => true)) pattern.each do |item| text.should =~ item end end end end end end actions = %w{destroy list revoke generate sign print verify fingerprint} actions.each do |action| it { should be_action action } it "should fail #{action} when not a CA" do Puppet[:ca] = false expect { case subject.method(action).arity when -1 then subject.send(action) when -2 then subject.send(action, 'dummy') else raise "#{action} has arity #{subject.method(action).arity}" end }.should raise_error(/Not a CA/) end end end diff --git a/spec/integration/faces/documentation_spec.rb b/spec/integration/faces/documentation_spec.rb index 75e54f230..65a8170ef 100755 --- a/spec/integration/faces/documentation_spec.rb +++ b/spec/integration/faces/documentation_spec.rb @@ -1,64 +1,64 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe "documentation of faces" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do it "should generate global help" do help = nil expect { help = Puppet::Face[:help, :current].help }.not_to raise_error help.should be_an_instance_of String help.length.should be > 200 end ######################################################################## # Can we actually generate documentation for the face, and the actions it # has? This avoids situations where the ERB template turns out to have a # bug in it, triggered in something the user might do. context "face help messages" do # we need to set a bunk module path here, because without doing so, # the autoloader will try to use it before it is initialized. Puppet[:modulepath] = "/dev/null" Puppet::Face.faces.sort.each do |face_name| # REVISIT: We should walk all versions of the face here... let :help do Puppet::Face[:help, :current] end context "generating help" do it "for #{face_name}" do expect { text = help.help(face_name) text.should be_an_instance_of String text.length.should be > 100 }.not_to raise_error end Puppet::Face[face_name, :current].actions.sort.each do |action_name| it "for #{face_name}.#{action_name}" do expect { text = help.help(face_name, action_name) text.should be_an_instance_of String text.length.should be > 100 }.not_to raise_error end end end ######################################################################## # Ensure that we have authorship and copyright information in *our* faces; # if you apply this to third party faces you might well be disappointed. context "licensing of Puppet Labs face '#{face_name}'" do subject { Puppet::Face[face_name, :current] } its :license do should =~ /Apache\s*2/ end its :copyright do should =~ /Puppet Labs/ end # REVISIT: This is less that ideal, I think, but right now I am more # comfortable watching us ship with some copyright than without any; we # can redress that when it becomes appropriate. --daniel 2011-04-27 its :copyright do should =~ /20\d{2}/ end end end end end end diff --git a/spec/integration/file_serving/content_spec.rb b/spec/integration/file_serving/content_spec.rb index e2efecfa4..a54e73713 100755 --- a/spec/integration/file_serving/content_spec.rb +++ b/spec/integration/file_serving/content_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/content' require 'shared_behaviours/file_serving' describe Puppet::FileServing::Content, " when finding files" do it_should_behave_like "Puppet::FileServing::Files" before do @test_class = Puppet::FileServing::Content @indirection = Puppet::FileServing::Content.indirection end end diff --git a/spec/integration/file_serving/fileset_spec.rb b/spec/integration/file_serving/fileset_spec.rb index f4b869847..1cf7f9aa6 100755 --- a/spec/integration/file_serving/fileset_spec.rb +++ b/spec/integration/file_serving/fileset_spec.rb @@ -1,13 +1,13 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/fileset' describe Puppet::FileServing::Fileset do it "should be able to recurse on a single file" do @path = Tempfile.new("fileset_integration") fileset = Puppet::FileServing::Fileset.new(@path.path) lambda { fileset.files }.should_not raise_error end end diff --git a/spec/integration/file_serving/metadata_spec.rb b/spec/integration/file_serving/metadata_spec.rb index d9aaa37f5..1b5e4131a 100755 --- a/spec/integration/file_serving/metadata_spec.rb +++ b/spec/integration/file_serving/metadata_spec.rb @@ -1,15 +1,15 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/metadata' require 'shared_behaviours/file_serving' require 'puppet/indirector/file_metadata/file_server' describe Puppet::FileServing::Metadata, " when finding files" do it_should_behave_like "Puppet::FileServing::Files" before do @test_class = Puppet::FileServing::Metadata @indirection = Puppet::FileServing::Metadata.indirection end end diff --git a/spec/integration/file_serving/terminus_helper_spec.rb b/spec/integration/file_serving/terminus_helper_spec.rb index 7500b1fc0..6fec2dfe8 100755 --- a/spec/integration/file_serving/terminus_helper_spec.rb +++ b/spec/integration/file_serving/terminus_helper_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/terminus_helper' class TerminusHelperIntegrationTester include Puppet::FileServing::TerminusHelper def model Puppet::FileServing::Metadata end end describe Puppet::FileServing::TerminusHelper do it "should be able to recurse on a single file" do @path = Tempfile.new("fileset_integration") request = Puppet::Indirector::Request.new(:metadata, :find, @path.path, :recurse => true) tester = TerminusHelperIntegrationTester.new lambda { tester.path2instances(request, @path.path) }.should_not raise_error end end diff --git a/spec/integration/indirector/catalog/compiler_spec.rb b/spec/integration/indirector/catalog/compiler_spec.rb index f51a3f24e..712930fb9 100755 --- a/spec/integration/indirector/catalog/compiler_spec.rb +++ b/spec/integration/indirector/catalog/compiler_spec.rb @@ -1,64 +1,64 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/catalog' Puppet::Resource::Catalog.indirection.terminus(:compiler) describe Puppet::Resource::Catalog::Compiler do before do Facter.stubs(:value).returns "something" @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@one = Puppet::Resource.new(:file, "/one")) @catalog.add_resource(@two = Puppet::Resource.new(:file, "/two")) end after { Puppet.settings.clear } it "should remove virtual resources when filtering" do @one.virtual = true Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.should == [ @two.ref ] end it "should not remove exported resources when filtering" do @one.exported = true Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.sort.should == [ @one.ref, @two.ref ] end it "should remove virtual exported resources when filtering" do @one.exported = true @one.virtual = true Puppet::Resource::Catalog.indirection.terminus.filter(@catalog).resource_refs.should == [ @two.ref ] end it "should filter out virtual resources when finding a catalog" do @one.virtual = true request = stub 'request', :name => "mynode" Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog) Puppet::Resource::Catalog.indirection.find(request).resource_refs.should == [ @two.ref ] end it "should not filter out exported resources when finding a catalog" do @one.exported = true request = stub 'request', :name => "mynode" Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog) Puppet::Resource::Catalog.indirection.find(request).resource_refs.sort.should == [ @one.ref, @two.ref ] end it "should filter out virtual exported resources when finding a catalog" do @one.exported = true @one.virtual = true request = stub 'request', :name => "mynode" Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request) Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog) Puppet::Resource::Catalog.indirection.find(request).resource_refs.should == [ @two.ref ] end end diff --git a/spec/integration/indirector/catalog/queue_spec.rb b/spec/integration/indirector/catalog/queue_spec.rb index 945c3cc78..6e5437532 100755 --- a/spec/integration/indirector/catalog/queue_spec.rb +++ b/spec/integration/indirector/catalog/queue_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/catalog' describe "Puppet::Resource::Catalog::Queue", :if => Puppet.features.pson? do before do Puppet::Resource::Catalog.indirection.terminus(:queue) @catalog = Puppet::Resource::Catalog.new @one = Puppet::Resource.new(:file, "/one") @two = Puppet::Resource.new(:file, "/two") @catalog.add_resource(@one, @two) @catalog.add_edge(@one, @two) Puppet[:trace] = true end after { Puppet.settings.clear } it "should render catalogs to pson and publish them via the queue client when catalogs are saved" do terminus = Puppet::Resource::Catalog.indirection.terminus(:queue) client = mock 'client' terminus.stubs(:client).returns client client.expects(:publish_message).with(:catalog, @catalog.to_pson) request = Puppet::Indirector::Request.new(:catalog, :save, "foo", @catalog) terminus.save(request) end it "should intern catalog messages when they are passed via a subscription" do client = mock 'client' Puppet::Resource::Catalog::Queue.stubs(:client).returns client pson = @catalog.to_pson client.expects(:subscribe).with(:catalog).yields(pson) Puppet.expects(:err).never result = [] Puppet::Resource::Catalog::Queue.subscribe do |catalog| result << catalog end catalog = result.shift catalog.should be_instance_of(Puppet::Resource::Catalog) catalog.resource(:file, "/one").should be_instance_of(Puppet::Resource) catalog.resource(:file, "/two").should be_instance_of(Puppet::Resource) catalog.should be_edge(catalog.resource(:file, "/one"), catalog.resource(:file, "/two")) end end diff --git a/spec/integration/indirector/direct_file_server_spec.rb b/spec/integration/indirector/direct_file_server_spec.rb index 1e6e876a4..f3a0c5926 100755 --- a/spec/integration/indirector/direct_file_server_spec.rb +++ b/spec/integration/indirector/direct_file_server_spec.rb @@ -1,69 +1,69 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_content/file' describe Puppet::Indirector::DirectFileServer, " when interacting with the filesystem and the model" do include PuppetSpec::Files before do # We just test a subclass, since it's close enough. @terminus = Puppet::Indirector::FileContent::File.new @filepath = make_absolute("/path/to/my/file") end it "should return an instance of the model" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do FileTest.expects(:exists?).with(@filepath).returns(true) @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}", nil)).should be_instance_of(Puppet::FileServing::Content) end end it "should return an instance capable of returning its content" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do FileTest.expects(:exists?).with(@filepath).returns(true) File.stubs(:lstat).with(@filepath).returns(stub("stat", :ftype => "file")) IO.expects(:binread).with(@filepath).returns("my content") instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}", nil)) instance.content.should == "my content" end end end describe Puppet::Indirector::DirectFileServer, " when interacting with FileServing::Fileset and the model" do include PuppetSpec::Files let(:path) { tmpdir('direct_file_server_testing') } before do @terminus = Puppet::Indirector::FileContent::File.new File.open(File.join(path, "one"), "w") { |f| f.print "one content" } File.open(File.join(path, "two"), "w") { |f| f.print "two content" } @request = @terminus.indirection.request(:search, "file:///#{path}", nil, :recurse => true) end it "should return an instance for every file in the fileset" do result = @terminus.search(@request) result.should be_instance_of(Array) result.length.should == 3 result.each { |r| r.should be_instance_of(Puppet::FileServing::Content) } end it "should return instances capable of returning their content" do @terminus.search(@request).each do |instance| case instance.full_path when /one/; instance.content.should == "one content" when /two/; instance.content.should == "two content" when path else raise "No valid key for #{instance.path.inspect}" end end end end diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb index b4e1c908d..0f1cb48ba 100755 --- a/spec/integration/indirector/file_content/file_server_spec.rb +++ b/spec/integration/indirector/file_content/file_server_spec.rb @@ -1,90 +1,90 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_content/file_server' require 'shared_behaviours/file_server_terminus' require 'puppet_spec/files' describe Puppet::Indirector::FileContent::FileServer, " when finding files" do it_should_behave_like "Puppet::Indirector::FileServerTerminus" include PuppetSpec::Files before do @terminus = Puppet::Indirector::FileContent::FileServer.new @test_class = Puppet::FileServing::Content Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil) end it "should find plugin file content in the environment specified in the request" do path = tmpfile("file_content_with_env") Dir.mkdir(path) modpath = File.join(path, "mod") FileUtils.mkdir_p(File.join(modpath, "lib")) file = File.join(modpath, "lib", "file.rb") File.open(file, "wb") { |f| f.write "1\r\n" } Puppet.settings[:modulepath] = "/no/such/file" env = Puppet::Node::Environment.new("foo") env.stubs(:modulepath).returns [path] result = Puppet::FileServing::Content.indirection.search("plugins", :environment => "foo", :recurse => true) result.should_not be_nil result.length.should == 2 result.map {|x| x.should be_instance_of(Puppet::FileServing::Content) } result.find {|x| x.relative_path == 'file.rb' }.content.should == "1\r\n" end it "should find file content in modules" do path = tmpfile("file_content") Dir.mkdir(path) modpath = File.join(path, "mymod") FileUtils.mkdir_p(File.join(modpath, "files")) file = File.join(modpath, "files", "myfile") File.open(file, "wb") { |f| f.write "1\r\n" } Puppet.settings[:modulepath] = path result = Puppet::FileServing::Content.indirection.find("modules/mymod/myfile") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\r\n" end it "should find file content in files when node name expansions are used" do FileTest.stubs(:exists?).returns true FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true) @path = tmpfile("file_server_testing") Dir.mkdir(@path) subdir = File.join(@path, "mynode") Dir.mkdir(subdir) File.open(File.join(subdir, "myfile"), "wb") { |f| f.write "1\r\n" } # Use a real mount, so the integration is a bit deeper. @mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") @mount1.stubs(:allowed?).returns true @mount1.path = File.join(@path, "%h") @parser = stub 'parser', :changed? => false @parser.stubs(:parse).returns("one" => @mount1) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) path = File.join(@path, "myfile") result = Puppet::FileServing::Content.indirection.find("one/myfile", :environment => "foo", :node => "mynode") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\r\n" end end diff --git a/spec/integration/indirector/file_metadata/file_server_spec.rb b/spec/integration/indirector/file_metadata/file_server_spec.rb index c2cbcfa5c..2be286a02 100755 --- a/spec/integration/indirector/file_metadata/file_server_spec.rb +++ b/spec/integration/indirector/file_metadata/file_server_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_metadata/file_server' require 'shared_behaviours/file_server_terminus' describe Puppet::Indirector::FileMetadata::FileServer, " when finding files" do it_should_behave_like "Puppet::Indirector::FileServerTerminus" before do @terminus = Puppet::Indirector::FileMetadata::FileServer.new @test_class = Puppet::FileServing::Metadata end end diff --git a/spec/integration/indirector/node/ldap_spec.rb b/spec/integration/indirector/node/ldap_spec.rb index 7e53141dc..1d25d992b 100755 --- a/spec/integration/indirector/node/ldap_spec.rb +++ b/spec/integration/indirector/node/ldap_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do it "should use a restrictive filter when searching for nodes in a class" do ldap = Puppet::Node.indirection.terminus(:ldap) Puppet::Node.indirection.stubs(:terminus).returns ldap ldap.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=foo))") Puppet::Node.indirection.search "eh", :class => "foo" end end diff --git a/spec/integration/network/formats_spec.rb b/spec/integration/network/formats_spec.rb index faf667439..f55f74045 100755 --- a/spec/integration/network/formats_spec.rb +++ b/spec/integration/network/formats_spec.rb @@ -1,104 +1,104 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/formats' class PsonIntTest attr_accessor :string def ==(other) other.class == self.class and string == other.string end def self.from_pson(data) new(data[0]) end def initialize(string) @string = string end def to_pson(*args) { 'type' => self.class.name, 'data' => [@string] }.to_pson(*args) end def self.canonical_order(s) s.gsub(/\{"data":\[(.*?)\],"type":"PsonIntTest"\}/,'{"type":"PsonIntTest","data":[\1]}') end end describe Puppet::Network::FormatHandler.format(:s) do before do @format = Puppet::Network::FormatHandler.format(:s) end it "should support certificates" do @format.should be_supported(Puppet::SSL::Certificate) end it "should not support catalogs" do @format.should_not be_supported(Puppet::Resource::Catalog) end end describe Puppet::Network::FormatHandler.format(:pson) do describe "when pson is absent", :if => (! Puppet.features.pson?) do before do @pson = Puppet::Network::FormatHandler.format(:pson) end it "should not be suitable" do @pson.should_not be_suitable end end describe "when pson is available", :if => Puppet.features.pson? do before do @pson = Puppet::Network::FormatHandler.format(:pson) end it "should be able to render an instance to pson" do instance = PsonIntTest.new("foo") PsonIntTest.canonical_order(@pson.render(instance)).should == PsonIntTest.canonical_order('{"type":"PsonIntTest","data":["foo"]}' ) end it "should be able to render arrays to pson" do @pson.render([1,2]).should == '[1,2]' end it "should be able to render arrays containing hashes to pson" do @pson.render([{"one"=>1},{"two"=>2}]).should == '[{"one":1},{"two":2}]' end it "should be able to render multiple instances to pson" do one = PsonIntTest.new("one") two = PsonIntTest.new("two") PsonIntTest.canonical_order(@pson.render([one,two])).should == PsonIntTest.canonical_order('[{"type":"PsonIntTest","data":["one"]},{"type":"PsonIntTest","data":["two"]}]') end it "should be able to intern pson into an instance" do @pson.intern(PsonIntTest, '{"type":"PsonIntTest","data":["foo"]}').should == PsonIntTest.new("foo") end it "should be able to intern pson with no class information into an instance" do @pson.intern(PsonIntTest, '["foo"]').should == PsonIntTest.new("foo") end it "should be able to intern multiple instances from pson" do @pson.intern_multiple(PsonIntTest, '[{"type": "PsonIntTest", "data": ["one"]},{"type": "PsonIntTest", "data": ["two"]}]').should == [ PsonIntTest.new("one"), PsonIntTest.new("two") ] end it "should be able to intern multiple instances from pson with no class information" do @pson.intern_multiple(PsonIntTest, '[["one"],["two"]]').should == [ PsonIntTest.new("one"), PsonIntTest.new("two") ] end end end diff --git a/spec/integration/network/server/mongrel_spec.rb b/spec/integration/network/server/mongrel_spec.rb index bd097ccec..f0b7c7dc0 100755 --- a/spec/integration/network/server/mongrel_spec.rb +++ b/spec/integration/network/server/mongrel_spec.rb @@ -1,63 +1,63 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/server' require 'net/http' describe Puppet::Network::Server do describe "when using mongrel", :if => Puppet.features.mongrel? do # This reduces the odds of conflicting port numbers between concurrent runs # of the suite on the same machine dramatically. def port 20001 + ($$ % 40000) end before :each do Puppet[:servertype] = 'mongrel' Puppet[:server] = '127.0.0.1' @params = { :port => port, :handlers => [ :node ] } @server = Puppet::Network::Server.new(@params) end after :each do @server.unlisten if @server.listening? end describe "when listening" do it "should be reachable on the specified address and port" do @server.listen expect { Net::HTTP.get('127.0.0.1', '/', port) }.should_not raise_error end it "should default to '127.0.0.1' as its bind address" do @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.stubs(:unlisten) # we're breaking listening internally, so we have to keep it from unlistening @server.send(:http_server).expects(:listen).with { |args| args[:address] == "127.0.0.1" } @server.listen end it "should use any specified bind address" do Puppet[:bindaddress] = "0.0.0.0" @server = Puppet::Network::Server.new(@params.merge(:port => 34343)) @server.stubs(:unlisten) # we're breaking listening internally, so we have to keep it from unlistening @server.send(:http_server).expects(:listen).with { |args| args[:address] == "0.0.0.0" } @server.listen end it "should not allow multiple servers to listen on the same address and port" do @server.listen @server2 = Puppet::Network::Server.new(@params) lambda { @server2.listen }.should raise_error end end describe "after unlistening" do it "should not be reachable on the port and address assigned" do @server.listen @server.unlisten expect { Net::HTTP.get('127.0.0.1', '/', port) }. should raise_error Errno::ECONNREFUSED end end end end diff --git a/spec/integration/network/server/webrick_spec.rb b/spec/integration/network/server/webrick_spec.rb index 0f31855b2..e89c32ac1 100755 --- a/spec/integration/network/server/webrick_spec.rb +++ b/spec/integration/network/server/webrick_spec.rb @@ -1,90 +1,90 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/server' require 'puppet/ssl/certificate_authority' require 'socket' describe Puppet::Network::Server, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files # This reduces the odds of conflicting port numbers between concurrent runs # of the suite on the same machine dramatically. def port 20000 + ($$ % 40000) end describe "when using webrick" do before :each do Puppet[:servertype] = 'webrick' Puppet[:server] = '127.0.0.1' @params = { :port => port, :handlers => [ :node ] } # Get a safe temporary file dir = tmpdir("webrick_integration_testing") Puppet.settings[:confdir] = dir Puppet.settings[:vardir] = dir Puppet.settings[:logdir] = dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local ca = Puppet::SSL::CertificateAuthority.new ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.indirection.find(Puppet[:certname]) end after do Puppet.settings.clear Puppet::SSL::Host.ca_location = :none end describe "before listening" do it "should not be reachable at the specified address and port" do lambda { TCPSocket.new('127.0.0.1', port) }.should raise_error end end describe "when listening" do it "should be reachable on the specified address and port" do @server = Puppet::Network::Server.new(@params.merge(:port => port)) @server.listen lambda { TCPSocket.new('127.0.0.1', port) }.should_not raise_error end it "should default to '0.0.0.0' as its bind address" do Puppet.settings.clear Puppet[:servertype] = 'webrick' Puppet[:bindaddress].should == '0.0.0.0' end it "should use any specified bind address" do Puppet[:bindaddress] = "127.0.0.1" @server = Puppet::Network::Server.new(@params.merge(:port => port)) @server.stubs(:unlisten) # we're breaking listening internally, so we have to keep it from unlistening @server.send(:http_server).expects(:listen).with { |args| args[:address] == "127.0.0.1" } @server.listen end it "should not allow multiple servers to listen on the same address and port" do @server = Puppet::Network::Server.new(@params.merge(:port => port)) @server.listen @server2 = Puppet::Network::Server.new(@params.merge(:port => port)) lambda { @server2.listen }.should raise_error end after :each do @server.unlisten if @server && @server.listening? end end describe "after unlistening" do it "should not be reachable on the port and address assigned" do @server = Puppet::Network::Server.new(@params.merge(:port => port)) @server.listen @server.unlisten lambda { TCPSocket.new('127.0.0.1', port) }.should raise_error(Errno::ECONNREFUSED) end end end end diff --git a/spec/integration/node/environment_spec.rb b/spec/integration/node/environment_spec.rb index 25a14afd4..baf8b97f8 100755 --- a/spec/integration/node/environment_spec.rb +++ b/spec/integration/node/environment_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Node::Environment do include PuppetSpec::Files it "should be able to return each module from its environment with the environment, name, and path set correctly" do base = tmpfile("env_modules") Dir.mkdir(base) dirs = [] mods = {} %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir Dir.mkdir(dir) mod = "mod#{num}" moddir = File.join(dir, mod) mods[mod] = moddir Dir.mkdir(moddir) end environment = Puppet::Node::Environment.new("foo") environment.stubs(:modulepath).returns dirs environment.modules.each do |mod| mod.environment.should == environment mod.path.should == mods[mod.name] end end it "should not yield the same module from different module paths" do base = tmpfile("env_modules") Dir.mkdir(base) dirs = [] mods = {} %w{1 2}.each do |num| dir = File.join(base, "dir#{num}") dirs << dir Dir.mkdir(dir) mod = "mod" moddir = File.join(dir, mod) mods[mod] = moddir Dir.mkdir(moddir) end environment = Puppet::Node::Environment.new("foo") environment.stubs(:modulepath).returns dirs mods = environment.modules mods.length.should == 1 mods[0].path.should == File.join(base, "dir1", "mod") end end diff --git a/spec/integration/node/facts_spec.rb b/spec/integration/node/facts_spec.rb index 910d72dec..f39ddb00e 100755 --- a/spec/integration/node/facts_spec.rb +++ b/spec/integration/node/facts_spec.rb @@ -1,41 +1,41 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Node::Facts do describe "when using the indirector" do it "should expire any cached node instances when it is saved" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml Puppet::Node::Facts.indirection.terminus(:yaml).should equal(Puppet::Node::Facts.indirection.terminus(:yaml)) terminus = Puppet::Node::Facts.indirection.terminus(:yaml) terminus.stubs :save Puppet::Node.indirection.expects(:expire).with("me", optionally(instance_of(Hash))) facts = Puppet::Node::Facts.new("me") Puppet::Node::Facts.indirection.save(facts) end it "should be able to delegate to the :yaml terminus" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml # Load now, before we stub the exists? method. terminus = Puppet::Node::Facts.indirection.terminus(:yaml) terminus.expects(:path).with("me").returns "/my/yaml/file" FileTest.expects(:exist?).with("/my/yaml/file").returns false Puppet::Node::Facts.indirection.find("me").should be_nil end it "should be able to delegate to the :facter terminus" do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :facter Facter.expects(:to_hash).returns "facter_hash" facts = Puppet::Node::Facts.new("me") Puppet::Node::Facts.expects(:new).with("me", "facter_hash").returns facts Puppet::Node::Facts.indirection.find("me").should equal(facts) end end end diff --git a/spec/integration/node_spec.rb b/spec/integration/node_spec.rb index 4376f774f..b8b7a658c 100755 --- a/spec/integration/node_spec.rb +++ b/spec/integration/node_spec.rb @@ -1,79 +1,79 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node' describe Puppet::Node do describe "when delegating indirection calls" do before do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil @name = "me" @node = Puppet::Node.new(@name) end it "should be able to use the yaml terminus" do Puppet::Node.indirection.stubs(:terminus_class).returns :yaml # Load now, before we stub the exists? method. terminus = Puppet::Node.indirection.terminus(:yaml) terminus.expects(:path).with(@name).returns "/my/yaml/file" FileTest.expects(:exist?).with("/my/yaml/file").returns false Puppet::Node.indirection.find(@name).should be_nil end it "should have an ldap terminus" do Puppet::Node.indirection.terminus(:ldap).should_not be_nil end it "should be able to use the plain terminus" do Puppet::Node.indirection.stubs(:terminus_class).returns :plain # Load now, before we stub the exists? method. Puppet::Node.indirection.terminus(:plain) Puppet::Node.expects(:new).with(@name).returns @node Puppet::Node.indirection.find(@name).should equal(@node) end describe "and using the memory terminus" do before do @name = "me" @old_terminus = Puppet::Node.indirection.terminus_class @terminus = Puppet::Node.indirection.terminus(:memory) Puppet::Node.indirection.stubs(:terminus).returns @terminus @node = Puppet::Node.new(@name) end it "should find no nodes by default" do Puppet::Node.indirection.find(@name).should be_nil end it "should be able to find nodes that were previously saved" do Puppet::Node.indirection.save(@node) Puppet::Node.indirection.find(@name).should equal(@node) end it "should replace existing saved nodes when a new node with the same name is saved" do Puppet::Node.indirection.save(@node) two = Puppet::Node.new(@name) Puppet::Node.indirection.save(two) Puppet::Node.indirection.find(@name).should equal(two) end it "should be able to remove previously saved nodes" do Puppet::Node.indirection.save(@node) Puppet::Node.indirection.destroy(@node.name) Puppet::Node.indirection.find(@name).should be_nil end it "should fail when asked to destroy a node that does not exist" do proc { Puppet::Node.indirection.destroy(@node) }.should raise_error(ArgumentError) end end end end diff --git a/spec/integration/parser/collector_spec.rb b/spec/integration/parser/collector_spec.rb index c14fa4184..ca242c9b1 100755 --- a/spec/integration/parser/collector_spec.rb +++ b/spec/integration/parser/collector_spec.rb @@ -1,37 +1,37 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/collector' describe Puppet::Parser::Collector do before do @scope = Puppet::Parser::Scope.new(:compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))) @resource = Puppet::Parser::Resource.new("file", "/tmp/testing", :scope => @scope, :source => "fakesource") {:owner => "root", :group => "bin", :mode => "644"}.each do |param, value| @resource[param] = value end end def query(text) code = "File <| #{text} |>" parser = Puppet::Parser::Parser.new(@scope.compiler) return parser.parse(code).code[0].query end {true => [%{title == "/tmp/testing"}, %{(title == "/tmp/testing")}, %{group == bin}, %{title == "/tmp/testing" and group == bin}, %{title == bin or group == bin}, %{title == "/tmp/testing" or title == bin}, %{title == "/tmp/testing"}, %{(title == "/tmp/testing" or title == bin) and group == bin}], false => [%{title == bin}, %{title == bin or (title == bin and group == bin)}, %{title != "/tmp/testing"}, %{title != "/tmp/testing" and group != bin}] }.each do |result, ary| ary.each do |string| it "should return '#{result}' when collecting resources with '#{string}'" do str, code = query(string).evaluate @scope code.should be_instance_of(Proc) code.call(@resource).should == result end end end end diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index 88d2ea9a9..9d5ab4165 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -1,309 +1,309 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::Compiler do before :each do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end after do Puppet.settings.clear end it "should be able to determine the configuration version from a local version control repository" do pending("Bug #14071 about semantics of Puppet::Util::Execute on Windows", :if => Puppet.features.microsoft_windows?) do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' @parser = Puppet::Parser::Parser.new "development" @compiler = Puppet::Parser::Compiler.new(@node) @compiler.catalog.version.should == version end end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do Puppet[:code] = <<-PP class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP @node.stubs(:classes).returns(['foo', 'bar']) catalog = Puppet::Parser::Compiler.compile(@node) catalog.resource("Notify[foo_notify]").should_not be_nil catalog.resource("Notify[bar_notify]").should_not be_nil end describe "when resolving class references" do it "should favor local scope, even if there's an included class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include baz include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end it "should favor local scope, even if there's an unincluded class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end end describe "(ticket #13349) when explicitly specifying top scope" do ["class {'::bar::baz':}", "include ::bar::baz"].each do |include| describe "with #{include}" do it "should find the top level class" do Puppet[:code] = <<-MANIFEST class { 'foo::test': } class foo::test { #{include} } class bar::baz { notify { 'good!': } } class foo::bar::baz { notify { 'bad!': } } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) catalog.resource("Class[Bar::Baz]").should_not be_nil catalog.resource("Notify[good!]").should_not be_nil catalog.resource("Class[Foo::Bar::Baz]").should be_nil catalog.resource("Notify[bad!]").should be_nil end end end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['class', 'define', 'node'].each do |thing| it "should not allow #{thing} inside evaluated conditional constructs" do Puppet[:code] = <<-PP if true { #{thing} foo { } notify { decoy: } } PP begin Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) raise "compilation should have raised Puppet::Error" rescue Puppet::Error => e e.message.should =~ /at line 2/ end end end it "should not allow classes inside unevaluated conditional constructs" do Puppet[:code] = <<-PP if false { class foo { } } PP lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error) end describe "when defining relationships" do def extract_name(ref) ref.sub(/File\[(\w+)\]/, '\1') end let(:node) { Puppet::Node.new('mynode') } let(:code) do <<-MANIFEST file { [a,b,c]: mode => 0644, } file { [d,e]: mode => 0755, } MANIFEST end let(:expected_relationships) { [] } let(:expected_subscriptions) { [] } before :each do Puppet[:code] = code end after :each do catalog = described_class.compile(node) resources = catalog.resources.select { |res| res.type == 'File' } actual_relationships, actual_subscriptions = [:before, :notify].map do |relation| resources.map do |res| dependents = Array(res[relation]) dependents.map { |ref| [res.title, extract_name(ref)] } end.inject(&:concat) end actual_relationships.should =~ expected_relationships actual_subscriptions.should =~ expected_subscriptions end it "should create a relationship" do code << "File[a] -> File[b]" expected_relationships << ['a','b'] end it "should create a subscription" do code << "File[a] ~> File[b]" expected_subscriptions << ['a', 'b'] end it "should create relationships using title arrays" do code << "File[a,b] -> File[c,d]" expected_relationships.concat [ ['a', 'c'], ['b', 'c'], ['a', 'd'], ['b', 'd'], ] end it "should create relationships using collection expressions" do code << "File <| mode == 0644 |> -> File <| mode == 0755 |>" expected_relationships.concat [ ['a', 'd'], ['b', 'd'], ['c', 'd'], ['a', 'e'], ['b', 'e'], ['c', 'e'], ] end it "should create relationships using resource names" do code << "'File[a]' -> 'File[b]'" expected_relationships << ['a', 'b'] end it "should create relationships using variables" do code << <<-MANIFEST $var = File[a] $var -> File[b] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using case statements" do code << <<-MANIFEST $var = 10 case $var { 10: { file { s1: } } 12: { file { s2: } } } -> case $var + 2 { 10: { file { t1: } } 12: { file { t2: } } } MANIFEST expected_relationships << ['s1', 't2'] end it "should create relationships using array members" do code << <<-MANIFEST $var = [ [ [ File[a], File[b] ] ] ] $var[0][0][0] -> $var[0][0][1] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using hash members" do code << <<-MANIFEST $var = {'foo' => {'bar' => {'source' => File[a], 'target' => File[b]}}} $var[foo][bar][source] -> $var[foo][bar][target] MANIFEST expected_relationships << ['a', 'b'] end it "should create relationships using resource declarations" do code << "file { l: } -> file { r: }" expected_relationships << ['l', 'r'] end it "should chain relationships" do code << "File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]" expected_relationships << ['a', 'b'] << ['d', 'c'] expected_subscriptions << ['b', 'c'] << ['e', 'd'] end end end diff --git a/spec/integration/parser/functions/require_spec.rb b/spec/integration/parser/functions/require_spec.rb index aa15f92be..3f66b1ebb 100755 --- a/spec/integration/parser/functions/require_spec.rb +++ b/spec/integration/parser/functions/require_spec.rb @@ -1,43 +1,43 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "The require function" do before :each do @node = Puppet::Node.new("mynode") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.send(:evaluate_main) @compiler.catalog.client_version = "0.25" @scope = @compiler.topscope # preload our functions Puppet::Parser::Functions.function(:include) Puppet::Parser::Functions.function(:require) end it "should add a dependency between the 'required' class and our class" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass") @scope.function_require("requiredclass") @scope.resource["require"].should_not be_nil ref = @scope.resource["require"].shift ref.type.should == "Class" ref.title.should == "Requiredclass" end it "should queue relationships between the 'required' class and our classes" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass1") @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "requiredclass2") @scope.function_require("requiredclass1") @scope.function_require("requiredclass2") @scope.resource["require"].should_not be_nil (ref1,ref2) = @scope.resource["require"] ref1.type.should == "Class" ref1.title.should == "Requiredclass1" ref2.type.should == "Class" ref2.title.should == "Requiredclass2" end end diff --git a/spec/integration/parser/functions_spec.rb b/spec/integration/parser/functions_spec.rb index 6a8fbca9c..74e7b849e 100755 --- a/spec/integration/parser/functions_spec.rb +++ b/spec/integration/parser/functions_spec.rb @@ -1,20 +1,20 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::Functions do before :each do Puppet::Parser::Functions.rmfunction("template") if Puppet::Parser::Functions.functions.include?("template") end it "should support multiple threads autoloading the same function" do threads = [] lambda { 10.times { |a| threads << Thread.new { Puppet::Parser::Functions.function("template") } } }.should_not raise_error threads.each { |t| t.join } end end diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb index f6abdb274..7ca7170e8 100755 --- a/spec/integration/parser/parser_spec.rb +++ b/spec/integration/parser/parser_spec.rb @@ -1,150 +1,150 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::Parser do module ParseMatcher class ParseAs def initialize(klass) @parser = Puppet::Parser::Parser.new "development" @class = klass end def result_instance @result.code[0] end def matches?(string) @string = string @result = @parser.parse(string) result_instance.instance_of?(@class) end def description "parse as a #{@class}" end def failure_message " expected #{@string} to parse as #{@class} but was #{result_instance.class}" end def negative_failure_message " expected #{@string} not to parse as #{@class}" end end def parse_as(klass) ParseAs.new(klass) end class ParseWith def initialize(block) @parser = Puppet::Parser::Parser.new "development" @block = block end def result_instance @result.code[0] end def matches?(string) @string = string @result = @parser.parse(string) @block.call(result_instance) end def description "parse with the block evaluating to true" end def failure_message " expected #{@string} to parse with a true result in the block" end def negative_failure_message " expected #{@string} not to parse with a true result in the block" end end def parse_with(&block) ParseWith.new(block) end end include ParseMatcher before :each do @resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser = Puppet::Parser::Parser.new "development" end describe "when parsing comments before statement" do it "should associate the documentation to the statement AST node" do ast = @parser.parse(""" # comment class test {} """) ast.code[0].should be_a(Puppet::Parser::AST::Hostclass) ast.code[0].name.should == 'test' ast.code[0].instantiate('')[0].doc.should == "comment\n" end end describe "when parsing" do it "should be able to parse normal left to right relationships" do "Notify[foo] -> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should be able to parse right to left relationships" do "Notify[foo] <- Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should be able to parse normal left to right subscriptions" do "Notify[foo] ~> Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should be able to parse right to left subscriptions" do "Notify[foo] <~ Notify[bar]".should parse_as(Puppet::Parser::AST::Relationship) end it "should correctly set the arrow type of a relationship" do "Notify[foo] <~ Notify[bar]".should parse_with { |rel| rel.arrow == "<~" } end it "should be able to parse deep hash access" do %q{ $hash = { 'a' => { 'b' => { 'c' => 'it works' } } } $out = $hash['a']['b']['c'] }.should parse_with { |v| v.value.is_a?(Puppet::Parser::AST::ASTHash) } end it "should fail if asked to parse '$foo::::bar'" do expect { @parser.parse("$foo::::bar") }.should raise_error(Puppet::ParseError, /Syntax error at ':'/) end describe "function calls" do it "should be able to pass an array to a function" do "my_function([1,2,3])".should parse_with { |fun| fun.is_a?(Puppet::Parser::AST::Function) && fun.arguments[0].evaluate(stub 'scope') == ['1','2','3'] } end it "should be able to pass a hash to a function" do "my_function({foo => bar})".should parse_with { |fun| fun.is_a?(Puppet::Parser::AST::Function) && fun.arguments[0].evaluate(stub 'scope') == {'foo' => 'bar'} } end end describe "collections" do it "should find resources according to an expression" do %q{ File <| mode == 0700 + 0050 + 0050 |> }.should parse_with { |coll| coll.is_a?(Puppet::Parser::AST::Collection) && coll.query.evaluate(stub 'scope').first == ["mode", "==", 0700 + 0050 + 0050] } end end end end diff --git a/spec/integration/parser/ruby_manifest_spec.rb b/spec/integration/parser/ruby_manifest_spec.rb index 7f3bb71e9..5321d7a33 100755 --- a/spec/integration/parser/ruby_manifest_spec.rb +++ b/spec/integration/parser/ruby_manifest_spec.rb @@ -1,127 +1,127 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'tempfile' require 'puppet_spec/files' describe "Pure ruby manifests" do include PuppetSpec::Files before do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") @test_dir = tmpdir('ruby_manifest_test') end after do Puppet.settings.clear end def write_file(name, contents) path = File.join(@test_dir, name) File.open(path, "w") { |f| f.write(contents) } path end def compile(contents) Puppet[:code] = contents Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end end it "should allow classes" do write_file('foo.rb', ["hostclass 'one' do notify('one_notify') end", "hostclass 'two' do notify('two_notify') end"].join("\n")) catalog = compile("import 'foo'\ninclude one") catalog.resource("Notify[one_notify]").should_not be_nil catalog.resource("Notify[two_notify]").should be_nil end it "should allow defines" do write_file('foo.rb', 'define "bar", :arg do notify("bar_#{@name}_#{@arg}") end') catalog = compile("import 'foo'\nbar { instance: arg => 'xyz' }") catalog.resource("Notify[bar_instance_xyz]").should_not be_nil catalog.resource("Bar[instance]").should_not be_nil end it "should allow node declarations" do write_file('foo.rb', "node 'mynode' do notify('mynode') end") catalog = compile("import 'foo'") node_declaration = catalog.resource("Notify[mynode]") node_declaration.should_not be_nil node_declaration.title.should == 'mynode' end it "should allow access to the environment" do write_file('foo.rb', ["hostclass 'bar' do", " if environment.is_a? Puppet::Node::Environment", " notify('success')", " end", "end"].join("\n")) compile("import 'foo'\ninclude bar").resource("Notify[success]").should_not be_nil end it "should allow creation of resources of built-in types" do write_file('foo.rb', "hostclass 'bar' do file 'test_file', :owner => 'root', :mode => '644' end") catalog = compile("import 'foo'\ninclude bar") file = catalog.resource("File[test_file]") file.should be_a(Puppet::Resource) file.type.should == 'File' file.title.should == 'test_file' file.exported.should_not be file.virtual.should_not be file[:owner].should == 'root' file[:mode].should == '644' file[:stage].should be_nil # TODO: is this correct behavior? end it "should allow calling user-defined functions" do write_file('foo.rb', "hostclass 'bar' do user_func 'name', :arg => 'xyz' end") catalog = compile(['define user_func($arg) { notify {"n_$arg": } }', 'import "foo"', 'include bar'].join("\n")) catalog.resource("Notify[n_xyz]").should_not be_nil catalog.resource("User_func[name]").should_not be_nil end it "should be properly cached for multiple compiles" do # Note: we can't test this by calling compile() twice, because # that sets Puppet[:code], which clears out all cached # environments. Puppet[:filetimeout] = 1000 write_file('foo.rb', "hostclass 'bar' do notify('success') end") Puppet[:code] = "import 'foo'\ninclude bar" # Compile the catalog and check it catalog = Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end catalog.resource("Notify[success]").should_not be_nil # Secretly change the file to make it invalid. This change # shouldn't be noticed because the we've set a high # Puppet[:filetimeout]. write_file('foo.rb', "raise 'should not be executed'") # Compile the catalog a second time and make sure it's still ok. catalog = Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end catalog.resource("Notify[success]").should_not be_nil end it "should be properly reloaded when stale" do Puppet[:filetimeout] = -1 # force stale check to happen all the time write_file('foo.rb', "hostclass 'bar' do notify('version1') end") catalog = compile("import 'foo'\ninclude bar") catalog.resource("Notify[version1]").should_not be_nil sleep 1 # so that timestamp will change forcing file reload write_file('foo.rb', "hostclass 'bar' do notify('version2') end") catalog = compile("import 'foo'\ninclude bar") catalog.resource("Notify[version1]").should be_nil catalog.resource("Notify[version2]").should_not be_nil end end diff --git a/spec/integration/provider/mailalias/aliases_spec.rb b/spec/integration/provider/mailalias/aliases_spec.rb index 232704e61..1388b5192 100755 --- a/spec/integration/provider/mailalias/aliases_spec.rb +++ b/spec/integration/provider/mailalias/aliases_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' provider_class = Puppet::Type.type(:mailalias).provider(:aliases) describe provider_class do # #1560, in which we corrupt the format of complex mail aliases. it_should_behave_like "all parsedfile providers", provider_class end diff --git a/spec/integration/provider/package_spec.rb b/spec/integration/provider/package_spec.rb index 435a45d05..314c04455 100755 --- a/spec/integration/provider/package_spec.rb +++ b/spec/integration/provider/package_spec.rb @@ -1,44 +1,44 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "Package provider" do include PuppetSpec::Files Puppet::Type.type(:package).providers.each do |name| provider = Puppet::Type.type(:package).provider(name) describe name, :if => provider.suitable? do it "should fail when asked to install an invalid package" do pending("This test hangs forever with recent versions of RubyGems") if provider.name == :gem options = {:name => "nosuch#{provider.name}", :provider => provider.name} # The MSI provider requires that source be specified as it is # what actually determines if the package exists. if provider.name == :msi options[:source] = tmpfile("msi_package") end pkg = Puppet::Type.newpackage(options) lambda { pkg.provider.install }.should raise_error end it "should be able to get a list of existing packages" do if provider.name == :msi Puppet[:vardir] = tmpdir('msi_package_var_dir') end # the instances method requires root priviledges on gentoo # if the eix cache is outdated (to run eix-update) so make # sure we dont actually run eix-update if provider.name == :portage provider.stubs(:update_eix).returns('Database contains 15240 packages in 155 categories') end provider.instances.each do |package| package.should be_instance_of(provider) package.properties[:provider].should == provider.name end end end end end diff --git a/spec/integration/provider/service/init_spec.rb b/spec/integration/provider/service/init_spec.rb index 311a85b8c..a2da4c57b 100755 --- a/spec/integration/provider/service/init_spec.rb +++ b/spec/integration/provider/service/init_spec.rb @@ -1,46 +1,46 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider = Puppet::Type.type(:service).provider(:init) describe provider do describe "when running on FreeBSD" do before :each do Facter.stubs(:value).with(:operatingsystem).returns 'FreeBSD' end it "should set its default path to include /etc/rc.d and /usr/local/etc/rc.d" do provider.defpath.should == ["/etc/rc.d", "/usr/local/etc/rc.d"] end end describe "when running on HP-UX" do before :each do Facter.stubs(:value).with(:operatingsystem).returns 'HP-UX' end it "should set its default path to include /sbin/init.d" do provider.defpath.should == "/sbin/init.d" end end describe "when running on Archlinux" do before :each do Facter.stubs(:value).with(:operatingsystem).returns 'Archlinux' end it "should set its default path to include /etc/rc.d" do provider.defpath.should == "/etc/rc.d" end end describe "when not running on FreeBSD, HP-UX or Archlinux" do before :each do Facter.stubs(:value).with(:operatingsystem).returns 'RedHat' end it "should set its default path to include /etc/init.d" do provider.defpath.should == "/etc/init.d" end end end diff --git a/spec/integration/provider/ssh_authorized_key_spec.rb b/spec/integration/provider/ssh_authorized_key_spec.rb index c45874c0a..41f2aff0c 100755 --- a/spec/integration/provider/ssh_authorized_key_spec.rb +++ b/spec/integration/provider/ssh_authorized_key_spec.rb @@ -1,219 +1,219 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_bucket/dipper' describe Puppet::Type.type(:ssh_authorized_key).provider(:parsed), '(integration)', :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files let :fake_userfile do tmpfile('authorized_keys.user') end let :fake_rootfile do tmpfile('authorized_keys.root') end let :sample_rsa_keys do [ 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQCi18JBZOq10X3w4f67nVhO0O3s5Y1vHH4UgMSM3ZnQwbC5hjGyYSi9UULOoQQoQynI/a0I9NL423/Xk/XJVIKCHcS8q6V2Wmjd+fLNelOjxxoW6mbIytEt9rDvwgq3Mof3/m21L3t2byvegR00a+ikKbmInPmKwjeWZpexCIsHzQ==', # 1024 bit 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLClyvi3CsJw5Id6khZs2/+s11qOH4Gdp6iDioDsrIp0m8kSiPr71VGyQYAfPzzvHemHS7Xg0NkG1Kc8u9tRqBQfTvz7ubq0AT/g01+4P2hQ/soFkuwlUG/HVnnaYb6N0Qp5SHWvD5vBE2nFFQVpP5GrSctPtHSjzJq/i+6LYhmQ==', # 1024 bit 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDLygAO6txXkh9FNV8xSsBkATeqLbHzS7sFjGI3gt0Dx6q3LjyKwbhQ1RLf28kd5G6VWiXmClU/RtiPdUz8nrGuun++2mrxzrXrvpR9dq1lygLQ2wn2cI35dN5bjRMtXy3decs6HUhFo9MoNwX250rUWfdCyNPhGIp6OOfmjdy+UeLGNxq9wDx6i4bT5tVVSqVRtsEfw9+ICXchzl85QudjneVVpP+thriPZXfXA5eaGwAo/dmoKOIhUwF96gpdLqzNtrGQuxPbV80PTbGv9ZtAtTictxaDz8muXO7he9pXmchUpxUKtMFjHkL0FAZ9tRPmv3RA30sEr2fZ8+LKvnE50w0' #2048 Bit ] end let :sample_dsa_keys do [ 'AAAAB3NzaC1kc3MAAACBAOPck2O8MIDSqxPSnvENt6tzRrKJ5oOhB6Nc6oEcWm+VEH1gvuxdiRqwoMgRwyEf1yUd+UAcLw3a6Jn+EtFyEBN/5WF+4Tt4xTxZ0Pfik2Wc5uqHbQ2dkmOoXiAOYPiD3JUQ1Xwm/J0CgetjitoLfzAGdCNhMqguqAuHcVJ78ZZbAAAAFQCIBKFYZ+I18I+dtgteirXh+VVEEwAAAIEAs1yvQ/wnLLrRCM660pF4kBiw3D6dJfMdCXWQpn0hZmkBQSIzZv4Wuk3giei5luxscDxNc+y3CTXtnyG4Kt1Yi2sOdvhRI3rX8tD+ejn8GHazM05l5VIo9uu4AQPIE32iV63IqgApSBbJ6vDJW91oDH0J492WdLCar4BS/KE3cRwAAACBAN0uSDyJqYLRsfYcFn4HyVf6TJxQm1IcwEt6GcJVzgjri9VtW7FqY5iBqa9B9Zdh5XXAYJ0XLsWQCcrmMHM2XGHGpA4gL9VlCJ/0QvOcXxD2uK7IXwAVUA7g4V4bw8EVnFv2Flufozhsp+4soo1xiYc5jiFVHwVlk21sMhAtKAeF' # 1024 Bit ] end let :sample_lines do [ "ssh-rsa #{sample_rsa_keys[1]} root@someotherhost", "ssh-dss #{sample_dsa_keys[0]} root@anywhere", "ssh-rsa #{sample_rsa_keys[2]} paul", "ssh-rsa #{sample_rsa_keys[2]} dummy" ] end let :dummy do Puppet::Type.type(:ssh_authorized_key).new( :name => 'dummy', :target => fake_userfile, :user => 'nobody', :ensure => :absent ) end before :each do File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields end after :each do described_class.clear # Work around bug #6628 end def create_fake_key(username, content) filename = (username == :root ? fake_rootfile : fake_userfile ) File.open(filename, 'w') do |f| content.each do |line| f.puts line end end end def check_fake_key(username, expected_content) filename = (username == :root ? fake_rootfile : fake_userfile ) content = File.readlines(filename).map(&:chomp).sort.reject{ |x| x =~ /^# HEADER:/ } content.join("\n").should == expected_content.sort.join("\n") end def run_in_catalog(*resources) Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to the filebucket catalog = Puppet::Resource::Catalog.new catalog.host_config = false resources.each do |resource| resource.expects(:err).never catalog.add_resource(resource) end catalog.apply end it "should not complain about empty lines and comments" do described_class.expects(:flush).never sample = ['',sample_lines[0],' ',sample_lines[1],'# just a comment','#and another'] create_fake_key(:user,sample) run_in_catalog(dummy) check_fake_key(:user, sample) end it "should keep empty lines and comments when modifying a file" do create_fake_key(:user, ['',sample_lines[0],' ',sample_lines[3],'# just a comment','#and another']) run_in_catalog(dummy) check_fake_key(:user, ['',sample_lines[0],' ','# just a comment','#and another']) end describe "when managing one resource" do describe "with ensure set to absent" do let :example do Puppet::Type.type(:ssh_authorized_key).new( :name => 'root@hostname', :type => :rsa, :key => sample_rsa_keys[0], :target => fake_rootfile, :user => 'root', :ensure => :absent ) end it "should not modify root's keyfile if resource is currently not present" do create_fake_key(:root, sample_lines) run_in_catalog(example) check_fake_key(:root, sample_lines) end it "remove the key from root's keyfile if resource is currently present" do create_fake_key(:root, sample_lines + ["ssh-rsa #{sample_rsa_keys[0]} root@hostname"]) run_in_catalog(example) check_fake_key(:root, sample_lines) end end describe "when ensure is present" do let :example do Puppet::Type.type(:ssh_authorized_key).new( :name => 'root@hostname', :type => :rsa, :key => sample_rsa_keys[0], :target => fake_rootfile, :user => 'root', :ensure => :present ) end # just a dummy so the parsedfile provider is aware # of the user's authorized_keys file it "should add the key if it is not present" do create_fake_key(:root, sample_lines) run_in_catalog(example) check_fake_key(:root, sample_lines + ["ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) end it "should modify the type if type is out of sync" do create_fake_key(:root,sample_lines + [ "ssh-dss #{sample_rsa_keys[0]} root@hostname" ]) run_in_catalog(example) check_fake_key(:root, sample_lines + [ "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) end it "should modify the key if key is out of sync" do create_fake_key(:root,sample_lines + [ "ssh-rsa #{sample_rsa_keys[1]} root@hostname" ]) run_in_catalog(example) check_fake_key(:root, sample_lines + [ "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) end it "should remove the key from old file if target is out of sync" do create_fake_key(:user, [ sample_lines[0], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) create_fake_key(:root, [ sample_lines[1], sample_lines[2] ]) run_in_catalog(example, dummy) check_fake_key(:user, [ sample_lines[0] ]) #check_fake_key(:root, [ sample_lines[1], sample_lines[2], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) end it "should add the key to new file if target is out of sync" do create_fake_key(:user, [ sample_lines[0], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) create_fake_key(:root, [ sample_lines[1], sample_lines[2] ]) run_in_catalog(example, dummy) #check_fake_key(:user, [ sample_lines[0] ]) check_fake_key(:root, [ sample_lines[1], sample_lines[2], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) end it "should modify options if options are out of sync" do example[:options]=[ 'from="*.domain1,host1.domain2"', 'no-port-forwarding', 'no-pty' ] create_fake_key(:root, sample_lines + [ "from=\"*.false,*.false2\",no-port-forwarding,no-pty ssh-rsa #{sample_rsa_keys[0]} root@hostname"]) run_in_catalog(example) check_fake_key(:root, sample_lines + [ "from=\"*.domain1,host1.domain2\",no-port-forwarding,no-pty ssh-rsa #{sample_rsa_keys[0]} root@hostname"] ) end end end describe "when managing two resource" do let :examples do resources = [] resources << Puppet::Type.type(:ssh_authorized_key).new( :name => 'root@hostname', :type => :rsa, :key => sample_rsa_keys[0], :target => fake_rootfile, :user => 'root', :ensure => :present ) resources << Puppet::Type.type(:ssh_authorized_key).new( :name => 'user@hostname', :key => sample_rsa_keys[1], :type => :rsa, :target => fake_userfile, :user => 'nobody', :ensure => :present ) resources end describe "and both keys are absent" do before :each do create_fake_key(:root, sample_lines) create_fake_key(:user, sample_lines) end it "should add both keys" do run_in_catalog(*examples) check_fake_key(:root, sample_lines + [ "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) check_fake_key(:user, sample_lines + [ "ssh-rsa #{sample_rsa_keys[1]} user@hostname" ]) end end end end diff --git a/spec/integration/reference/providers_spec.rb b/spec/integration/reference/providers_spec.rb index c2b83588d..81f5ad975 100755 --- a/spec/integration/reference/providers_spec.rb +++ b/spec/integration/reference/providers_spec.rb @@ -1,16 +1,16 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/reference' reference = Puppet::Util::Reference.reference(:providers) describe reference do it "should exist" do reference.should_not be_nil end it "should be able to be rendered as markdown" do reference.to_markdown end end diff --git a/spec/integration/reports_spec.rb b/spec/integration/reports_spec.rb index 17662c810..52ee66441 100755 --- a/spec/integration/reports_spec.rb +++ b/spec/integration/reports_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/reports' describe Puppet::Reports, " when using report types" do before do Puppet.settings.stubs(:use) end it "should load report types as modules" do Puppet::Reports.report(:store).should be_instance_of(Module) end end diff --git a/spec/integration/resource/catalog_spec.rb b/spec/integration/resource/catalog_spec.rb index df310b184..f46f7e86e 100755 --- a/spec/integration/resource/catalog_spec.rb +++ b/spec/integration/resource/catalog_spec.rb @@ -1,55 +1,55 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Resource::Catalog do describe "when pson is available", :if => Puppet.features.pson? do it "should support pson" do Puppet::Resource::Catalog.supported_formats.should be_include(:pson) end end describe "when using the indirector" do before do # This is so the tests work w/out networking. Facter.stubs(:to_hash).returns({"hostname" => "foo.domain.com"}) Facter.stubs(:value).returns("eh") end it "should be able to delegate to the :yaml terminus" do Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :yaml # Load now, before we stub the exists? method. terminus = Puppet::Resource::Catalog.indirection.terminus(:yaml) terminus.expects(:path).with("me").returns "/my/yaml/file" FileTest.expects(:exist?).with("/my/yaml/file").returns false Puppet::Resource::Catalog.indirection.find("me").should be_nil end it "should be able to delegate to the :compiler terminus" do Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :compiler # Load now, before we stub the exists? method. compiler = Puppet::Resource::Catalog.indirection.terminus(:compiler) node = mock 'node' node.stub_everything Puppet::Node.indirection.expects(:find).returns(node) compiler.expects(:compile).with(node).returns nil Puppet::Resource::Catalog.indirection.find("me").should be_nil end it "should pass provided node information directly to the terminus" do terminus = mock 'terminus' Puppet::Resource::Catalog.indirection.stubs(:terminus).returns terminus node = mock 'node' terminus.expects(:find).with { |request| request.options[:use_node] == node } Puppet::Resource::Catalog.indirection.find("me", :use_node => node) end end end diff --git a/spec/integration/resource/type_collection_spec.rb b/spec/integration/resource/type_collection_spec.rb index 6ea2e7fe7..a3d7d042a 100755 --- a/spec/integration/resource/type_collection_spec.rb +++ b/spec/integration/resource/type_collection_spec.rb @@ -1,95 +1,95 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet/resource/type_collection' describe Puppet::Resource::TypeCollection do describe "when autoloading from modules" do include PuppetSpec::Files before do @dir = tmpfile("autoload_testing") Puppet[:modulepath] = @dir FileUtils.mkdir_p @dir @code = Puppet::Resource::TypeCollection.new("env") Puppet::Node::Environment.new("env").stubs(:known_resource_types).returns @code end # Setup a module. def mk_module(name, files = {}) mdir = File.join(@dir, name) mandir = File.join(mdir, "manifests") FileUtils.mkdir_p mandir defs = files.delete(:define) Dir.chdir(mandir) do files.each do |file, classes| File.open("#{file}.pp", "w") do |f| classes.each { |klass| if defs f.puts "define #{klass} {}" else f.puts "class #{klass} {}" end } end end end end it "should return nil when a class can't be found or loaded" do @code.find_hostclass('', 'nosuchclass').should be_nil end it "should load the module's init file first" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("", name).name.should == name end it "should load the module's init file even when searching from a different namespace" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("other::ns", name).name.should == name end it "should be able to load definitions from the module base file" do name = "simpdef" mk_module(name, :define => true, :init => [name]) @code.find_definition("", name).name.should == name end it "should be able to load qualified classes from the module base file" do modname = "both" name = "sub" mk_module(modname, :init => %w{both both::sub}) @code.find_hostclass("both", name).name.should == "both::sub" end it "should be able load classes from a separate file" do modname = "separate" name = "sub" mk_module(modname, :init => %w{separate}, :sub => %w{separate::sub}) @code.find_hostclass("separate", name).name.should == "separate::sub" end it "should not fail when loading from a separate file if there is no module file" do modname = "alone" name = "sub" mk_module(modname, :sub => %w{alone::sub}) lambda { @code.find_hostclass("alone", name) }.should_not raise_error end it "should be able to load definitions from their own file" do name = "mymod" mk_module(name, :define => true, :mydefine => ["mymod::mydefine"]) @code.find_definition("", "mymod::mydefine").name.should == "mymod::mydefine" end end end diff --git a/spec/integration/ssl/certificate_authority_spec.rb b/spec/integration/ssl/certificate_authority_spec.rb index a4792449e..634ff3289 100755 --- a/spec/integration/ssl/certificate_authority_spec.rb +++ b/spec/integration/ssl/certificate_authority_spec.rb @@ -1,126 +1,126 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_authority' describe Puppet::SSL::CertificateAuthority, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before do # Get a safe temporary file dir = tmpdir("ca_integration_testing") Puppet.settings[:confdir] = dir Puppet.settings[:vardir] = dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local @ca = Puppet::SSL::CertificateAuthority.new end after { Puppet::SSL::Host.ca_location = :none Puppet.settings.clear Puppet::SSL::CertificateAuthority.instance_variable_set("@instance", nil) } it "should create a CA host" do @ca.host.should be_ca end it "should be able to generate a certificate" do @ca.generate_ca_certificate @ca.host.certificate.should be_instance_of(Puppet::SSL::Certificate) end it "should be able to generate a new host certificate" do @ca.generate("newhost") Puppet::SSL::Certificate.indirection.find("newhost").should be_instance_of(Puppet::SSL::Certificate) end it "should be able to revoke a host certificate" do @ca.generate("newhost") @ca.revoke("newhost") lambda { @ca.verify("newhost") }.should raise_error end it "should have a CRL" do @ca.generate_ca_certificate @ca.crl.should_not be_nil end it "should be able to read in a previously created CRL" do @ca.generate_ca_certificate # Create it to start with. @ca.crl Puppet::SSL::CertificateAuthority.new.crl.should_not be_nil end describe "when signing certificates" do before do @host = Puppet::SSL::Host.new("luke.madstop.com") # We have to provide the key, since when we're in :ca_only mode, we can only interact # with the CA key. key = Puppet::SSL::Key.new(@host.name) key.generate @host.key = key @host.generate_certificate_request path = File.join(Puppet[:requestdir], "luke.madstop.com.pem") end it "should be able to sign certificates" do @ca.sign("luke.madstop.com") end it "should save the signed certificate" do @ca.sign("luke.madstop.com") Puppet::SSL::Certificate.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::Certificate) end it "should be able to sign multiple certificates" do @other = Puppet::SSL::Host.new("other.madstop.com") okey = Puppet::SSL::Key.new(@other.name) okey.generate @other.key = okey @other.generate_certificate_request @ca.sign("luke.madstop.com") @ca.sign("other.madstop.com") Puppet::SSL::Certificate.indirection.find("other.madstop.com").should be_instance_of(Puppet::SSL::Certificate) Puppet::SSL::Certificate.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::Certificate) end it "should save the signed certificate to the :signeddir" do @ca.sign("luke.madstop.com") client_cert = File.join(Puppet[:signeddir], "luke.madstop.com.pem") File.read(client_cert).should == Puppet::SSL::Certificate.indirection.find("luke.madstop.com").content.to_s end it "should save valid certificates" do @ca.sign("luke.madstop.com") unless ssl = Puppet::Util::which('openssl') pending "No ssl available" else ca_cert = Puppet[:cacert] client_cert = File.join(Puppet[:signeddir], "luke.madstop.com.pem") output = %x{openssl verify -CAfile #{ca_cert} #{client_cert}} $CHILD_STATUS.should == 0 end end end end diff --git a/spec/integration/ssl/certificate_request_spec.rb b/spec/integration/ssl/certificate_request_spec.rb index f634636e0..4357854ca 100755 --- a/spec/integration/ssl/certificate_request_spec.rb +++ b/spec/integration/ssl/certificate_request_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_request' describe Puppet::SSL::CertificateRequest do include PuppetSpec::Files before do # Get a safe temporary file dir = tmpdir("csr_integration_testing") Puppet.settings.clear Puppet.settings[:confdir] = dir Puppet.settings[:vardir] = dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :none @csr = Puppet::SSL::CertificateRequest.new("luke.madstop.com") @key = OpenSSL::PKey::RSA.new(512) # This is necessary so the terminus instances don't lie around. Puppet::SSL::CertificateRequest.indirection.termini.clear end after do Puppet.settings.clear end it "should be able to generate CSRs" do @csr.generate(@key) end it "should be able to save CSRs" do Puppet::SSL::CertificateRequest.indirection.save(@csr) end it "should be able to find saved certificate requests via the Indirector" do @csr.generate(@key) Puppet::SSL::CertificateRequest.indirection.save(@csr) Puppet::SSL::CertificateRequest.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::CertificateRequest) end it "should save the completely CSR when saving" do @csr.generate(@key) Puppet::SSL::CertificateRequest.indirection.save(@csr) Puppet::SSL::CertificateRequest.indirection.find("luke.madstop.com").content.to_s.should == @csr.content.to_s end end diff --git a/spec/integration/ssl/certificate_revocation_list_spec.rb b/spec/integration/ssl/certificate_revocation_list_spec.rb index 2f9a01eef..3e9507da6 100755 --- a/spec/integration/ssl/certificate_revocation_list_spec.rb +++ b/spec/integration/ssl/certificate_revocation_list_spec.rb @@ -1,37 +1,37 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_revocation_list' describe Puppet::SSL::CertificateRevocationList do include PuppetSpec::Files before do # Get a safe temporary file dir = tmpdir("ca_integration_testing") Puppet.settings[:confdir] = dir Puppet.settings[:vardir] = dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local end after { Puppet::SSL::Host.ca_location = :none Puppet.settings.clear # This is necessary so the terminus instances don't lie around. Puppet::SSL::Host.indirection.termini.clear } it "should be able to read in written out CRLs with no revoked certificates" do ca = Puppet::SSL::CertificateAuthority.new raise "CRL not created" unless FileTest.exist?(Puppet[:hostcrl]) crl = Puppet::SSL::CertificateRevocationList.new("crl_int_testing") crl.read(Puppet[:hostcrl]) end end diff --git a/spec/integration/ssl/host_spec.rb b/spec/integration/ssl/host_spec.rb index 18e80be3b..f56a9c275 100755 --- a/spec/integration/ssl/host_spec.rb +++ b/spec/integration/ssl/host_spec.rb @@ -1,84 +1,84 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/host' describe Puppet::SSL::Host do include PuppetSpec::Files before do # Get a safe temporary file dir = tmpdir("host_integration_testing") Puppet.settings[:confdir] = dir Puppet.settings[:vardir] = dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local @host = Puppet::SSL::Host.new("luke.madstop.com") @ca = Puppet::SSL::CertificateAuthority.new end after { Puppet::SSL::Host.ca_location = :none Puppet.settings.clear } it "should be considered a CA host if its name is equal to 'ca'" do Puppet::SSL::Host.new(Puppet::SSL::CA_NAME).should be_ca end describe "when managing its key" do it "should be able to generate and save a key" do @host.generate_key end it "should save the key such that the Indirector can find it" do @host.generate_key Puppet::SSL::Key.indirection.find(@host.name).content.to_s.should == @host.key.to_s end it "should save the private key into the :privatekeydir" do @host.generate_key File.read(File.join(Puppet.settings[:privatekeydir], "luke.madstop.com.pem")).should == @host.key.to_s end end describe "when managing its certificate request" do it "should be able to generate and save a certificate request" do @host.generate_certificate_request end it "should save the certificate request such that the Indirector can find it" do @host.generate_certificate_request Puppet::SSL::CertificateRequest.indirection.find(@host.name).content.to_s.should == @host.certificate_request.to_s end it "should save the private certificate request into the :privatekeydir" do @host.generate_certificate_request File.read(File.join(Puppet.settings[:requestdir], "luke.madstop.com.pem")).should == @host.certificate_request.to_s end end describe "when the CA host" do it "should never store its key in the :privatekeydir" do Puppet.settings.use(:main, :ssl, :ca) @ca = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name) @ca.generate_key FileTest.should_not be_exist(File.join(Puppet[:privatekeydir], "ca.pem")) end end it "should pass the verification of its own SSL store", :unless => Puppet.features.microsoft_windows? do @host.generate @ca = Puppet::SSL::CertificateAuthority.new @ca.sign(@host.name) @host.ssl_store.verify(@host.certificate.content).should be_true end end diff --git a/spec/integration/transaction/report_spec.rb b/spec/integration/transaction/report_spec.rb index 8c581cc04..53fa04762 100755 --- a/spec/integration/transaction/report_spec.rb +++ b/spec/integration/transaction/report_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Transaction::Report do describe "when using the indirector" do after do Puppet.settings.stubs(:use) end it "should be able to delegate to the :processor terminus" do Puppet::Transaction::Report.indirection.stubs(:terminus_class).returns :processor terminus = Puppet::Transaction::Report.indirection.terminus(:processor) Facter.stubs(:value).returns "host.domain.com" report = Puppet::Transaction::Report.new("apply") terminus.expects(:process).with(report) Puppet::Transaction::Report.indirection.save(report) end end end diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb index c5928a33a..ccc9aac31 100755 --- a/spec/integration/transaction_spec.rb +++ b/spec/integration/transaction_spec.rb @@ -1,346 +1,346 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction' describe Puppet::Transaction do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end def mk_catalog(*resources) catalog = Puppet::Resource::Catalog.new(Puppet::Node.new("mynode")) resources.each { |res| catalog.add_resource res } catalog end def usr_bin_touch(path) Puppet.features.microsoft_windows? ? "#{ENV['windir']}/system32/cmd.exe /c \"type NUL >> \"#{path}\"\"" : "/usr/bin/touch #{path}" end def touch(path) Puppet.features.microsoft_windows? ? "cmd.exe /c \"type NUL >> \"#{path}\"\"" : "touch #{path}" end it "should not apply generated resources if the parent resource fails" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false catalog.add_resource resource child_resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar/baz"), :backup => false resource.expects(:eval_generate).returns([child_resource]) transaction = Puppet::Transaction.new(catalog) resource.expects(:retrieve).raises "this is a failure" resource.stubs(:err) child_resource.expects(:retrieve).never transaction.evaluate end it "should not apply virtual resources" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false resource.virtual = true catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) resource.expects(:evaluate).never transaction.evaluate end it "should apply exported resources" do catalog = Puppet::Resource::Catalog.new path = tmpfile("exported_files") resource = Puppet::Type.type(:file).new :path => path, :backup => false, :ensure => :file resource.exported = true catalog.add_resource resource catalog.apply FileTest.should be_exist(path) end it "should not apply virtual exported resources" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false resource.exported = true resource.virtual = true catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) resource.expects(:evaluate).never transaction.evaluate end it "should not apply device resources on normal host" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:interface).new :name => "FastEthernet 0/1" catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = false transaction.expects(:apply).never.with(resource, nil) transaction.evaluate transaction.resource_status(resource).should be_skipped end it "should not apply host resources on device" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = true transaction.expects(:apply).never.with(resource, nil) transaction.evaluate transaction.resource_status(resource).should be_skipped end it "should apply device resources on device" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:interface).new :name => "FastEthernet 0/1" catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = true transaction.expects(:apply).with(resource, nil) transaction.evaluate transaction.resource_status(resource).should_not be_skipped end it "should apply resources appliable on host and device on a device" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:schedule).new :name => "test" catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = true transaction.expects(:apply).with(resource, nil) transaction.evaluate transaction.resource_status(resource).should_not be_skipped end # Verify that one component requiring another causes the contained # resources in the requiring component to get refreshed. it "should propagate events from a contained resource through its container to its dependent container's contained resources" do transaction = nil file = Puppet::Type.type(:file).new :path => tmpfile("event_propagation"), :ensure => :present execfile = File.join(tmpdir("exec_event"), "exectestingness2") exec = Puppet::Type.type(:exec).new :command => touch(execfile), :path => ENV['PATH'] catalog = mk_catalog(file) fcomp = Puppet::Type.type(:component).new(:name => "Foo[file]") catalog.add_resource fcomp catalog.add_edge(fcomp, file) ecomp = Puppet::Type.type(:component).new(:name => "Foo[exec]") catalog.add_resource ecomp catalog.add_resource exec catalog.add_edge(ecomp, exec) ecomp[:subscribe] = Puppet::Resource.new(:foo, "file") exec[:refreshonly] = true exec.expects(:refresh) catalog.apply end # Make sure that multiple subscriptions get triggered. it "should propagate events to all dependent resources" do path = tmpfile("path") file1 = tmpfile("file1") file2 = tmpfile("file2") file = Puppet::Type.type(:file).new( :path => path, :ensure => "file" ) exec1 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => touch(file1), :refreshonly => true, :subscribe => Puppet::Resource.new(:file, path) ) exec2 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => touch(file2), :refreshonly => true, :subscribe => Puppet::Resource.new(:file, path) ) catalog = mk_catalog(file, exec1, exec2) catalog.apply FileTest.should be_exist(file1) FileTest.should be_exist(file2) end it "should not let one failed refresh result in other refreshes failing" do path = tmpfile("path") newfile = tmpfile("file") file = Puppet::Type.type(:file).new( :path => path, :ensure => "file" ) exec1 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => touch(File.expand_path("/this/cannot/possibly/exist")), :logoutput => true, :refreshonly => true, :subscribe => file, :title => "one" ) exec2 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => touch(newfile), :logoutput => true, :refreshonly => true, :subscribe => [file, exec1], :title => "two" ) exec1.stubs(:err) catalog = mk_catalog(file, exec1, exec2) catalog.apply FileTest.should be_exists(newfile) end it "should still trigger skipped resources" do catalog = mk_catalog catalog.add_resource(*Puppet::Type.type(:schedule).mkdefaultschedules) Puppet[:ignoreschedules] = false file = Puppet::Type.type(:file).new( :name => tmpfile("file"), :ensure => "file", :backup => false ) fname = tmpfile("exec") exec = Puppet::Type.type(:exec).new( :name => touch(fname), :path => Puppet.features.microsoft_windows? ? "#{ENV['windir']}/system32" : "/usr/bin:/bin", :schedule => "monthly", :subscribe => Puppet::Resource.new("file", file.name) ) catalog.add_resource(file, exec) # Run it once catalog.apply FileTest.should be_exists(fname) # Now remove it, so it can get created again File.unlink(fname) file[:content] = "some content" catalog.apply FileTest.should be_exists(fname) # Now remove it, so it can get created again File.unlink(fname) # And tag our exec exec.tag("testrun") # And our file, so it runs file.tag("norun") Puppet[:tags] = "norun" file[:content] = "totally different content" catalog.apply FileTest.should be_exists(fname) end it "should not attempt to evaluate resources with failed dependencies" do exec = Puppet::Type.type(:exec).new( :command => "#{File.expand_path('/bin/mkdir')} /this/path/cannot/possibly/exist", :title => "mkdir" ) file1 = Puppet::Type.type(:file).new( :title => "file1", :path => tmpfile("file1"), :require => exec, :ensure => :file ) file2 = Puppet::Type.type(:file).new( :title => "file2", :path => tmpfile("file2"), :require => file1, :ensure => :file ) catalog = mk_catalog(exec, file1, file2) catalog.apply FileTest.should_not be_exists(file1[:path]) FileTest.should_not be_exists(file2[:path]) end it "should not trigger subscribing resources on failure" do file1 = tmpfile("file1") file2 = tmpfile("file2") create_file1 = Puppet::Type.type(:exec).new( :command => usr_bin_touch(file1) ) exec = Puppet::Type.type(:exec).new( :command => "#{File.expand_path('/bin/mkdir')} /this/path/cannot/possibly/exist", :title => "mkdir", :notify => create_file1 ) create_file2 = Puppet::Type.type(:exec).new( :command => usr_bin_touch(file2), :subscribe => exec ) catalog = mk_catalog(exec, create_file1, create_file2) catalog.apply FileTest.should_not be_exists(file1) FileTest.should_not be_exists(file2) end # #801 -- resources only checked in noop should be rescheduled immediately. it "should immediately reschedule noop resources" do Puppet::Type.type(:schedule).mkdefaultschedules resource = Puppet::Type.type(:notify).new(:name => "mymessage", :noop => true) catalog = Puppet::Resource::Catalog.new catalog.add_resource resource trans = catalog.apply trans.resource_harness.should be_scheduled(trans.resource_status(resource), resource) end end diff --git a/spec/integration/type/exec_spec.rb b/spec/integration/type/exec_spec.rb index 3a29dead7..beefb50df 100755 --- a/spec/integration/type/exec_spec.rb +++ b/spec/integration/type/exec_spec.rb @@ -1,77 +1,77 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Type.type(:exec) do include PuppetSpec::Files let(:catalog) { Puppet::Resource::Catalog.new } let(:path) { tmpfile('exec_provider') } let(:command) { "ruby -e 'File.open(\"#{path}\", \"w\") { |f| f.print \"foo\" }'" } before :each do catalog.host_config = false end it "should execute the command" do exec = described_class.new :command => command, :path => ENV['PATH'] catalog.add_resource exec catalog.apply File.read(path).should == 'foo' end it "should not execute the command if onlyif returns non-zero" do exec = described_class.new( :command => command, :onlyif => "ruby -e 'exit 44'", :path => ENV['PATH'] ) catalog.add_resource exec catalog.apply File.should_not be_exist(path) end it "should execute the command if onlyif returns zero" do exec = described_class.new( :command => command, :onlyif => "ruby -e 'exit 0'", :path => ENV['PATH'] ) catalog.add_resource exec catalog.apply File.read(path).should == 'foo' end it "should execute the command if unless returns non-zero" do exec = described_class.new( :command => command, :unless => "ruby -e 'exit 45'", :path => ENV['PATH'] ) catalog.add_resource exec catalog.apply File.read(path).should == 'foo' end it "should not execute the command if unless returns zero" do exec = described_class.new( :command => command, :unless => "ruby -e 'exit 0'", :path => ENV['PATH'] ) catalog.add_resource exec catalog.apply File.should_not be_exist(path) end end diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index f7ec1819c..28e1d038d 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1,1035 +1,1035 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' if Puppet.features.microsoft_windows? require 'puppet/util/windows' class WindowsSecurity extend Puppet::Util::Windows::Security end end describe Puppet::Type.type(:file) do include PuppetSpec::Files let(:catalog) { Puppet::Resource::Catalog.new } let(:path) { tmpfile('file_testing') } if Puppet.features.posix? def set_mode(mode, file) File.chmod(mode, file) end def get_mode(file) File.lstat(file).mode end def get_owner(file) File.lstat(file).uid end def get_group(file) File.lstat(file).gid end else class SecurityHelper extend Puppet::Util::Windows::Security end def set_mode(mode, file) SecurityHelper.set_mode(mode, file) end def get_mode(file) SecurityHelper.get_mode(file) end def get_owner(file) SecurityHelper.get_owner(file) end def get_group(file) SecurityHelper.get_group(file) end end before do # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end it "should not attempt to manage files that do not exist if no means of creating the file is specified" do source = tmpfile('source') catalog.add_resource described_class.new :path => source, :mode => 0755 report = catalog.apply.report report.resource_statuses["File[#{source}]"].should_not be_failed File.should_not be_exist(source) end describe "when setting permissions" do it "should set the owner" do target = tmpfile_with_contents('target', '') owner = get_owner(target) catalog.add_resource described_class.new( :name => target, :owner => owner ) catalog.apply get_owner(target).should == owner end it "should set the group" do target = tmpfile_with_contents('target', '') group = get_group(target) catalog.add_resource described_class.new( :name => target, :group => group ) catalog.apply get_group(target).should == group end describe "when setting mode" do describe "for directories" do let(:target) { tmpdir('dir_mode') } it "should set executable bits for newly created directories" do catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0600) catalog.apply (get_mode(target) & 07777).should == 0700 end it "should set executable bits for existing readable directories" do set_mode(0600, target) catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0644) catalog.apply (get_mode(target) & 07777).should == 0755 end it "should not set executable bits for unreadable directories" do begin catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0300) catalog.apply (get_mode(target) & 07777).should == 0300 ensure # so we can cleanup set_mode(0700, target) end end it "should set user, group, and other executable bits" do catalog.add_resource described_class.new(:path => target, :ensure => :directory, :mode => 0664) catalog.apply (get_mode(target) & 07777).should == 0775 end it "should set executable bits when overwriting a non-executable file" do target_path = tmpfile_with_contents('executable', '') set_mode(0444, target_path) catalog.add_resource described_class.new(:path => target_path, :ensure => :directory, :mode => 0666, :backup => false) catalog.apply (get_mode(target_path) & 07777).should == 0777 File.should be_directory(target_path) end end describe "for files" do it "should not set executable bits" do catalog.add_resource described_class.new(:path => path, :ensure => :file, :mode => 0666) catalog.apply (get_mode(path) & 07777).should == 0666 end it "should not set executable bits when replacing an executable directory (#10365)" do pending("bug #10365") FileUtils.mkdir(path) set_mode(0777, path) catalog.add_resource described_class.new(:path => path, :ensure => :file, :mode => 0666, :backup => false, :force => true) catalog.apply (get_mode(path) & 07777).should == 0666 end end describe "for links", :unless => Puppet.features.microsoft_windows? do let(:link) { tmpfile('link_mode') } describe "when managing links" do let(:link_target) { tmpfile('target') } before :each do FileUtils.touch(link_target) File.chmod(0444, link_target) File.symlink(link_target, link) end it "should not set the executable bit on the link nor the target" do catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => 0666, :target => link_target, :links => :manage) catalog.apply (File.stat(link).mode & 07777) == 0666 (File.lstat(link_target).mode & 07777) == 0444 end it "should ignore dangling symlinks (#6856)" do File.delete(link_target) catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => 0666, :target => link_target, :links => :manage) catalog.apply File.should_not be_exist(link) end end describe "when following links" do it "should ignore dangling symlinks (#6856)" do target = tmpfile('dangling') FileUtils.touch(target) File.symlink(target, link) File.delete(target) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply end describe "to a directory" do let(:link_target) { tmpdir('dir_target') } before :each do File.chmod(0600, link_target) File.symlink(link_target, link) end after :each do File.chmod(0750, link_target) end describe "that is readable" do it "should set the executable bits when creating the destination (#10315)" do pending "bug #10315" catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow) catalog.apply (get_mode(path) & 07777).should == 0777 end it "should set the executable bits when overwriting the destination (#10315)" do pending "bug #10315" FileUtils.touch(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow) catalog.apply (get_mode(path) & 07777).should == 0777 end end describe "that is not readable" do before :each do set_mode(0300, link_target) end # so we can cleanup after :each do set_mode(0700, link_target) end it "should not set executable bits when creating the destination (#10315)" do pending "bug #10315" catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow) catalog.apply (get_mode(path) & 07777).should == 0666 end it "should not set executable bits when overwriting the destination" do FileUtils.touch(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0666, :links => :follow) catalog.apply (get_mode(path) & 07777).should == 0666 end end end describe "to a file" do let(:target) { tmpfile('file_target') } it "should create the file, not a symlink (#2817, #10315)" do pending "bug #2817, #10315" catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_file(path) (get_mode(path) & 07777) == 0600 end it "should overwrite the file" do FileUtils.touch(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_file(path) (get_mode(path) & 07777) == 0600 end end describe "to a link to a directory" do let(:real_target) { tmpdir('real_target') } let(:target) { tmpfile('target') } before :each do File.chmod(0666, real_target) # link -> target -> real_target File.symlink(real_target, target) File.symlink(target, link) end after :each do File.chmod(0750, real_target) end describe "when following all links" do it "should create the destination and apply executable bits (#10315)" do pending "bug #10315" catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_directory(path) (get_mode(path) & 07777) == 0777 end it "should overwrite the destination and apply executable bits" do FileUtils.mkdir(path) catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow) catalog.apply File.should be_directory(path) (get_mode(path) & 07777) == 0777 end end end end end end end describe "when writing files" do it "should backup files to a filebucket when one is configured" do filebucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = described_class.new :path => path, :backup => "mybucket", :content => "foo" catalog.add_resource file catalog.add_resource filebucket File.open(file[:path], "wb") { |f| f.puts "bar" } md5 = Digest::MD5.hexdigest(IO.binread(file[:path])) catalog.apply filebucket.bucket.getfile(md5).should == "bar\n" end it "should backup files in the local directory when a backup string is provided" do file = described_class.new :path => path, :backup => ".bak", :content => "foo" catalog.add_resource file File.open(file[:path], "w") { |f| f.puts "bar" } catalog.apply backup = file[:path] + ".bak" FileTest.should be_exist(backup) File.read(backup).should == "bar\n" end it "should fail if no backup can be performed" do dir = tmpfile("backups") Dir.mkdir(dir) file = described_class.new :path => File.join(dir, "testfile"), :backup => ".bak", :content => "foo" catalog.add_resource file File.open(file[:path], 'w') { |f| f.puts "bar" } # Create a directory where the backup should be so that writing to it fails Dir.mkdir(File.join(dir, "testfile.bak")) Puppet::Util::Log.stubs(:newmessage) catalog.apply File.read(file[:path]).should == "bar\n" end it "should not backup symlinks", :unless => Puppet.features.microsoft_windows? do link = tmpfile("link") dest1 = tmpfile("dest1") dest2 = tmpfile("dest2") bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = described_class.new :path => link, :target => dest2, :ensure => :link, :backup => "mybucket" catalog.add_resource file catalog.add_resource bucket File.open(dest1, "w") { |f| f.puts "whatever" } File.symlink(dest1, link) md5 = Digest::MD5.hexdigest(File.read(file[:path])) catalog.apply File.readlink(link).should == dest2 Find.find(bucket[:path]) { |f| File.file?(f) }.should be_nil end it "should backup directories to the local filesystem by copying the whole directory" do file = described_class.new :path => path, :backup => ".bak", :content => "foo", :force => true catalog.add_resource file Dir.mkdir(path) otherfile = File.join(path, "foo") File.open(otherfile, "w") { |f| f.print "yay" } catalog.apply backup = "#{path}.bak" FileTest.should be_directory(backup) File.read(File.join(backup, "foo")).should == "yay" end it "should backup directories to filebuckets by backing up each file separately" do bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = described_class.new :path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo", :force => true catalog.add_resource file catalog.add_resource bucket Dir.mkdir(file[:path]) foofile = File.join(file[:path], "foo") barfile = File.join(file[:path], "bar") File.open(foofile, "w") { |f| f.print "fooyay" } File.open(barfile, "w") { |f| f.print "baryay" } foomd5 = Digest::MD5.hexdigest(File.read(foofile)) barmd5 = Digest::MD5.hexdigest(File.read(barfile)) catalog.apply bucket.bucket.getfile(foomd5).should == "fooyay" bucket.bucket.getfile(barmd5).should == "baryay" end it "should propagate failures encountered when renaming the temporary file" do file = described_class.new :path => path, :content => "foo" file.stubs(:perform_backup).returns(true) catalog.add_resource file File.open(path, "w") { |f| f.print "bar" } File.expects(:rename).raises ArgumentError expect { file.write(:content) }.to raise_error(Puppet::Error, /Could not rename temporary file/) File.read(path).should == "bar" end end describe "when recursing" do def build_path(dir) Dir.mkdir(dir) File.chmod(0750, dir) @dirs = [dir] @files = [] %w{one two}.each do |subdir| fdir = File.join(dir, subdir) Dir.mkdir(fdir) File.chmod(0750, fdir) @dirs << fdir %w{three}.each do |file| ffile = File.join(fdir, file) @files << ffile File.open(ffile, "w") { |f| f.puts "test #{file}" } File.chmod(0640, ffile) end end end it "should be able to recurse over a nonexistent file" do @file = described_class.new( :name => path, :mode => 0644, :recurse => true, :backup => false ) catalog.add_resource @file lambda { @file.eval_generate }.should_not raise_error end it "should be able to recursively set properties on existing files" do path = tmpfile("file_integration_tests") build_path(path) file = described_class.new( :name => path, :mode => 0644, :recurse => true, :backup => false ) catalog.add_resource file catalog.apply @dirs.should_not be_empty @dirs.each do |path| (get_mode(path) & 007777).should == 0755 end @files.should_not be_empty @files.each do |path| (get_mode(path) & 007777).should == 0644 end end it "should be able to recursively make links to other files", :unless => Puppet.features.microsoft_windows? do source = tmpfile("file_link_integration_source") build_path(source) dest = tmpfile("file_link_integration_dest") @file = described_class.new(:name => dest, :target => source, :recurse => true, :ensure => :link, :backup => false) catalog.add_resource @file catalog.apply @dirs.each do |path| link_path = path.sub(source, dest) File.lstat(link_path).should be_directory end @files.each do |path| link_path = path.sub(source, dest) File.lstat(link_path).ftype.should == "link" end end it "should be able to recursively copy files" do source = tmpfile("file_source_integration_source") build_path(source) dest = tmpfile("file_source_integration_dest") @file = described_class.new(:name => dest, :source => source, :recurse => true, :backup => false) catalog.add_resource @file catalog.apply @dirs.each do |path| newpath = path.sub(source, dest) File.lstat(newpath).should be_directory end @files.each do |path| newpath = path.sub(source, dest) File.lstat(newpath).ftype.should == "file" end end it "should not recursively manage files managed by a more specific explicit file" do dir = tmpfile("recursion_vs_explicit_1") subdir = File.join(dir, "subdir") file = File.join(subdir, "file") FileUtils.mkdir_p(subdir) File.open(file, "w") { |f| f.puts "" } base = described_class.new(:name => dir, :recurse => true, :backup => false, :mode => "755") sub = described_class.new(:name => subdir, :recurse => true, :backup => false, :mode => "644") catalog.add_resource base catalog.add_resource sub catalog.apply (get_mode(file) & 007777).should == 0644 end it "should recursively manage files even if there is an explicit file whose name is a prefix of the managed file" do managed = File.join(path, "file") generated = File.join(path, "file_with_a_name_starting_with_the_word_file") managed_mode = 0700 FileUtils.mkdir_p(path) FileUtils.touch(managed) FileUtils.touch(generated) catalog.add_resource described_class.new(:name => path, :recurse => true, :backup => false, :mode => managed_mode) catalog.add_resource described_class.new(:name => managed, :recurse => true, :backup => false, :mode => "644") catalog.apply (get_mode(generated) & 007777).should == managed_mode end describe "when recursing remote directories" do describe "when sourceselect first" do describe "for a directory" do it "should recursively copy the first directory that exists" do one = File.expand_path('thisdoesnotexist') two = tmpdir('two') FileUtils.mkdir_p(File.join(two, 'three')) FileUtils.touch(File.join(two, 'three', 'four')) catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :sourceselect => :first, :source => [one, two] ) catalog.apply File.should be_directory(path) File.should_not be_exist(File.join(path, 'one')) File.should be_exist(File.join(path, 'three', 'four')) end it "should recursively copy an empty directory" do one = File.expand_path('thisdoesnotexist') two = tmpdir('two') three = tmpdir('three') file_in_dir_with_contents(three, 'a', '') catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :sourceselect => :first, :source => [one, two, three] ) catalog.apply File.should be_directory(path) File.should_not be_exist(File.join(path, 'a')) end it "should only recurse one level" do one = tmpdir('one') FileUtils.mkdir_p(File.join(one, 'a', 'b')) FileUtils.touch(File.join(one, 'a', 'b', 'c')) two = tmpdir('two') FileUtils.mkdir_p(File.join(two, 'z')) FileUtils.touch(File.join(two, 'z', 'y')) catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :recurselimit => 1, :sourceselect => :first, :source => [one, two] ) catalog.apply File.should be_exist(File.join(path, 'a')) File.should_not be_exist(File.join(path, 'a', 'b')) File.should_not be_exist(File.join(path, 'z')) end end describe "for a file" do it "should copy the first file that exists" do one = File.expand_path('thisdoesnotexist') two = tmpfile_with_contents('two', 'yay') three = tmpfile_with_contents('three', 'no') catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :file, :backup => false, :sourceselect => :first, :source => [one, two, three] ) catalog.apply File.read(path).should == 'yay' end it "should copy an empty file" do one = File.expand_path('thisdoesnotexist') two = tmpfile_with_contents('two', '') three = tmpfile_with_contents('three', 'no') catalog.add_resource Puppet::Type.newfile( :path => path, :ensure => :file, :backup => false, :sourceselect => :first, :source => [one, two, three] ) catalog.apply File.read(path).should == '' end end end describe "when sourceselect all" do describe "for a directory" do it "should recursively copy all sources from the first valid source" do dest = tmpdir('dest') one = tmpdir('one') two = tmpdir('two') three = tmpdir('three') four = tmpdir('four') file_in_dir_with_contents(one, 'a', one) file_in_dir_with_contents(two, 'a', two) file_in_dir_with_contents(two, 'b', two) file_in_dir_with_contents(three, 'a', three) file_in_dir_with_contents(three, 'c', three) obj = Puppet::Type.newfile( :path => dest, :ensure => :directory, :backup => false, :recurse => true, :sourceselect => :all, :source => [one, two, three, four] ) catalog.add_resource obj catalog.apply File.read(File.join(dest, 'a')).should == one File.read(File.join(dest, 'b')).should == two File.read(File.join(dest, 'c')).should == three end it "should only recurse one level from each valid source" do one = tmpdir('one') FileUtils.mkdir_p(File.join(one, 'a', 'b')) FileUtils.touch(File.join(one, 'a', 'b', 'c')) two = tmpdir('two') FileUtils.mkdir_p(File.join(two, 'z')) FileUtils.touch(File.join(two, 'z', 'y')) obj = Puppet::Type.newfile( :path => path, :ensure => :directory, :backup => false, :recurse => true, :recurselimit => 1, :sourceselect => :all, :source => [one, two] ) catalog.add_resource obj catalog.apply File.should be_exist(File.join(path, 'a')) File.should_not be_exist(File.join(path, 'a', 'b')) File.should be_exist(File.join(path, 'z')) File.should_not be_exist(File.join(path, 'z', 'y')) end end end end end describe "when generating resources" do before do source = tmpdir("generating_in_catalog_source") s1 = file_in_dir_with_contents(source, "one", "uno") s2 = file_in_dir_with_contents(source, "two", "dos") @file = described_class.new( :name => path, :source => source, :recurse => true, :backup => false ) catalog.add_resource @file end it "should add each generated resource to the catalog" do catalog.apply do |trans| catalog.resource(:file, File.join(path, "one")).should be_a(described_class) catalog.resource(:file, File.join(path, "two")).should be_a(described_class) end end it "should have an edge to each resource in the relationship graph" do catalog.apply do |trans| one = catalog.resource(:file, File.join(path, "one")) catalog.relationship_graph.should be_edge(@file, one) two = catalog.resource(:file, File.join(path, "two")) catalog.relationship_graph.should be_edge(@file, two) end end end describe "when copying files" do it "should be able to copy files with pound signs in their names (#285)" do source = tmpfile_with_contents("filewith#signs", "foo") dest = tmpfile("destwith#signs") catalog.add_resource described_class.new(:name => dest, :source => source) catalog.apply File.read(dest).should == "foo" end it "should be able to copy files with spaces in their names" do dest = tmpfile("destwith spaces") source = tmpfile_with_contents("filewith spaces", "foo") File.chmod(0755, source) catalog.add_resource described_class.new(:path => dest, :source => source) catalog.apply expected_mode = Puppet.features.microsoft_windows? ? 0644 : 0755 File.read(dest).should == "foo" (File.stat(dest).mode & 007777).should == expected_mode end it "should be able to copy individual files even if recurse has been specified" do source = tmpfile_with_contents("source", "foo") dest = tmpfile("dest") catalog.add_resource described_class.new(:name => dest, :source => source, :recurse => true) catalog.apply File.read(dest).should == "foo" end end it "should create a file with content if ensure is omitted" do catalog.add_resource described_class.new( :path => path, :content => "this is some content, yo" ) catalog.apply File.read(path).should == "this is some content, yo" end it "should create files with content if both content and ensure are set" do file = described_class.new( :path => path, :ensure => "file", :content => "this is some content, yo" ) catalog.add_resource file catalog.apply File.read(path).should == "this is some content, yo" end it "should delete files with sources but that are set for deletion" do source = tmpfile_with_contents("source_source_with_ensure", "yay") dest = tmpfile_with_contents("source_source_with_ensure", "boo") file = described_class.new( :path => dest, :ensure => :absent, :source => source, :backup => false ) catalog.add_resource file catalog.apply File.should_not be_exist(dest) end describe "when sourcing" do let(:source) { tmpfile_with_contents("source_default_values", "yay") } it "should apply the source metadata values" do set_mode(0770, source) file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false ) catalog.add_resource file catalog.apply get_owner(path).should == get_owner(source) get_group(path).should == get_group(source) (get_mode(path) & 07777).should == 0770 end it "should override the default metadata values" do set_mode(0770, source) file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false, :mode => 0440 ) catalog.add_resource file catalog.apply (get_mode(path) & 07777).should == 0440 end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do it "should provide valid default values when ACLs are not supported" do Puppet::Util::Windows::Security.stubs(:supports_acl?).with(source).returns false file = described_class.new( :path => path, :ensure => :file, :source => source, :backup => false ) catalog.add_resource file catalog.apply get_owner(path).should == 'S-1-5-32-544' get_group(path).should == 'S-1-0-0' get_mode(path).should == 0644 end end end describe "when purging files" do before do sourcedir = tmpdir("purge_source") destdir = tmpdir("purge_dest") sourcefile = File.join(sourcedir, "sourcefile") @copiedfile = File.join(destdir, "sourcefile") @localfile = File.join(destdir, "localfile") @purgee = File.join(destdir, "to_be_purged") File.open(@localfile, "w") { |f| f.print "oldtest" } File.open(sourcefile, "w") { |f| f.print "funtest" } # this file should get removed File.open(@purgee, "w") { |f| f.print "footest" } lfobj = Puppet::Type.newfile( :title => "localfile", :path => @localfile, :content => "rahtest", :ensure => :file, :backup => false ) destobj = Puppet::Type.newfile( :title => "destdir", :path => destdir, :source => sourcedir, :backup => false, :purge => true, :recurse => true ) catalog.add_resource lfobj, destobj catalog.apply end it "should still copy remote files" do File.read(@copiedfile).should == 'funtest' end it "should not purge managed, local files" do File.read(@localfile).should == 'rahtest' end it "should purge files that are neither remote nor otherwise managed" do FileTest.should_not be_exist(@purgee) end end def tmpfile_with_contents(name, contents) file = tmpfile(name) File.open(file, "w") { |f| f.write contents } file end def file_in_dir_with_contents(dir, name, contents) full_name = File.join(dir, name) File.open(full_name, "w") { |f| f.write contents } full_name end end diff --git a/spec/integration/type/package_spec.rb b/spec/integration/type/package_spec.rb index 7d03cb9c0..5fdaf1b89 100755 --- a/spec/integration/type/package_spec.rb +++ b/spec/integration/type/package_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package), "when choosing a default package provider" do before do # the default provider is cached. Puppet::Type.type(:package).defaultprovider = nil end def provider_name(os) {"Ubuntu" => :apt, "Debian" => :apt, "Darwin" => :pkgdmg, "RedHat" => :up2date, "Fedora" => :yum, "FreeBSD" => :ports, "OpenBSD" => :openbsd, "Solaris" => :sun}[os] end it "should have a default provider" do Puppet::Type.type(:package).defaultprovider.should_not be_nil end it "should choose the correct provider each platform" do unless default_provider = provider_name(Facter.value(:operatingsystem)) pending("No default provider specified in this test for #{Facter.value(:operatingsystem)}") end Puppet::Type.type(:package).defaultprovider.name.should == default_provider end end diff --git a/spec/integration/type/tidy_spec.rb b/spec/integration/type/tidy_spec.rb index d1bb62d6e..56003861c 100755 --- a/spec/integration/type/tidy_spec.rb +++ b/spec/integration/type/tidy_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet/file_bucket/dipper' describe Puppet::Type.type(:tidy) do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end # Testing #355. it "should be able to remove dead links", :unless => Puppet.features.microsoft_windows? do dir = tmpfile("tidy_link_testing") link = File.join(dir, "link") target = tmpfile("no_such_file_tidy_link_testing") Dir.mkdir(dir) File.symlink(target, link) tidy = Puppet::Type.type(:tidy).new :path => dir, :recurse => true catalog = Puppet::Resource::Catalog.new catalog.add_resource(tidy) catalog.apply FileTest.should_not be_symlink(link) end end diff --git a/spec/integration/type_spec.rb b/spec/integration/type_spec.rb index 9fd10485f..4677a75de 100755 --- a/spec/integration/type_spec.rb +++ b/spec/integration/type_spec.rb @@ -1,32 +1,32 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/type' describe Puppet::Type do it "should not lose its provider list when it is reloaded" do type = Puppet::Type.newtype(:integration_test) do newparam(:name) {} end provider = type.provide(:myprovider) {} # reload it type = Puppet::Type.newtype(:integration_test) do newparam(:name) {} end type.provider(:myprovider).should equal(provider) end it "should not lose its provider parameter when it is reloaded" do type = Puppet::Type.newtype(:reload_test_type) provider = type.provide(:test_provider) # reload it type = Puppet::Type.newtype(:reload_test_type) type.parameters.should include(:provider) end end diff --git a/spec/integration/util/autoload_spec.rb b/spec/integration/util/autoload_spec.rb index b5f1455a4..dc3160db2 100755 --- a/spec/integration/util/autoload_spec.rb +++ b/spec/integration/util/autoload_spec.rb @@ -1,107 +1,107 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/autoload' require 'fileutils' class AutoloadIntegrator @things = [] def self.newthing(name) @things << name end def self.thing?(name) @things.include? name end def self.clear @things.clear end end require 'puppet_spec/files' describe Puppet::Util::Autoload do include PuppetSpec::Files def with_file(name, *path) path = File.join(*path) # Now create a file to load File.open(path, "w") { |f| f.puts "\nAutoloadIntegrator.newthing(:#{name.to_s})\n" } yield File.delete(path) end def with_loader(name, path) dir = tmpfile(name + path) $LOAD_PATH << dir Dir.mkdir(dir) rbdir = File.join(dir, path.to_s) Dir.mkdir(rbdir) loader = Puppet::Util::Autoload.new(name, path) yield rbdir, loader Dir.rmdir(rbdir) Dir.rmdir(dir) $LOAD_PATH.pop AutoloadIntegrator.clear end it "should make instances available by the loading class" do loader = Puppet::Util::Autoload.new("foo", "bar") Puppet::Util::Autoload["foo"].should == loader end it "should not fail when asked to load a missing file" do Puppet::Util::Autoload.new("foo", "bar").load(:eh).should be_false end it "should load and return true when it successfully loads a file" do with_loader("foo", "bar") { |dir,loader| with_file(:mything, dir, "mything.rb") { loader.load(:mything).should be_true loader.class.should be_loaded("bar/mything") AutoloadIntegrator.should be_thing(:mything) } } end it "should consider a file loaded when asked for the name without an extension" do with_loader("foo", "bar") { |dir,loader| with_file(:noext, dir, "noext.rb") { loader.load(:noext) loader.class.should be_loaded("bar/noext") } } end it "should consider a file loaded when asked for the name with an extension" do with_loader("foo", "bar") { |dir,loader| with_file(:noext, dir, "withext.rb") { loader.load(:withext) loader.class.should be_loaded("bar/withext.rb") } } end it "should be able to load files directly from modules" do ## modulepath can't be used until after app settings are initialized, so we need to simulate that: Puppet.settings.expects(:app_defaults_initialized?).returns(true).at_least_once modulepath = tmpfile("autoload_module_testing") libdir = File.join(modulepath, "mymod", "lib", "foo") FileUtils.mkdir_p(libdir) file = File.join(libdir, "plugin.rb") Puppet[:modulepath] = modulepath with_loader("foo", "foo") do |dir, loader| with_file(:plugin, file.split("/")) do loader.load(:plugin) loader.class.should be_loaded("foo/plugin.rb") end end end end diff --git a/spec/integration/util/feature_spec.rb b/spec/integration/util/feature_spec.rb index c3c13764d..4c58ab62b 100755 --- a/spec/integration/util/feature_spec.rb +++ b/spec/integration/util/feature_spec.rb @@ -1,53 +1,53 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/feature' require 'puppet_spec/files' describe Puppet::Util::Feature do include PuppetSpec::Files it "should be able to load features from disk" do libdir = tmpfile("feature_lib") Dir.mkdir(libdir) $LOAD_PATH << libdir $features = Puppet::Util::Feature.new("feature_lib") Dir.mkdir(File.join(libdir, "feature_lib")) File.open(File.join(libdir, "feature_lib", "able_to_load.rb"), "w") do |f| f.puts "$features.add(:able_to_load) { true }" end $features.should be_able_to_load end # TODO: Make this a spec test or remove it. def test_dynamic_loading $features = @features cleanup { $features = nil } # Now create a feature and make sure it loads. FileUtils.mkdir_p(@path) nope = File.join(@path, "nope.rb") File.open(nope, "w") { |f| f.puts "$features.add(:nope, :libs => %w{nosuchlib})" } assert_nothing_raised("Failed to autoload features") do assert(! @features.nope?, "'nope' returned true") end # First make sure "yep?" returns false assert_nothing_raised("Missing feature threw an exception") do assert(! @features.notyep?, "'notyep' returned true before definition") end yep = File.join(@path, "yep.rb") File.open(yep, "w") { |f| f.puts "$features.add(:yep, :libs => %w{puppet})" } assert(@features.yep?, "false 'yep' is apparently cached or feature could not be loaded") end end diff --git a/spec/integration/util/rdoc/parser_spec.rb b/spec/integration/util/rdoc/parser_spec.rb index 90dab749f..523e35c53 100755 --- a/spec/integration/util/rdoc/parser_spec.rb +++ b/spec/integration/util/rdoc/parser_spec.rb @@ -1,60 +1,60 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "RDoc::Parser", :if => Puppet.features.rdoc1? do require 'puppet_spec/files' include PuppetSpec::Files before :all do require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' end before :each do tmpdir = tmpfile('rdoc_parser_tmp') Dir.mkdir(tmpdir) @parsedfile = File.join(tmpdir, 'init.pp') File.open(@parsedfile, 'w') do |f| f.puts '# comment' f.puts 'class ::test {}' end @top_level = stub_everything 'toplevel', :file_relative_name => @parsedfile @module = stub_everything 'module' @puppet_top_level = RDoc::PuppetTopLevel.new(@top_level) RDoc::PuppetTopLevel.stubs(:new).returns(@puppet_top_level) @puppet_top_level.expects(:add_module).returns(@module) @parser = RDoc::Parser.new(@top_level, @parsedfile, nil, Options.instance, RDoc::Stats.new) end after(:each) do File.unlink(@parsedfile) end def get_test_class(toplevel) # toplevel -> main -> test toplevel.classes[0].classes[0] end it "should parse to RDoc data structure" do @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Resource::Type) } @parser.scan end it "should get a PuppetClass for the main class" do @parser.scan.classes[0].should be_a(RDoc::PuppetClass) end it "should produce a PuppetClass whose name is test" do get_test_class(@parser.scan).name.should == "test" end it "should produce a PuppetClass whose comment is 'comment'" do get_test_class(@parser.scan).comment.should == "comment\n" end end diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index e84e67896..2e5842ca5 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -1,42 +1,42 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Settings do include PuppetSpec::Files def minimal_default_settings { :noop => {:default => false, :desc => "noop"} } end it "should be able to make needed directories" do settings = Puppet::Settings.new settings.define_settings :main, minimal_default_settings.update( :maindir => { :default => tmpfile("main"), :type => :directory, :desc => "a", } ) settings.use(:main) File.should be_directory(settings[:maindir]) end it "should make its directories with the correct modes" do settings = Puppet::Settings.new settings.define_settings :main, minimal_default_settings.update( :maindir => { :default => tmpfile("main"), :type => :directory, :desc => "a", :mode => 0750 } ) settings.use(:main) (File.stat(settings[:maindir]).mode & 007777).should == (Puppet.features.microsoft_windows? ? 0755 : 0750) end end diff --git a/spec/lib/puppet_spec/settings.rb b/spec/lib/puppet_spec/settings.rb index fc6bdf92b..739fcf670 100644 --- a/spec/lib/puppet_spec/settings.rb +++ b/spec/lib/puppet_spec/settings.rb @@ -1,16 +1,16 @@ module PuppetSpec::Settings # It would probably be preferable to refactor defaults.rb such that the real definitions of # these settings were available as a variable, which was then accessible for use during tests. # However, I'm not doing that yet because I don't want to introduce any additional moving parts # to this already very large changeset. # Would be nice to clean this up later. --cprice 2012-03-20 TEST_APP_DEFAULT_DEFINITIONS = { :run_mode => { :default => :test, :desc => "run mode" }, :name => { :default => "test", :desc => "name" }, :logdir => { :type => :directory, :default => "test", :desc => "logdir" }, :confdir => { :type => :directory, :default => "test", :desc => "confdir" }, :vardir => { :type => :directory, :default => "test", :desc => "vardir" }, :rundir => { :type => :directory, :default => "test", :desc => "rundir" }, } -end \ No newline at end of file +end diff --git a/spec/shared_behaviours/file_server_terminus.rb b/spec/shared_behaviours/file_server_terminus.rb index 3b95462f3..d769d0d87 100755 --- a/spec/shared_behaviours/file_server_terminus.rb +++ b/spec/shared_behaviours/file_server_terminus.rb @@ -1,41 +1,41 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec shared_examples_for "Puppet::Indirector::FileServerTerminus" do # This only works if the shared behaviour is included before # the 'before' block in the including context. before do Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil) FileTest.stubs(:exists?).returns true FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true) @path = Tempfile.new("file_server_testing") path = @path.path @path.close! @path = path Dir.mkdir(@path) File.open(File.join(@path, "myfile"), "w") { |f| f.print "my content" } # Use a real mount, so the integration is a bit deeper. @mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") @mount1.path = @path @parser = stub 'parser', :changed? => false @parser.stubs(:parse).returns("one" => @mount1) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) # Stub out the modules terminus @modules = mock 'modules terminus' @request = Puppet::Indirector::Request.new(:indirection, :method, "puppet://myhost/one/myfile", nil) end it "should use the file server configuration to find files" do @modules.stubs(:find).returns(nil) @terminus.indirection.stubs(:terminus).with(:modules).returns(@modules) path = File.join(@path, "myfile") @terminus.find(@request).should be_instance_of(@test_class) end end diff --git a/spec/shared_behaviours/file_serving.rb b/spec/shared_behaviours/file_serving.rb index 8bffa84c8..89515740c 100755 --- a/spec/shared_behaviours/file_serving.rb +++ b/spec/shared_behaviours/file_serving.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec shared_examples_for "Puppet::FileServing::Files" do it "should use the rest terminus when the 'puppet' URI scheme is used and a host name is present" do uri = "puppet://myhost/fakemod/my/file" # It appears that the mocking somehow interferes with the caching subsystem. # This mock somehow causes another terminus to get generated. term = @indirection.terminus(:rest) @indirection.stubs(:terminus).with(:rest).returns term term.expects(:find) @indirection.find(uri) end it "should use the rest terminus when the 'puppet' URI scheme is used, no host name is present, and default_file_terminus is rest" do uri = "puppet:///fakemod/my/file" Puppet[:default_file_terminus] = "rest" @indirection.terminus(:rest).expects(:find) @indirection.find(uri) end it "should use the file_server terminus when the 'puppet' URI scheme is used, no host name is present, and default_file_terminus is file_server" do uri = "puppet:///fakemod/my/file" Puppet::Node::Environment.stubs(:new).returns(stub("env", :name => "testing", :module => nil, :modulepath => [])) Puppet[:default_file_terminus] = "file_server" Puppet[:fileserverconfig] = "/whatever" @indirection.terminus(:file_server).expects(:find) @indirection.terminus(:file_server).stubs(:authorized?).returns(true) @indirection.find(uri) end it "should use the file terminus when the 'file' URI scheme is used" do uri = Puppet::Util.path_to_uri(File.expand_path('/fakemod/my/other file')) uri.scheme.should == 'file' @indirection.terminus(:file).expects(:find) @indirection.find(uri.to_s) end it "should use the file terminus when a fully qualified path is provided" do uri = File.expand_path("/fakemod/my/file") @indirection.terminus(:file).expects(:find) @indirection.find(uri) end it "should use the configuration to test whether the request is allowed" do uri = "fakemod/my/file" mount = mock 'mount' config = stub 'configuration', :split_path => [mount, "eh"] @indirection.terminus(:file_server).stubs(:configuration).returns config @indirection.terminus(:file_server).expects(:find) mount.expects(:allowed?).returns(true) @indirection.find(uri, :node => "foo", :ip => "bar") end end diff --git a/spec/unit/agent/disabler_spec.rb b/spec/unit/agent/disabler_spec.rb index c93be4dc6..dc1ad6642 100644 --- a/spec/unit/agent/disabler_spec.rb +++ b/spec/unit/agent/disabler_spec.rb @@ -1,68 +1,68 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/agent' require 'puppet/agent/locker' class DisablerTester include Puppet::Agent::Disabler end describe Puppet::Agent::Disabler do before do @disabler = DisablerTester.new end ## These tests are currently very implementation-specific, and they rely heavily on ## having access to the "disable_lockfile" method. However, I've made this method private ## because it really shouldn't be exposed outside of our implementation... therefore ## these tests have to use a lot of ".send" calls. They should probably be cleaned up ## but for the moment I wanted to make sure not to lose any of the functionality of ## the tests. --cprice 2012-04-16 it "should use an JsonLockfile instance as its disable_lockfile" do @disabler.send(:disable_lockfile).should be_instance_of(Puppet::Util::JsonLockfile) end it "should use puppet's :agent_disabled_lockfile' setting to determine its lockfile path" do Puppet.expects(:[]).with(:agent_disabled_lockfile).returns("/my/lock.disabled") lock = Puppet::Util::JsonLockfile.new("/my/lock.disabled") Puppet::Util::JsonLockfile.expects(:new).with("/my/lock.disabled").returns lock @disabler.send(:disable_lockfile) end it "should reuse the same lock file each time" do @disabler.send(:disable_lockfile).should equal(@disabler.send(:disable_lockfile)) end it "should lock the file when disabled" do @disabler.send(:disable_lockfile).expects(:lock) @disabler.disable end it "should unlock the file when enabled" do @disabler.send(:disable_lockfile).expects(:unlock) @disabler.enable end it "should check the lock if it is disabled" do @disabler.send(:disable_lockfile).expects(:locked?) @disabler.disabled? end it "should report the disable message when disabled" do lockfile = PuppetSpec::Files.tmpfile("lock") lock = Puppet::Util::JsonLockfile.new(lockfile) Puppet.expects(:[]).with(:agent_disabled_lockfile).returns("/my/lock.disabled") Puppet::Util::JsonLockfile.expects(:new).with("/my/lock.disabled").returns lock msg = "I'm busy, go away" @disabler.disable(msg) @disabler.disable_message.should == msg end end diff --git a/spec/unit/agent/locker_spec.rb b/spec/unit/agent/locker_spec.rb index 9d3188c14..7a6f56e80 100755 --- a/spec/unit/agent/locker_spec.rb +++ b/spec/unit/agent/locker_spec.rb @@ -1,93 +1,93 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/agent' require 'puppet/agent/locker' class LockerTester include Puppet::Agent::Locker end describe Puppet::Agent::Locker do before do @locker = LockerTester.new end ## These tests are currently very implementation-specific, and they rely heavily on ## having access to the lockfile object. However, I've made this method private ## because it really shouldn't be exposed outside of our implementation... therefore ## these tests have to use a lot of ".send" calls. They should probably be cleaned up ## but for the moment I wanted to make sure not to lose any of the functionality of ## the tests. --cprice 2012-04-16 it "should use a Pidlock instance as its lockfile" do @locker.send(:lockfile).should be_instance_of(Puppet::Util::Pidlock) end it "should use puppet's :agent_pidfile' setting to determine its lockfile path" do Puppet.expects(:[]).with(:agent_pidfile).returns("/my/lock") lock = Puppet::Util::Pidlock.new("/my/lock") Puppet::Util::Pidlock.expects(:new).with("/my/lock").returns lock @locker.send(:lockfile) end it "should reuse the same lock file each time" do @locker.send(:lockfile).should equal(@locker.send(:lockfile)) end it "should have a method that yields when a lock is attained" do @locker.send(:lockfile).expects(:lock).returns true yielded = false @locker.lock do yielded = true end yielded.should be_true end it "should return the block result when the lock method successfully locked" do @locker.send(:lockfile).expects(:lock).returns true @locker.lock { :result }.should == :result end it "should return nil when the lock method does not receive the lock" do @locker.send(:lockfile).expects(:lock).returns false @locker.lock {}.should be_nil end it "should not yield when the lock method does not receive the lock" do @locker.send(:lockfile).expects(:lock).returns false yielded = false @locker.lock { yielded = true } yielded.should be_false end it "should not unlock when a lock was not received" do @locker.send(:lockfile).expects(:lock).returns false @locker.send(:lockfile).expects(:unlock).never @locker.lock {} end it "should unlock after yielding upon obtaining a lock" do @locker.send(:lockfile).stubs(:lock).returns true @locker.send(:lockfile).expects(:unlock) @locker.lock {} end it "should unlock after yielding upon obtaining a lock, even if the block throws an exception" do @locker.send(:lockfile).stubs(:lock).returns true @locker.send(:lockfile).expects(:unlock) lambda { @locker.lock { raise "foo" } }.should raise_error(RuntimeError) end it "should be considered running if the lockfile is locked" do @locker.send(:lockfile).expects(:locked?).returns true @locker.should be_running end end diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb index 0b3e76d74..eac738f3c 100755 --- a/spec/unit/agent_spec.rb +++ b/spec/unit/agent_spec.rb @@ -1,326 +1,326 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/agent' class AgentTestClient def run # no-op end def stop # no-op end end def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Agent do before do @agent = Puppet::Agent.new(AgentTestClient) # So we don't actually try to hit the filesystem. @agent.stubs(:lock).yields # make Puppet::Application safe for stubbing; restore in an :after block; silence warnings for this. without_warnings { Puppet::Application = Class.new(Puppet::Application) } Puppet::Application.stubs(:clear?).returns(true) Puppet::Application.class_eval do class << self def controlled_run(&block) block.call end end end end after do # restore Puppet::Application from stub-safe subclass, and silence warnings without_warnings { Puppet::Application = Puppet::Application.superclass } end it "should set its client class at initialization" do Puppet::Agent.new("foo").client_class.should == "foo" end it "should include the Locker module" do Puppet::Agent.ancestors.should be_include(Puppet::Agent::Locker) end it "should create an instance of its client class and run it when asked to run" do client = mock 'client' AgentTestClient.expects(:new).returns client client.expects(:run) @agent.stubs(:running?).returns false @agent.stubs(:disabled?).returns false @agent.run end it "should be considered running if the lock file is locked" do lockfile = mock 'lockfile' @agent.expects(:lockfile).returns(lockfile) lockfile.expects(:locked?).returns true @agent.should be_running end describe "when being run" do before do AgentTestClient.stubs(:lockfile_path).returns "/my/lock" @agent.stubs(:running?).returns false @agent.stubs(:disabled?).returns false end it "should splay" do @agent.expects(:splay) @agent.run end it "should do nothing if already running" do @agent.expects(:running?).returns true AgentTestClient.expects(:new).never @agent.run end it "should do nothing if disabled" do @agent.expects(:disabled?).returns(true) AgentTestClient.expects(:new).never @agent.run end it "(#11057) should notify the user about why a run is skipped" do Puppet::Application.stubs(:controlled_run).returns(false) Puppet::Application.stubs(:run_status).returns('MOCK_RUN_STATUS') # This is the actual test that we inform the user why the run is skipped. # We assume this information is contained in # Puppet::Application.run_status Puppet.expects(:notice).with(regexp_matches(/MOCK_RUN_STATUS/)) @agent.run end it "should display an informative message if the agent is administratively disabled" do @agent.expects(:disabled?).returns true @agent.expects(:disable_message).returns "foo" Puppet.expects(:notice).with(regexp_matches(/Skipping run of .*; administratively disabled.*\(Reason: 'foo'\)/)) @agent.run end it "should use Puppet::Application.controlled_run to manage process state behavior" do calls = sequence('calls') Puppet::Application.expects(:controlled_run).yields.in_sequence(calls) AgentTestClient.expects(:new).once.in_sequence(calls) @agent.run end it "should not fail if a client class instance cannot be created" do AgentTestClient.expects(:new).raises "eh" Puppet.expects(:err) @agent.run end it "should not fail if there is an exception while running its client" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).raises "eh" Puppet.expects(:err) @agent.run end it "should use a mutex to restrict multi-threading" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client mutex = mock 'mutex' @agent.expects(:sync).returns mutex mutex.expects(:synchronize) client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it @agent.run end it "should use a filesystem lock to restrict multiple processes running the agent" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client @agent.expects(:lock) client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it @agent.run end it "should make its client instance available while running" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).with { @agent.client.should equal(client); true } @agent.run end it "should run the client instance with any arguments passed to it" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).with("testargs") @agent.run("testargs") end it "should return the agent result" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client @agent.expects(:lock).returns(:result) @agent.run.should == :result end describe "when should_fork is true" do before do @agent.should_fork = true Kernel.stubs(:fork) Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => 0)] @agent.stubs(:exit) end it "should run the agent in a forked process" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run) Kernel.expects(:fork).yields @agent.run end it "should exit child process if child exit" do client = AgentTestClient.new AgentTestClient.expects(:new).returns client client.expects(:run).raises(SystemExit) Kernel.expects(:fork).yields @agent.expects(:exit).with(-1) @agent.run end it "should re-raise exit happening in the child" do Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => -1)] lambda { @agent.run }.should raise_error(SystemExit) end it "should re-raise NoMoreMemory happening in the child" do Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => -2)] lambda { @agent.run }.should raise_error(NoMemoryError) end it "should return the child exit code" do Process.stubs(:waitpid2).returns [123, (stub 'process::status', :exitstatus => 777)] @agent.run.should == 777 end it "should return the block exit code as the child exit code" do Kernel.expects(:fork).yields @agent.expects(:exit).with(777) @agent.run_in_fork { 777 } end end end describe "when splaying" do before do Puppet.settings.stubs(:value).with(:splay).returns true Puppet.settings.stubs(:value).with(:splaylimit).returns "10" end it "should do nothing if splay is disabled" do Puppet.settings.expects(:value).returns false @agent.expects(:sleep).never @agent.splay end it "should do nothing if it has already splayed" do @agent.expects(:splayed?).returns true @agent.expects(:sleep).never @agent.splay end it "should log that it is splaying" do @agent.stubs :sleep Puppet.expects :info @agent.splay end it "should sleep for a random portion of the splaylimit plus 1" do Puppet.settings.expects(:value).with(:splaylimit).returns "50" @agent.expects(:rand).with(51).returns 10 @agent.expects(:sleep).with(10) @agent.splay end it "should mark that it has splayed" do @agent.stubs(:sleep) @agent.splay @agent.should be_splayed end end describe "when checking execution state" do describe 'with regular run status' do before :each do Puppet::Application.stubs(:restart_requested?).returns(false) Puppet::Application.stubs(:stop_requested?).returns(false) Puppet::Application.stubs(:interrupted?).returns(false) Puppet::Application.stubs(:clear?).returns(true) end it 'should be false for :stopping?' do @agent.stopping?.should be_false end it 'should be false for :needing_restart?' do @agent.needing_restart?.should be_false end end describe 'with a stop requested' do before :each do Puppet::Application.stubs(:clear?).returns(false) Puppet::Application.stubs(:restart_requested?).returns(false) Puppet::Application.stubs(:stop_requested?).returns(true) Puppet::Application.stubs(:interrupted?).returns(true) end it 'should be true for :stopping?' do @agent.stopping?.should be_true end it 'should be false for :needing_restart?' do @agent.needing_restart?.should be_false end end describe 'with a restart requested' do before :each do Puppet::Application.stubs(:clear?).returns(false) Puppet::Application.stubs(:restart_requested?).returns(true) Puppet::Application.stubs(:stop_requested?).returns(false) Puppet::Application.stubs(:interrupted?).returns(true) end it 'should be false for :stopping?' do @agent.stopping?.should be_false end it 'should be true for :needing_restart?' do @agent.needing_restart?.should be_true end end end end diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb index 746cf3331..0afab99f5 100755 --- a/spec/unit/application/agent_spec.rb +++ b/spec/unit/application/agent_spec.rb @@ -1,626 +1,626 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/agent' require 'puppet/application/agent' require 'puppet/network/server' require 'puppet/daemon' describe Puppet::Application::Agent do include PuppetSpec::Files before :each do @puppetd = Puppet::Application[:agent] @puppetd.stubs(:puts) @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet[:daemonize] = false @agent = stub_everything 'agent' Puppet::Agent.stubs(:new).returns(@agent) @puppetd.preinit Puppet::Util::Log.stubs(:newdestination) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) end it "should operate in agent run_mode" do @puppetd.class.run_mode.name.should == :agent end it "should declare a main command" do @puppetd.should respond_to(:main) end it "should declare a onetime command" do @puppetd.should respond_to(:onetime) end it "should declare a fingerprint command" do @puppetd.should respond_to(:fingerprint) end it "should declare a preinit block" do @puppetd.should respond_to(:preinit) end describe "in preinit" do it "should catch INT" do Signal.expects(:trap).with { |arg,block| arg == :INT } @puppetd.preinit end it "should init client to true" do @puppetd.preinit @puppetd.options[:client].should be_true end it "should init fqdn to nil" do @puppetd.preinit @puppetd.options[:fqdn].should be_nil end it "should init serve to []" do @puppetd.preinit @puppetd.options[:serve].should == [] end it "should use MD5 as default digest algorithm" do @puppetd.preinit @puppetd.options[:digest].should == :MD5 end it "should not fingerprint by default" do @puppetd.preinit @puppetd.options[:fingerprint].should be_false end it "should init waitforcert to nil" do @puppetd.preinit @puppetd.options[:waitforcert].should be_nil end end describe "when handling options" do before do @puppetd.command_line.stubs(:args).returns([]) end [:centrallogging, :enable, :debug, :fqdn, :test, :verbose, :digest].each do |option| it "should declare handle_#{option} method" do @puppetd.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @puppetd.options.expects(:[]=).with(option, 'arg') @puppetd.send("handle_#{option}".to_sym, 'arg') end end describe "when handling --disable" do it "should declare handle_disable method" do @puppetd.should respond_to(:handle_disable) end it "should set disable to true" do @puppetd.options.stubs(:[]=) @puppetd.options.expects(:[]=).with(:disable, true) @puppetd.handle_disable('') end it "should store disable message" do @puppetd.options.stubs(:[]=) @puppetd.options.expects(:[]=).with(:disable_message, "message") @puppetd.handle_disable('message') end end it "should set client to false with --no-client" do @puppetd.handle_no_client(nil) @puppetd.options[:client].should be_false end it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do Puppet[:onetime] = true Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(0) @puppetd.setup_host end it "should use supplied waitforcert when --onetime is specified" do Puppet[:onetime] = true @puppetd.handle_waitforcert(60) Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(60) @puppetd.setup_host end it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(120) @puppetd.setup_host end it "should use the waitforcert setting when checking for a signed certificate" do Puppet[:waitforcert] = 10 Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(10) @puppetd.setup_host end it "should set the log destination with --logdest" do @puppetd.options.stubs(:[]=).with { |opt,val| opt == :setdest } Puppet::Log.expects(:newdestination).with("console") @puppetd.handle_logdest("console") end it "should put the setdest options to true" do @puppetd.options.expects(:[]=).with(:setdest,true) @puppetd.handle_logdest("console") end it "should parse the log destination from the command line" do @puppetd.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @puppetd.parse_options end it "should store the waitforcert options with --waitforcert" do @puppetd.options.expects(:[]=).with(:waitforcert,42) @puppetd.handle_waitforcert("42") end it "should set args[:Port] with --port" do @puppetd.handle_port("42") @puppetd.args[:Port].should == "42" end end describe "during setup" do before :each do @puppetd.options.stubs(:[]) Puppet.stubs(:info) FileTest.stubs(:exists?).returns(true) Puppet[:libdir] = "/dev/null/lib" Puppet::SSL::Host.stubs(:ca_location=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Transaction::Report.indirection.stubs(:cache_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) @host = stub_everything 'host' Puppet::SSL::Host.stubs(:new).returns(@host) Puppet.stubs(:settraps) end describe "with --test" do before :each do #Puppet.settings.stubs(:handlearg) @puppetd.options.stubs(:[]=) end it "should call setup_test" do @puppetd.options.stubs(:[]).with(:test).returns(true) @puppetd.expects(:setup_test) @puppetd.setup end it "should set options[:verbose] to true" do @puppetd.options.expects(:[]=).with(:verbose,true) @puppetd.setup_test end it "should set options[:onetime] to true" do Puppet[:onetime] = false @puppetd.setup_test Puppet[:onetime].should == true end it "should set options[:detailed_exitcodes] to true" do @puppetd.options.expects(:[]=).with(:detailed_exitcodes,true) @puppetd.setup_test end end it "should call setup_logs" do @puppetd.expects(:setup_logs) @puppetd.setup end describe "when setting up logs" do before :each do Puppet::Util::Log.stubs(:newdestination) end it "should set log level to debug if --debug was passed" do @puppetd.options.stubs(:[]).with(:debug).returns(true) @puppetd.setup_logs Puppet::Util::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @puppetd.options.stubs(:[]).with(:verbose).returns(true) @puppetd.setup_logs Puppet::Util::Log.level.should == :info end [:verbose, :debug].each do |level| it "should set console as the log destination with level #{level}" do @puppetd.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @puppetd.setup_logs end end it "should set a default log destination if no --logdest" do @puppetd.options.stubs(:[]).with(:setdest).returns(false) Puppet::Util::Log.expects(:setup_default) @puppetd.setup_logs end end it "should print puppet config if asked to in Puppet config" do Puppet[:configprint] = "pluginsync" Puppet.settings.expects(:print_configs).returns true expect { @puppetd.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do path = make_absolute('/my/path') Puppet[:modulepath] = path Puppet[:configprint] = "modulepath" Puppet::Settings.any_instance.expects(:puts).with(path) expect { @puppetd.setup }.to exit_with 0 end it "should set a central log destination with --centrallogs" do @puppetd.options.stubs(:[]).with(:centrallogs).returns(true) Puppet[:server] = "puppet.reductivelabs.com" Puppet::Util::Log.stubs(:setup_default) Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") @puppetd.setup end it "should use :main, :puppetd, and :ssl" do Puppet.settings.expects(:use).with(:main, :agent, :ssl) @puppetd.setup end it "should install a remote ca location" do Puppet::SSL::Host.expects(:ca_location=).with(:remote) @puppetd.setup end it "should install a none ca location in fingerprint mode" do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:none) @puppetd.setup end it "should tell the report handler to use REST" do Puppet::Transaction::Report.indirection.expects(:terminus_class=).with(:rest) @puppetd.setup end it "should tell the report handler to cache locally as yaml" do Puppet::Transaction::Report.indirection.expects(:cache_class=).with(:yaml) @puppetd.setup end it "should default catalog_terminus setting to 'rest'" do @puppetd.initialize_app_defaults Puppet[:catalog_terminus].should == :rest end it "should default node_terminus setting to 'rest'" do @puppetd.initialize_app_defaults Puppet[:node_terminus].should == :rest end it "should tell the catalog handler to use cache" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:yaml) @puppetd.setup end it "should default facts_terminus setting to 'facter'" do @puppetd.initialize_app_defaults Puppet[:facts_terminus].should == :facter end it "should create an agent" do Puppet::Agent.stubs(:new).with(Puppet::Configurer) @puppetd.setup end [:enable, :disable].each do |action| it "should delegate to enable_disable_client if we #{action} the agent" do @puppetd.options.stubs(:[]).with(action).returns(true) @puppetd.expects(:enable_disable_client).with(@agent) @puppetd.setup end end describe "when enabling or disabling agent" do [:enable, :disable].each do |action| it "should call client.#{action}" do @puppetd.options.stubs(:[]).with(action).returns(true) @agent.expects(action) expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 end end it "should pass the disable message when disabling" do @puppetd.options.stubs(:[]).with(:disable).returns(true) @puppetd.options.stubs(:[]).with(:disable_message).returns("message") @agent.expects(:disable).with("message") expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 end it "should pass the default disable message when disabling without a message" do @puppetd.options.stubs(:[]).with(:disable).returns(true) @puppetd.options.stubs(:[]).with(:disable_message).returns(nil) @agent.expects(:disable).with("reason not specified") expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 end it "should finally exit" do expect { @puppetd.enable_disable_client(@agent) }.to exit_with 0 end end it "should inform the daemon about our agent if :client is set to 'true'" do @puppetd.options.expects(:[]).with(:client).returns true @daemon.expects(:agent=).with(@agent) @puppetd.setup end it "should not inform the daemon about our agent if :client is set to 'false'" do @puppetd.options[:client] = false @daemon.expects(:agent=).never @puppetd.setup end it "should daemonize if needed" do Puppet.features.stubs(:microsoft_windows?).returns false Puppet[:daemonize] = true @daemon.expects(:daemonize) @puppetd.setup end it "should wait for a certificate" do @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) @host.expects(:wait_for_cert).with(123) @puppetd.setup end it "should not wait for a certificate in fingerprint mode" do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) @puppetd.options.stubs(:[]).with(:waitforcert).returns(123) @host.expects(:wait_for_cert).never @puppetd.setup end it "should setup listen if told to and not onetime" do Puppet[:listen] = true @puppetd.options.stubs(:[]).with(:onetime).returns(false) @puppetd.expects(:setup_listen) @puppetd.setup end describe "when setting up listen" do before :each do Puppet[:authconfig] = 'auth' FileTest.stubs(:exists?).with('auth').returns(true) File.stubs(:exist?).returns(true) @puppetd.options.stubs(:[]).with(:serve).returns([]) @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) end it "should exit if no authorization file" do Puppet.stubs(:err) FileTest.stubs(:exists?).with(Puppet[:rest_authconfig]).returns(false) expect { @puppetd.setup_listen }.to exit_with 14 end it "should use puppet default port" do Puppet[:puppetport] = 32768 Puppet::Network::Server.expects(:new).with { |args| args[:port] == 32768 } @puppetd.setup_listen end end describe "when setting up for fingerprint" do before(:each) do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) end it "should not setup as an agent" do @puppetd.expects(:setup_agent).never @puppetd.setup end it "should not create an agent" do Puppet::Agent.stubs(:new).with(Puppet::Configurer).never @puppetd.setup end it "should not daemonize" do @daemon.expects(:daemonize).never @puppetd.setup end it "should setup our certificate host" do @puppetd.expects(:setup_host) @puppetd.setup end end end describe "when running" do before :each do @puppetd.agent = @agent @puppetd.daemon = @daemon @puppetd.options.stubs(:[]).with(:fingerprint).returns(false) end it "should dispatch to fingerprint if --fingerprint is used" do @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) @puppetd.stubs(:fingerprint) @puppetd.run_command end it "should dispatch to onetime if --onetime is used" do @puppetd.options.stubs(:[]).with(:onetime).returns(true) @puppetd.stubs(:onetime) @puppetd.run_command end it "should dispatch to main if --onetime and --fingerprint are not used" do @puppetd.options.stubs(:[]).with(:onetime).returns(false) @puppetd.stubs(:main) @puppetd.run_command end describe "with --onetime" do before :each do @agent.stubs(:run).returns(:report) @puppetd.options.stubs(:[]).with(:client).returns(:client) @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(false) Puppet.stubs(:newservice) end it "should exit if no defined --client" do $stderr.stubs(:puts) @puppetd.options.stubs(:[]).with(:client).returns(nil) expect { @puppetd.onetime }.to exit_with 43 end it "should setup traps" do @daemon.expects(:set_signal_traps) expect { @puppetd.onetime }.to exit_with 0 end it "should not let the agent fork" do @agent.expects(:should_fork=).with(false) expect { @puppetd.onetime }.to exit_with 0 end it "should let the agent run" do @agent.expects(:run).returns(:report) expect { @puppetd.onetime }.to exit_with 0 end it "should finish by exiting with 0 error code" do expect { @puppetd.onetime }.to exit_with 0 end it "should stop the daemon" do @daemon.expects(:stop).with(:exit => false) expect { @puppetd.onetime }.to exit_with 0 end describe "and --detailed-exitcodes" do before :each do @puppetd.options.stubs(:[]).with(:detailed_exitcodes).returns(true) end it "should exit with agent computed exit status" do Puppet[:noop] = false @agent.stubs(:run).returns(666) expect { @puppetd.onetime }.to exit_with 666 end it "should exit with the agent's exit status, even if --noop is set." do Puppet[:noop] = true @agent.stubs(:run).returns(666) expect { @puppetd.onetime }.to exit_with 666 end end end describe "with --fingerprint" do before :each do @cert = stub_everything 'cert' @puppetd.options.stubs(:[]).with(:fingerprint).returns(true) @puppetd.options.stubs(:[]).with(:digest).returns(:MD5) @host = stub_everything 'host' @puppetd.stubs(:host).returns(@host) end it "should fingerprint the certificate if it exists" do @host.expects(:certificate).returns(@cert) @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" @puppetd.fingerprint end it "should fingerprint the certificate request if no certificate have been signed" do @host.expects(:certificate).returns(nil) @host.expects(:certificate_request).returns(@cert) @cert.expects(:fingerprint).with(:MD5).returns "fingerprint" @puppetd.fingerprint end it "should display the fingerprint" do @host.stubs(:certificate).returns(@cert) @cert.stubs(:fingerprint).with(:MD5).returns("DIGEST") @puppetd.expects(:puts).with "DIGEST" @puppetd.fingerprint end end describe "without --onetime and --fingerprint" do before :each do Puppet.stubs(:notice) @puppetd.options.stubs(:[]).with(:client) end it "should start our daemon" do @daemon.expects(:start) @puppetd.main end end end end diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb index 54416dfc4..68c90add4 100755 --- a/spec/unit/application/apply_spec.rb +++ b/spec/unit/application/apply_spec.rb @@ -1,419 +1,419 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/apply' require 'puppet/file_bucket/dipper' require 'puppet/configurer' require 'fileutils' describe Puppet::Application::Apply do before :each do @apply = Puppet::Application[:apply] Puppet::Util::Log.stubs(:newdestination) end after :each do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Node::Facts.indirection.cache_class = nil Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil end [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes,:catalog].each do |option| it "should declare handle_#{option} method" do @apply.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @apply.options.expects(:[]=).with(option, 'arg') @apply.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @apply.options.expects(:[]=).with(:code, 'arg') @apply.send("handle_execute".to_sym, 'arg') end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @apply.handle_logdest("console") end it "should put the logset options to true" do @apply.options.expects(:[]=).with(:logset,true) @apply.handle_logdest("console") end it "should deprecate --apply" do Puppet.expects(:deprecation_warning).with do |arg| arg.match(/--apply is deprecated/) end command_line = Puppet::Util::CommandLine.new('puppet', ['apply', '--apply', 'catalog.json']) apply = Puppet::Application::Apply.new(command_line) apply.stubs(:run_command) apply.run end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::FileBucket::Dipper.stubs(:new) STDIN.stubs(:read) Puppet::Transaction::Report.indirection.stubs(:cache_class=) @apply.options.stubs(:[]).with(any_parameters) end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @apply.setup end it "should set INT trap" do Signal.expects(:trap).with(:INT) @apply.setup end it "should set log level to debug if --debug was passed" do @apply.options.stubs(:[]).with(:debug).returns(true) @apply.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @apply.options.stubs(:[]).with(:verbose).returns(true) @apply.setup Puppet::Log.level.should == :info end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns true Puppet.settings.expects(:print_configs).returns true expect { @apply.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @apply.setup }.to exit_with 1 end it "should tell the report handler to cache locally as yaml" do Puppet::Transaction::Report.indirection.expects(:cache_class=).with(:yaml) @apply.setup end it "should set pluginsource with no hostname" do @apply.app_defaults[:pluginsource].should == 'puppet:///plugins' end it "should set default_file_terminus to `file_server` to be local" do @apply.app_defaults[:default_file_terminus].should == 'file_server' end end describe "when executing" do it "should dispatch to 'apply' if it was called with 'apply'" do @apply.options[:catalog] = "foo" @apply.expects(:apply) @apply.run_command end it "should dispatch to main otherwise" do @apply.stubs(:options).returns({}) @apply.expects(:main) @apply.run_command end describe "the main command" do include PuppetSpec::Files before :each do Puppet[:prerun_command] = '' Puppet[:postrun_command] = '' Puppet::Node::Facts.indirection.terminus_class = :memory Puppet::Node::Facts.indirection.cache_class = :memory Puppet::Node.indirection.terminus_class = :memory Puppet::Node.indirection.cache_class = :memory @facts = Puppet::Node::Facts.new(Puppet[:node_name_value]) Puppet::Node::Facts.indirection.save(@facts) @node = Puppet::Node.new(Puppet[:node_name_value]) Puppet::Node.indirection.save(@node) @catalog = Puppet::Resource::Catalog.new @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.indirection.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = Puppet::Transaction.new(@catalog) @catalog.stubs(:apply).returns(@transaction) Puppet::Util::Storage.stubs(:load) Puppet::Configurer.any_instance.stubs(:save_last_run_summary) # to prevent it from trying to write files end after :each do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Node::Facts.indirection.cache_class = nil end it "should set the code to run from --code" do @apply.options[:code] = "code to run" Puppet.expects(:[]=).with(:code,"code to run") expect { @apply.main }.to exit_with 0 end it "should set the code to run from STDIN if no arguments" do @apply.command_line.stubs(:args).returns([]) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") expect { @apply.main }.to exit_with 0 end it "should set the manifest if a file is passed on command line and the file exists" do manifest = tmpfile('site.pp') FileUtils.touch(manifest) @apply.command_line.stubs(:args).returns([manifest]) Puppet.expects(:[]=).with(:manifest,manifest) expect { @apply.main }.to exit_with 0 end it "should raise an error if a file is passed on command line and the file does not exist" do noexist = tmpfile('noexist.pp') @apply.command_line.stubs(:args).returns([noexist]) lambda { @apply.main }.should raise_error(RuntimeError, "Could not find file #{noexist}") end it "should set the manifest to the first file and warn other files will be skipped" do manifest = tmpfile('starwarsIV') FileUtils.touch(manifest) @apply.command_line.stubs(:args).returns([manifest, 'starwarsI', 'starwarsII']) Puppet.expects(:[]=).with(:manifest,manifest) expect { @apply.main }.to exit_with 0 msg = @logs.find {|m| m.message =~ /Only one file can be applied per run/ } msg.message.should == 'Only one file can be applied per run. Skipping starwarsI, starwarsII' msg.level.should == :warning end it "should raise an error if we can't find the node" do Puppet::Node.indirection.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end it "should load custom classes if loadclasses" do @apply.options[:loadclasses] = true classfile = tmpfile('classfile') File.open(classfile, 'w') { |c| c.puts 'class' } Puppet[:classfile] = classfile @node.expects(:classes=).with(['class']) expect { @apply.main }.to exit_with 0 end it "should compile the catalog" do Puppet::Resource::Catalog.indirection.expects(:find).returns(@catalog) expect { @apply.main }.to exit_with 0 end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) expect { @apply.main }.to exit_with 0 end it "should finalize the catalog" do @catalog.expects(:finalize) expect { @apply.main }.to exit_with 0 end it "should call the prerun and postrun commands on a Configurer instance" do Puppet::Configurer.any_instance.expects(:execute_prerun_command).returns(true) Puppet::Configurer.any_instance.expects(:execute_postrun_command).returns(true) expect { @apply.main }.to exit_with 0 end it "should apply the catalog" do @catalog.expects(:apply).returns(stub_everything('transaction')) expect { @apply.main }.to exit_with 0 end it "should save the last run summary" do Puppet[:noop] = false report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.stubs(:new).returns(report) Puppet::Configurer.any_instance.expects(:save_last_run_summary).with(report) expect { @apply.main }.to exit_with 0 end describe "when using node_name_fact" do before :each do @facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name') Puppet::Node::Facts.indirection.save(@facts) @node = Puppet::Node.new('other_node_name') Puppet::Node.indirection.save(@node) Puppet[:node_name_fact] = 'my_name_fact' end it "should set the facts name based on the node_name_fact" do expect { @apply.main }.to exit_with 0 @facts.name.should == 'other_node_name' end it "should set the node_name_value based on the node_name_fact" do expect { @apply.main }.to exit_with 0 Puppet[:node_name_value].should == 'other_node_name' end it "should merge in our node the loaded facts" do @facts.values.merge!('key' => 'value') expect { @apply.main }.to exit_with 0 @node.parameters['key'].should == 'value' end it "should raise an error if we can't find the facts" do Puppet::Node::Facts.indirection.expects(:find).returns(nil) lambda { @apply.main }.should raise_error end end describe "with detailed_exitcodes" do before :each do @apply.options[:detailed_exitcodes] = true end it "should exit with report's computed exit status" do Puppet[:noop] = false Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) expect { @apply.main }.to exit_with 666 end it "should exit with report's computed exit status, even if --noop is set" do Puppet[:noop] = true Puppet::Transaction::Report.any_instance.stubs(:exit_status).returns(666) expect { @apply.main }.to exit_with 666 end it "should always exit with 0 if option is disabled" do Puppet[:noop] = false report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) expect { @apply.main }.to exit_with 0 end it "should always exit with 0 if --noop" do Puppet[:noop] = true report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) expect { @apply.main }.to exit_with 0 end end end describe "the 'apply' command" do # We want this memoized, and to be able to adjust the content, so we # have to do it ourselves. def temporary_catalog(content = '"something"') @tempfile = Tempfile.new('catalog.pson') @tempfile.write(content) @tempfile.close @tempfile.path end it "should read the catalog in from disk if a file name is provided" do @apply.options[:catalog] = temporary_catalog Puppet::Resource::Catalog.stubs(:convert_from). with(:pson,'"something"').returns(Puppet::Resource::Catalog.new) @apply.apply end it "should read the catalog in from stdin if '-' is provided" do @apply.options[:catalog] = "-" $stdin.expects(:read).returns '"something"' Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'"something"').returns Puppet::Resource::Catalog.new @apply.apply end it "should deserialize the catalog from the default format" do @apply.options[:catalog] = temporary_catalog Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'"something"').returns Puppet::Resource::Catalog.new @apply.apply end it "should fail helpfully if deserializing fails" do @apply.options[:catalog] = temporary_catalog('something syntactically invalid') lambda { @apply.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @apply.options[:catalog] = temporary_catalog Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'"something"').returns({:foo => "bar"}) Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) @apply.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @apply.options[:catalog] = temporary_catalog catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'"something"').returns catalog catalog.expects(:to_ral).returns "mycatalog" configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:run). with(:catalog => "mycatalog", :skip_plugin_download => true) @apply.apply end end end describe "apply_catalog" do it "should call the configurer with the catalog" do catalog = "I am a catalog" Puppet::Configurer.any_instance.expects(:run). with(:catalog => catalog, :skip_plugin_download => true) @apply.send(:apply_catalog, catalog) end end end diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb index f86803890..0ace32fbf 100755 --- a/spec/unit/application/cert_spec.rb +++ b/spec/unit/application/cert_spec.rb @@ -1,218 +1,218 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/cert' describe Puppet::Application::Cert => true do before :each do @cert_app = Puppet::Application[:cert] Puppet::Util::Log.stubs(:newdestination) end it "should operate in master run_mode" do @cert_app.class.run_mode.name.should equal(:master) end it "should declare a main command" do @cert_app.should respond_to(:main) end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject{ |m| m == :destroy }.each do |method| it "should declare option --#{method}" do @cert_app.should respond_to("handle_#{method}".to_sym) end end it "should set log level to info with the --verbose option" do @cert_app.handle_verbose(0) Puppet::Log.level.should == :info end it "should set log level to debug with the --debug option" do @cert_app.handle_debug(0) Puppet::Log.level.should == :debug end it "should set the fingerprint digest with the --digest option" do @cert_app.handle_digest(:digest) @cert_app.digest.should == :digest end it "should set cert_mode to :destroy for --clean" do @cert_app.handle_clean(0) @cert_app.subcommand.should == :destroy end it "should set all to true for --all" do @cert_app.handle_all(0) @cert_app.all.should be_true end it "should set signed to true for --signed" do @cert_app.handle_signed(0) @cert_app.signed.should be_true end Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject { |m| m == :destroy }.each do |method| it "should set cert_mode to #{method} with option --#{method}" do @cert_app.send("handle_#{method}".to_sym, nil) @cert_app.subcommand.should == method end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet::SSL::Host.stubs(:ca_location=) Puppet::SSL::CertificateAuthority.stubs(:new) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @cert_app.setup end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs).returns true expect { @cert_app.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @cert_app.setup }.to exit_with 1 end it "should set the CA location to 'only'" do Puppet::SSL::Host.expects(:ca_location=).with(:only) @cert_app.setup end it "should create a new certificate authority" do Puppet::SSL::CertificateAuthority.expects(:new) @cert_app.setup end it "should set the ca_location to :local if the cert_mode is generate" do @cert_app.subcommand = 'generate' Puppet::SSL::Host.expects(:ca_location=).with(:local) @cert_app.setup end it "should set the ca_location to :local if the cert_mode is destroy" do @cert_app.subcommand = 'destroy' Puppet::SSL::Host.expects(:ca_location=).with(:local) @cert_app.setup end it "should set the ca_location to :only if the cert_mode is print" do @cert_app.subcommand = 'print' Puppet::SSL::Host.expects(:ca_location=).with(:only) @cert_app.setup end end describe "when running" do before :each do @cert_app.all = false @ca = stub_everything 'ca' @cert_app.ca = @ca @cert_app.command_line.stubs(:args).returns([]) end it "should delegate to the CertificateAuthority" do @ca.expects(:apply) @cert_app.main end it "should delegate with :all if option --all was given" do @cert_app.handle_all(0) @ca.expects(:apply).with { |cert_mode,to| to[:to] == :all } @cert_app.main end it "should delegate to ca.apply with the hosts given on command line" do @cert_app.command_line.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |cert_mode,to| to[:to] == ["host"]} @cert_app.main end it "should send the currently set digest" do @cert_app.command_line.stubs(:args).returns(["host"]) @cert_app.handle_digest(:digest) @ca.expects(:apply).with { |cert_mode,to| to[:digest] == :digest} @cert_app.main end it "should revoke cert if cert_mode is clean" do @cert_app.subcommand = :destroy @cert_app.command_line.stubs(:args).returns(["host"]) @ca.expects(:apply).with { |cert_mode,to| cert_mode == :revoke } @ca.expects(:apply).with { |cert_mode,to| cert_mode == :destroy } @cert_app.main end end describe "when identifying subcommands" do before :each do @cert_app.all = false @ca = stub_everything 'ca' @cert_app.ca = @ca end %w{list revoke generate sign print verify fingerprint}.each do |cmd| short = cmd[0,1] [cmd, "--#{cmd}", "-#{short}"].each do |option| # In our command line '-v' was eaten by 'verbose', so we can't consume # it here; this is a special case from our otherwise standard # processing. --daniel 2011-02-22 next if option == "-v" it "should recognise '#{option}'" do args = [option, "fun.example.com"] @cert_app.command_line.stubs(:args).returns(args) @cert_app.parse_options @cert_app.subcommand.should == cmd.to_sym args.should == ["fun.example.com"] end end end %w{clean --clean -c}.each do |ugly| it "should recognise the '#{ugly}' option as destroy" do args = [ugly, "fun.example.com"] @cert_app.command_line.stubs(:args).returns(args) @cert_app.parse_options @cert_app.subcommand.should == :destroy args.should == ["fun.example.com"] end end it "should print help and exit if there is no subcommand" do args = [] @cert_app.command_line.stubs(:args).returns(args) @cert_app.stubs(:help).returns("I called for help!") @cert_app.expects(:puts).with("I called for help!") expect { @cert_app.parse_options }.to exit_with 0 @cert_app.subcommand.should be_nil end end end diff --git a/spec/unit/application/certificate_spec.rb b/spec/unit/application/certificate_spec.rb index c85d1e872..45c065b51 100755 --- a/spec/unit/application/certificate_spec.rb +++ b/spec/unit/application/certificate_spec.rb @@ -1,22 +1,22 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/certificate' describe Puppet::Application::Certificate do it "should have a 'ca-location' option" do # REVISIT: This is delegated from the face, and we will have a test there, # so is this actually a valuable test? --daniel 2011-04-07 subject.command_line.stubs(:args).returns %w{list} subject.preinit subject.parse_options subject.should respond_to(:handle_ca_location) end it "should accept the ca-location option" do subject.command_line.stubs(:args).returns %w{--ca-location local list} subject.preinit subject.parse_options subject.setup subject.arguments.should == [{ :ca_location => "local" }] end end diff --git a/spec/unit/application/config_spec.rb b/spec/unit/application/config_spec.rb index dc2fb5717..3996c7aa9 100755 --- a/spec/unit/application/config_spec.rb +++ b/spec/unit/application/config_spec.rb @@ -1,9 +1,9 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/config' describe Puppet::Application::Config do it "should be a subclass of Puppet::Application::FaceBase" do Puppet::Application::Config.superclass.should equal(Puppet::Application::FaceBase) end end diff --git a/spec/unit/application/describe_spec.rb b/spec/unit/application/describe_spec.rb index ecf7883c0..4f5d6c098 100755 --- a/spec/unit/application/describe_spec.rb +++ b/spec/unit/application/describe_spec.rb @@ -1,79 +1,79 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/describe' describe Puppet::Application::Describe do before :each do @describe = Puppet::Application[:describe] end it "should declare a main command" do @describe.should respond_to(:main) end it "should declare a preinit block" do @describe.should respond_to(:preinit) end [:providers,:list,:meta].each do |option| it "should declare handle_#{option} method" do @describe.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @describe.options.expects(:[]=).with("#{option}".to_sym, 'arg') @describe.send("handle_#{option}".to_sym, 'arg') end end describe "in preinit" do it "should set options[:parameteers] to true" do @describe.preinit @describe.options[:parameters].should be_true end end describe "when handling parameters" do it "should set options[:parameters] to false" do @describe.handle_short(nil) @describe.options[:parameters].should be_false end end describe "during setup" do it "should collect arguments in options[:types]" do @describe.command_line.stubs(:args).returns(['1','2']) @describe.setup @describe.options[:types].should == ['1','2'] end end describe "when running" do before :each do @typedoc = stub 'type_doc' TypeDoc.stubs(:new).returns(@typedoc) end it "should call list_types if options list is set" do @describe.options[:list] = true @typedoc.expects(:list_types) @describe.run_command end it "should call format_type for each given types" do @describe.options[:list] = false @describe.options[:types] = ['type'] @typedoc.expects(:format_type).with('type', @describe.options) @describe.run_command end end end diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb index 0fa90d5cc..6beb51c27 100755 --- a/spec/unit/application/device_spec.rb +++ b/spec/unit/application/device_spec.rb @@ -1,403 +1,403 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/device' require 'puppet/util/network_device/config' require 'ostruct' require 'puppet/configurer' describe Puppet::Application::Device do include PuppetSpec::Files before :each do @device = Puppet::Application[:device] @device.preinit Puppet::Util::Log.stubs(:newdestination) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) end it "should operate in agent run_mode" do @device.class.run_mode.name.should == :agent end it "should declare a main command" do @device.should respond_to(:main) end it "should declare a preinit block" do @device.should respond_to(:preinit) end describe "in preinit" do before :each do @device.stubs(:trap) end it "should catch INT" do Signal.expects(:trap).with { |arg,block| arg == :INT } @device.preinit end it "should init waitforcert to nil" do @device.preinit @device.options[:waitforcert].should be_nil end end describe "when handling options" do before do @device.command_line.stubs(:args).returns([]) end [:centrallogging, :debug, :verbose,].each do |option| it "should declare handle_#{option} method" do @device.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @device.options.expects(:[]=).with(option, 'arg') @device.send("handle_#{option}".to_sym, 'arg') end end it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do Puppet[:onetime] = true Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(0) @device.setup_host end it "should use supplied waitforcert when --onetime is specified" do Puppet[:onetime] = true @device.handle_waitforcert(60) Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(60) @device.setup_host end it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(120) @device.setup_host end it "should use the waitforcert setting when checking for a signed certificate" do Puppet[:waitforcert] = 10 Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(10) @device.setup_host end it "should set the log destination with --logdest" do @device.options.stubs(:[]=).with { |opt,val| opt == :setdest } Puppet::Log.expects(:newdestination).with("console") @device.handle_logdest("console") end it "should put the setdest options to true" do @device.options.expects(:[]=).with(:setdest,true) @device.handle_logdest("console") end it "should parse the log destination from the command line" do @device.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @device.parse_options end it "should store the waitforcert options with --waitforcert" do @device.options.expects(:[]=).with(:waitforcert,42) @device.handle_waitforcert("42") end it "should set args[:Port] with --port" do @device.handle_port("42") @device.args[:Port].should == "42" end end describe "during setup" do before :each do @device.options.stubs(:[]) Puppet.stubs(:info) FileTest.stubs(:exists?).returns(true) Puppet[:libdir] = "/dev/null/lib" Puppet::SSL::Host.stubs(:ca_location=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) @host = stub_everything 'host' Puppet::SSL::Host.stubs(:new).returns(@host) Puppet.stubs(:settraps) end it "should call setup_logs" do @device.expects(:setup_logs) @device.setup end describe "when setting up logs" do before :each do Puppet::Util::Log.stubs(:newdestination) end it "should set log level to debug if --debug was passed" do @device.options.stubs(:[]).with(:debug).returns(true) @device.setup_logs Puppet::Util::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @device.options.stubs(:[]).with(:verbose).returns(true) @device.setup_logs Puppet::Util::Log.level.should == :info end [:verbose, :debug].each do |level| it "should set console as the log destination with level #{level}" do @device.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @device.setup_logs end end it "should set a default log destination if no --logdest" do @device.options.stubs(:[]).with(:setdest).returns(false) Puppet::Util::Log.expects(:setup_default) @device.setup_logs end end it "should set a central log destination with --centrallogs" do @device.options.stubs(:[]).with(:centrallogs).returns(true) Puppet[:server] = "puppet.reductivelabs.com" Puppet::Util::Log.stubs(:newdestination).with(:syslog) Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") @device.setup end it "should use :main, :agent, :device and :ssl config" do Puppet.settings.expects(:use).with(:main, :agent, :device, :ssl) @device.setup end it "should install a remote ca location" do Puppet::SSL::Host.expects(:ca_location=).with(:remote) @device.setup end it "should tell the report handler to use REST" do Puppet::Transaction::Report.indirection.expects(:terminus_class=).with(:rest) @device.setup end it "should default the catalog_terminus setting to 'rest'" do @device.initialize_app_defaults Puppet[:catalog_terminus].should == :rest end it "should default the node_terminus setting to 'rest'" do @device.initialize_app_defaults Puppet[:node_terminus].should == :rest end it "should tell the catalog handler to use cache" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:yaml) @device.setup end it "should default the facts_terminus setting to 'network_device'" do @device.initialize_app_defaults Puppet[:facts_terminus].should == :network_device end end describe "when initializing each devices SSL" do before(:each) do @host = stub_everything 'host' Puppet::SSL::Host.stubs(:new).returns(@host) end it "should create a new ssl host" do Puppet::SSL::Host.expects(:new).returns(@host) @device.setup_host end it "should wait for a certificate" do @device.options.stubs(:[]).with(:waitforcert).returns(123) @host.expects(:wait_for_cert).with(123) @device.setup_host end end describe "when running" do before :each do @device.options.stubs(:[]).with(:fingerprint).returns(false) Puppet.stubs(:notice) @device.options.stubs(:[]).with(:client) Puppet::Util::NetworkDevice::Config.stubs(:devices).returns({}) end it "should dispatch to main" do @device.stubs(:main) @device.run_command end it "should get the device list" do device_hash = stub_everything 'device hash' Puppet::Util::NetworkDevice::Config.expects(:devices).returns(device_hash) @device.main end it "should exit if the device list is empty" do expect { @device.main }.to exit_with 1 end describe "for each device" do before(:each) do Puppet[:vardir] = make_absolute("/dummy") Puppet[:confdir] = make_absolute("/dummy") Puppet[:certname] = "certname" @device_hash = { "device1" => OpenStruct.new(:name => "device1", :url => "url", :provider => "cisco"), "device2" => OpenStruct.new(:name => "device2", :url => "url", :provider => "cisco"), } Puppet::Util::NetworkDevice::Config.stubs(:devices).returns(@device_hash) Puppet.settings.stubs(:set_value) Puppet.settings.stubs(:use) @device.stubs(:setup_host) Puppet::Util::NetworkDevice.stubs(:init) @configurer = stub_everything 'configurer' Puppet::Configurer.stubs(:new).returns(@configurer) end it "should set vardir to the device vardir" do Puppet.settings.expects(:set_value).with(:vardir, make_absolute("/dummy/devices/device1"), :cli) @device.main end it "should set confdir to the device confdir" do Puppet.settings.expects(:set_value).with(:confdir, make_absolute("/dummy/devices/device1"), :cli) @device.main end it "should set certname to the device certname" do Puppet.settings.expects(:set_value).with(:certname, "device1", :cli) Puppet.settings.expects(:set_value).with(:certname, "device2", :cli) @device.main end it "should make sure all the required folders and files are created" do Puppet.settings.expects(:use).with(:main, :agent, :ssl).twice @device.main end it "should initialize the device singleton" do Puppet::Util::NetworkDevice.expects(:init).with(@device_hash["device1"]).then.with(@device_hash["device2"]) @device.main end it "should setup the SSL context" do @device.expects(:setup_host).twice @device.main end it "should launch a configurer for this device" do @configurer.expects(:run).twice @device.main end [:vardir, :confdir].each do |setting| it "should cleanup the #{setting} setting after the run" do all_devices = Set.new(@device_hash.keys.map do |device_name| make_absolute("/dummy/devices/#{device_name}") end) found_devices = Set.new() # a block to use in a few places later to validate the arguments passed to "set_value" p = Proc.new do |my_setting, my_value, my_type| success = (my_setting == setting) && (my_type == :cli) && (all_devices.include?(my_value)) found_devices.add(my_value) if success success end seq = sequence("clean up dirs") all_devices.size.times do ## one occurrence of set / run / set("/dummy") for each device Puppet.settings.expects(:set_value).with(&p).in_sequence(seq) @configurer.expects(:run).in_sequence(seq) Puppet.settings.expects(:set_value).with(setting, make_absolute("/dummy"), :cli).in_sequence(seq) end @device.main # make sure that we were called with each of the defined devices all_devices.should == found_devices end end it "should cleanup the certname setting after the run" do all_devices = Set.new(@device_hash.keys) found_devices = Set.new() # a block to use in a few places later to validate the arguments passed to "set_value" p = Proc.new do |my_setting, my_value, my_type| success = (my_setting == :certname) && (my_type == :cli) && (all_devices.include?(my_value)) found_devices.add(my_value) if success success #true end seq = sequence("clean up certname") all_devices.size.times do ## one occurrence of set / run / set("certname") for each device Puppet.settings.expects(:set_value).with(&p).in_sequence(seq) @configurer.expects(:run).in_sequence(seq) Puppet.settings.expects(:set_value).with(:certname, "certname", :cli).in_sequence(seq) end @device.main # make sure that we were called with each of the defined devices all_devices.should == found_devices end it "should expire all cached attributes" do Puppet::SSL::Host.expects(:reset).twice @device.main end end end end diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb index aa5a1eef6..6f1ce884e 100755 --- a/spec/unit/application/doc_spec.rb +++ b/spec/unit/application/doc_spec.rb @@ -1,331 +1,331 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/doc' require 'puppet/util/reference' require 'puppet/util/rdoc' describe Puppet::Application::Doc do before :each do @doc = Puppet::Application[:doc] @doc.stubs(:puts) @doc.preinit Puppet::Util::Log.stubs(:newdestination) end it "should declare a other command" do @doc.should respond_to(:other) end it "should declare a rdoc command" do @doc.should respond_to(:rdoc) end it "should declare a fallback for unknown options" do @doc.should respond_to(:handle_unknown) end it "should declare a preinit block" do @doc.should respond_to(:preinit) end describe "in preinit" do it "should set references to []" do @doc.preinit @doc.options[:references].should == [] end it "should init mode to text" do @doc.preinit @doc.options[:mode].should == :text end it "should init format to to_markdown" do @doc.preinit @doc.options[:format].should == :to_markdown end end describe "when handling options" do [:all, :outputdir, :verbose, :debug, :charset].each do |option| it "should declare handle_#{option} method" do @doc.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @doc.options.expects(:[]=).with(option, 'arg') @doc.send("handle_#{option}".to_sym, 'arg') end end it "should store the format if valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(true) @doc.options.expects(:[]=).with(:format, 'to_format') @doc.handle_format('format') end it "should raise an error if the format is not valid" do Puppet::Util::Reference.stubs(:method_defined?).with('to_format').returns(false) lambda { @doc.handle_format('format') } end it "should store the mode if valid" do Puppet::Util::Reference.stubs(:modes).returns(stub('mode', :include? => true)) @doc.options.expects(:[]=).with(:mode, :mode) @doc.handle_mode('mode') end it "should store the mode if :rdoc" do Puppet::Util::Reference.modes.stubs(:include?).with('rdoc').returns(false) @doc.options.expects(:[]=).with(:mode, :rdoc) @doc.handle_mode('rdoc') end it "should raise an error if the mode is not valid" do Puppet::Util::Reference.modes.stubs(:include?).with('unknown').returns(false) lambda { @doc.handle_mode('unknown') } end it "should list all references on list and exit" do reference = stubs 'reference' ref = stubs 'ref' Puppet::Util::Reference.stubs(:references).returns([reference]) Puppet::Util::Reference.expects(:reference).with(reference).returns(ref) ref.expects(:doc) expect { @doc.handle_list(nil) }.to exit_with 0 end it "should add reference to references list with --reference" do @doc.options[:references] = [:ref1] @doc.handle_reference('ref2') @doc.options[:references].should == [:ref1,:ref2] end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) @doc.command_line.stubs(:args).returns([]) end it "should default to rdoc mode if there are command line arguments" do @doc.command_line.stubs(:args).returns(["1"]) @doc.stubs(:setup_rdoc) @doc.options.expects(:[]=).with(:mode,:rdoc) @doc.setup end it "should call setup_rdoc in rdoc mode" do @doc.options.stubs(:[]).with(:mode).returns(:rdoc) @doc.expects(:setup_rdoc) @doc.setup end it "should call setup_reference if not rdoc" do @doc.options.stubs(:[]).with(:mode).returns(:test) @doc.expects(:setup_reference) @doc.setup end describe "in non-rdoc mode" do it "should get all non-dynamic reference if --all" do @doc.options.stubs(:[]).with(:all).returns(true) @doc.options.stubs(:[]).with(:references).returns([]) static = stub 'static', :dynamic? => false dynamic = stub 'dynamic', :dynamic? => true Puppet::Util::Reference.stubs(:reference).with(:static).returns(static) Puppet::Util::Reference.stubs(:reference).with(:dynamic).returns(dynamic) Puppet::Util::Reference.stubs(:references).returns([:static,:dynamic]) @doc.options.stubs(:[]=).with(:references, [:static]) @doc.setup_reference end it "should default to :type if no references" do @doc.options.stubs(:[]).with(:all).returns(false) array = stub 'array', :empty? => true @doc.options.stubs(:[]).with(:references).returns(array) array.expects(:<<).with(:type) @doc.setup_reference end end describe "in rdoc mode" do before :each do @doc.options.stubs(:[]).returns(false) Puppet::Util::Log.stubs(:newdestination) end describe "when there are unknown args" do it "should expand --modulepath if any" do @doc.unknown_args = [ { :opt => "--modulepath", :arg => "path" } ] Puppet.settings.stubs(:handlearg) File.expects(:expand_path).with("path") @doc.setup_rdoc end it "should expand --manifestdir if any" do @doc.unknown_args = [ { :opt => "--manifestdir", :arg => "path" } ] Puppet.settings.stubs(:handlearg) File.expects(:expand_path).with("path") @doc.setup_rdoc end it "should give them to Puppet.settings" do @doc.unknown_args = [ { :opt => :option, :arg => :argument } ] Puppet.settings.expects(:handlearg).with(:option,:argument) @doc.setup_rdoc end end it "should operate in master run_mode" do @doc.class.run_mode.name.should == :master @doc.setup_rdoc end it "should set log level to debug if --debug" do @doc.options.stubs(:[]).with(:debug).returns(true) @doc.setup_rdoc Puppet::Util::Log.level.should == :debug end it "should set log level to info if --verbose" do @doc.options.stubs(:[]).with(:verbose).returns(true) @doc.setup_rdoc Puppet::Util::Log.level.should == :info end it "should set log destination to console if --verbose" do @doc.options.stubs(:[]).with(:verbose).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup_rdoc end it "should set log destination to console if --debug" do @doc.options.stubs(:[]).with(:debug).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @doc.setup_rdoc end end end describe "when running" do describe "in rdoc mode" do before :each do @doc.manifest = false Puppet.stubs(:info) Puppet.stubs(:[]).with(:trace).returns(false) @env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(@env) @env.stubs(:modulepath).returns(['modules']) @env.stubs(:[]).with(:manifest).returns('manifests/site.pp') Puppet.stubs(:[]).with(:modulepath).returns('modules') Puppet.stubs(:[]).with(:manifestdir).returns('manifests') @doc.options.stubs(:[]).with(:all).returns(false) @doc.options.stubs(:[]).with(:outputdir).returns('doc') @doc.options.stubs(:[]).with(:charset).returns(nil) Puppet.settings.stubs(:[]=).with(:document_all, false) Puppet.settings.stubs(:define_settings) Puppet::Util::RDoc.stubs(:rdoc) File.stubs(:expand_path).with('modules').returns('modules') File.stubs(:expand_path).with('manifests').returns('manifests') @doc.command_line.stubs(:args).returns([]) end it "should set document_all on --all" do @doc.options.expects(:[]).with(:all).returns(true) Puppet.settings.expects(:[]=).with(:document_all, true) expect { @doc.rdoc }.to exit_with 0 end it "should call Puppet::Util::RDoc.rdoc in full mode" do Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) expect { @doc.rdoc }.to exit_with 0 end it "should call Puppet::Util::RDoc.rdoc with a charset if --charset has been provided" do @doc.options.expects(:[]).with(:charset).returns("utf-8") Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], "utf-8") expect { @doc.rdoc }.to exit_with 0 end it "should call Puppet::Util::RDoc.rdoc in full mode with outputdir set to doc if no --outputdir" do @doc.options.expects(:[]).with(:outputdir).returns(false) Puppet::Util::RDoc.expects(:rdoc).with('doc', ['modules','manifests'], nil) expect { @doc.rdoc }.to exit_with 0 end it "should call Puppet::Util::RDoc.manifestdoc in manifest mode" do @doc.manifest = true Puppet::Util::RDoc.expects(:manifestdoc) expect { @doc.rdoc }.to exit_with 0 end it "should get modulepath and manifestdir values from the environment" do @env.expects(:modulepath).returns(['envmodules1','envmodules2']) @env.expects(:[]).with(:manifest).returns('envmanifests/site.pp') Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil) expect { @doc.rdoc }.to exit_with 0 end end describe "in the other modes" do it "should get reference in given format" do reference = stub 'reference' @doc.options.stubs(:[]).with(:mode).returns(:none) @doc.options.stubs(:[]).with(:references).returns([:ref]) require 'puppet/util/reference' Puppet::Util::Reference.expects(:reference).with(:ref).returns(reference) @doc.options.stubs(:[]).with(:format).returns(:format) @doc.stubs(:exit) reference.expects(:send).with { |format,contents| format == :format }.returns('doc') @doc.other end end end end diff --git a/spec/unit/application/face_base_spec.rb b/spec/unit/application/face_base_spec.rb index 97f9bb933..03525519b 100755 --- a/spec/unit/application/face_base_spec.rb +++ b/spec/unit/application/face_base_spec.rb @@ -1,387 +1,387 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/face_base' require 'tmpdir' class Puppet::Application::FaceBase::Basetest < Puppet::Application::FaceBase end describe Puppet::Application::FaceBase do let :app do app = Puppet::Application::FaceBase::Basetest.new app.command_line.stubs(:subcommand_name).returns('subcommand') Puppet::Util::Log.stubs(:newdestination) app end describe "#find_global_settings_argument" do it "should not match --ca to --ca-location" do option = mock('ca option', :optparse_args => ["--ca"]) Puppet.settings.expects(:each).yields(:ca, option) app.find_global_settings_argument("--ca-location").should be_nil end end describe "#parse_options" do before :each do app.command_line.stubs(:args).returns %w{} end describe "with just an action" do before :all do # We have to stub Signal.trap to avoid a crazy mess where we take # over signal handling and make it impossible to cancel the test # suite run. # # It would be nice to fix this elsewhere, but it is actually hard to # capture this in rspec 2.5 and all. :( --daniel 2011-04-08 Signal.stubs(:trap) app.command_line.stubs(:args).returns %w{foo} app.preinit app.parse_options end it "should set the face based on the type" do app.face.name.should == :basetest end it "should find the action" do app.action.should be app.action.name.should == :foo end end it "should stop if the first thing found is not an action" do app.command_line.stubs(:args).returns %w{banana count_args} expect { app.run }.to exit_with 1 @logs.first.should_not be_nil @logs.first.message.should =~ /has no 'banana' action/ end it "should use the default action if not given any arguments" do app.command_line.stubs(:args).returns [] action = stub(:options => [], :render_as => nil) Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(action) app.stubs(:main) app.run app.action.should == action app.arguments.should == [ { } ] end it "should use the default action if not given a valid one" do app.command_line.stubs(:args).returns %w{bar} action = stub(:options => [], :render_as => nil) Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(action) app.stubs(:main) app.run app.action.should == action app.arguments.should == [ 'bar', { } ] end it "should have no action if not given a valid one and there is no default action" do app.command_line.stubs(:args).returns %w{bar} Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil) app.stubs(:main) expect { app.run }.to exit_with 1 @logs.first.message.should =~ /has no 'bar' action./ end [%w{something_I_cannot_do}, %w{something_I_cannot_do argument}].each do |input| it "should report unknown actions nicely" do app.command_line.stubs(:args).returns input Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil) app.stubs(:main) expect { app.run }.to exit_with 1 @logs.first.message.should =~ /has no 'something_I_cannot_do' action/ end end [%w{something_I_cannot_do --unknown-option}, %w{something_I_cannot_do argument --unknown-option}].each do |input| it "should report unknown actions even if there are unknown options" do app.command_line.stubs(:args).returns input Puppet::Face[:basetest, '0.0.1'].expects(:get_default_action).returns(nil) app.stubs(:main) expect { app.run }.to exit_with 1 @logs.first.message.should =~ /has no 'something_I_cannot_do' action/ end end it "should report a sensible error when options with = fail" do app.command_line.stubs(:args).returns %w{--action=bar foo} expect { app.preinit; app.parse_options }. to raise_error OptionParser::InvalidOption, /invalid option: --action/ end it "should fail if an action option is before the action" do app.command_line.stubs(:args).returns %w{--action foo} expect { app.preinit; app.parse_options }. to raise_error OptionParser::InvalidOption, /invalid option: --action/ end it "should fail if an unknown option is before the action" do app.command_line.stubs(:args).returns %w{--bar foo} expect { app.preinit; app.parse_options }. to raise_error OptionParser::InvalidOption, /invalid option: --bar/ end it "should fail if an unknown option is after the action" do app.command_line.stubs(:args).returns %w{foo --bar} expect { app.preinit; app.parse_options }. to raise_error OptionParser::InvalidOption, /invalid option: --bar/ end it "should accept --bar as an argument to a mandatory option after action" do app.command_line.stubs(:args).returns %w{foo --mandatory --bar} app.preinit app.parse_options app.action.name.should == :foo app.options.should == { :mandatory => "--bar" } end it "should accept --bar as an argument to a mandatory option before action" do app.command_line.stubs(:args).returns %w{--mandatory --bar foo} app.preinit app.parse_options app.action.name.should == :foo app.options.should == { :mandatory => "--bar" } end it "should not skip when --foo=bar is given" do app.command_line.stubs(:args).returns %w{--mandatory=bar --bar foo} expect { app.preinit; app.parse_options }. to raise_error OptionParser::InvalidOption, /invalid option: --bar/ end { "boolean options before" => %w{--trace foo}, "boolean options after" => %w{foo --trace} }.each do |name, args| it "should accept global boolean settings #{name} the action" do app.command_line.stubs(:args).returns args Puppet.settings.initialize_global_settings(args) app.preinit app.parse_options Puppet[:trace].should be_true end end { "before" => %w{--syslogfacility user1 foo}, " after" => %w{foo --syslogfacility user1} }.each do |name, args| it "should accept global settings with arguments #{name} the action" do app.command_line.stubs(:args).returns args Puppet.settings.initialize_global_settings(args) app.preinit app.parse_options Puppet[:syslogfacility].should == "user1" end end it "should handle application-level options" do app.command_line.stubs(:args).returns %w{--verbose return_true} app.preinit app.parse_options app.face.name.should == :basetest end end describe "#setup" do it "should remove the action name from the arguments" do app.command_line.stubs(:args).returns %w{--mandatory --bar foo} app.preinit app.parse_options app.setup app.arguments.should == [{ :mandatory => "--bar" }] end it "should pass positional arguments" do myargs = %w{--mandatory --bar foo bar baz quux} app.command_line.stubs(:args).returns(myargs) app.preinit app.parse_options app.setup app.arguments.should == ['bar', 'baz', 'quux', { :mandatory => "--bar" }] end end describe "#main" do before :each do app.stubs(:puts) # don't dump text to screen. app.face = Puppet::Face[:basetest, '0.0.1'] app.action = app.face.get_action(:foo) app.arguments = ["myname", "myarg"] end it "should send the specified verb and name to the face" do app.face.expects(:foo).with(*app.arguments) expect { app.main }.to exit_with 0 end it "should lookup help when it cannot do anything else" do app.action = nil Puppet::Face[:help, :current].expects(:help).with(:basetest) expect { app.main }.to exit_with 1 end it "should use its render method to render any result" do app.expects(:render).with(app.arguments.length + 1, ["myname", "myarg"]) expect { app.main }.to exit_with 0 end end describe "error reporting" do before :each do app.stubs(:puts) # don't dump text to screen. app.render_as = :json app.face = Puppet::Face[:basetest, '0.0.1'] app.arguments = [{}] # we always have options in there... end it "should exit 0 when the action returns true" do app.action = app.face.get_action :return_true expect { app.main }.to exit_with 0 end it "should exit 0 when the action returns false" do app.action = app.face.get_action :return_false expect { app.main }.to exit_with 0 end it "should exit 0 when the action returns nil" do app.action = app.face.get_action :return_nil expect { app.main }.to exit_with 0 end it "should exit non-0 when the action raises" do app.action = app.face.get_action :return_raise expect { app.main }.not_to exit_with 0 end it "should use the exit code set by the action" do app.action = app.face.get_action :with_specific_exit_code expect { app.main }.to exit_with 5 end end describe "#render" do before :each do app.face = Puppet::Face[:basetest, '0.0.1'] app.action = app.face.get_action(:foo) end context "default rendering" do before :each do app.setup end ["hello", 1, 1.0].each do |input| it "should just return a #{input.class.name}" do app.render(input, {}).should == input end end [[1, 2], ["one"], [{ 1 => 1 }]].each do |input| it "should render #{input.class} using JSON" do app.render(input, {}).should == input.to_pson.chomp end end it "should render a non-trivially-keyed Hash with using JSON" do hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 } app.render(hash, {}).should == hash.to_pson.chomp end it "should render a {String,Numeric}-keyed Hash into a table" do object = Object.new hash = { "one" => 1, "two" => [], "three" => {}, "four" => object, 5 => 5, 6.0 => 6 } # Gotta love ASCII-betical sort order. Hope your objects are better # structured for display than my test one is. --daniel 2011-04-18 app.render(hash, {}).should == < { "1" => '1' * 40, "2" => '2' * 40, '3' => '3' * 40 }, "text" => { "a" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 } } app.render(hash, {}).should == <"1111111111111111111111111111111111111111", "2"=>"2222222222222222222222222222222222222222", "3"=>"3333333333333333333333333333333333333333"} text {"a"=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "b"=>"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "c"=>"cccccccccccccccccccccccccccccccccccccccc"} EOT end describe "when setting the rendering method" do after do # need to reset the when_rendering block so that other tests can set it later app.action.instance_variable_set("@when_rendering", {}) end it "should invoke the action rendering hook while rendering" do app.action.set_rendering_method_for(:console, proc { |value| "bi-winning!" }) app.render("bi-polar?", {}).should == "bi-winning!" end it "should invoke the action rendering hook with args and options while rendering" do app.action.instance_variable_set("@when_rendering", {}) app.action.when_invoked = proc { |name, options| 'just need to match arity for rendering' } app.action.set_rendering_method_for( :console, proc { |value, name, options| "I'm #{name}, no wait, I'm #{options[:altername]}" } ) app.render("bi-polar?", ['bob', {:altername => 'sue'}]).should == "I'm bob, no wait, I'm sue" end end it "should render JSON when asked for json" do app.render_as = :json json = app.render({ :one => 1, :two => 2 }, {}) json.should =~ /"one":\s*1\b/ json.should =~ /"two":\s*2\b/ PSON.parse(json).should == { "one" => 1, "two" => 2 } end end it "should fail early if asked to render an invalid format" do app.command_line.stubs(:args).returns %w{--render-as interpretive-dance return_true} # We shouldn't get here, thanks to the exception, and our expectation on # it, but this helps us fail if that slips up and all. --daniel 2011-04-27 Puppet::Face[:help, :current].expects(:help).never Puppet.expects(:err).with("Could not parse application options: I don't know how to render 'interpretive-dance'") expect { app.run }.to exit_with 1 end it "should work if asked to render a NetworkHandler format" do app.command_line.stubs(:args).returns %w{count_args a b c --render-as yaml} expect { expect { app.run }.to exit_with 0 }.to have_printed(/--- 3/) end it "should invoke when_rendering hook 's' when asked to render-as 's'" do app.command_line.stubs(:args).returns %w{with_s_rendering_hook --render-as s} app.action = app.face.get_action(:with_s_rendering_hook) expect { expect { app.run }.to exit_with 0 }.to have_printed(/you invoked the 's' rendering hook/) end end end diff --git a/spec/unit/application/facts_spec.rb b/spec/unit/application/facts_spec.rb index 1c4e8321f..73c1a3729 100755 --- a/spec/unit/application/facts_spec.rb +++ b/spec/unit/application/facts_spec.rb @@ -1,28 +1,28 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/facts' describe Puppet::Application::Facts do before :each do subject.command_line.stubs(:subcommand_name).returns 'facts' end it "should fail if no key is given to find" do subject.command_line.stubs(:args).returns %w{find} expect { expect { subject.run }.to exit_with 1 }.to have_printed /Error: puppet facts find takes 1 argument, but you gave 0/ @logs.first.to_s.should =~ /puppet facts find takes 1 argument, but you gave 0/ end it "should return facts if a key is given to find" do Puppet::Node::Facts.indirection.reset_terminus_class subject.command_line.stubs(:args).returns %w{find whatever --render-as yaml} expect { expect { subject.run }.to exit_with 0 }.should have_printed(/object:Puppet::Node::Facts/) @logs.should be_empty end end diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb index 30264a4d8..4a62b03f3 100755 --- a/spec/unit/application/filebucket_spec.rb +++ b/spec/unit/application/filebucket_spec.rb @@ -1,206 +1,206 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/filebucket' require 'puppet/file_bucket/dipper' describe Puppet::Application::Filebucket do before :each do @filebucket = Puppet::Application[:filebucket] end it "should declare a get command" do @filebucket.should respond_to(:get) end it "should declare a backup command" do @filebucket.should respond_to(:backup) end it "should declare a restore command" do @filebucket.should respond_to(:restore) end [:bucket, :debug, :local, :remote, :verbose].each do |option| it "should declare handle_#{option} method" do @filebucket.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @filebucket.options.expects(:[]=).with("#{option}".to_sym, 'arg') @filebucket.send("handle_#{option}".to_sym, 'arg') end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::FileBucket::Dipper.stubs(:new) @filebucket.options.stubs(:[]).with(any_parameters) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @filebucket.setup end it "should trap INT" do Signal.expects(:trap).with(:INT) @filebucket.setup end it "should set log level to debug if --debug was passed" do @filebucket.options.stubs(:[]).with(:debug).returns(true) @filebucket.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @filebucket.options.stubs(:[]).with(:verbose).returns(true) @filebucket.setup Puppet::Log.level.should == :info end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs).returns(true) expect { @filebucket.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @filebucket.setup }.to exit_with 1 end describe "with local bucket" do before :each do @filebucket.options.stubs(:[]).with(:local).returns(true) end it "should create a client with the default bucket if none passed" do Puppet.stubs(:[]).with(:bucketdir).returns("path") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } @filebucket.setup end it "should create a local Dipper with the given bucket" do @filebucket.options.stubs(:[]).with(:bucket).returns("path") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Path] == "path" } @filebucket.setup end end describe "with remote bucket" do it "should create a remote Client to the configured server" do Puppet.stubs(:[]).with(:server).returns("puppet.reductivelabs.com") Puppet::FileBucket::Dipper.expects(:new).with { |h| h[:Server] == "puppet.reductivelabs.com" } @filebucket.setup end end end describe "when running" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::FileBucket::Dipper.stubs(:new) @filebucket.options.stubs(:[]).with(any_parameters) @client = stub 'client' Puppet::FileBucket::Dipper.stubs(:new).returns(@client) @filebucket.setup end it "should use the first non-option parameter as the dispatch" do @filebucket.command_line.stubs(:args).returns(['get']) @filebucket.expects(:get) @filebucket.run_command end describe "the command get" do before :each do @filebucket.stubs(:print) @filebucket.stubs(:args).returns([]) end it "should call the client getfile method" do @client.expects(:getfile) @filebucket.get end it "should call the client getfile method with the given md5" do md5="DEADBEEF" @filebucket.stubs(:args).returns([md5]) @client.expects(:getfile).with(md5) @filebucket.get end it "should print the file content" do @client.stubs(:getfile).returns("content") @filebucket.expects(:print).returns("content") @filebucket.get end end describe "the command backup" do it "should fail if no arguments are specified" do @filebucket.stubs(:args).returns([]) lambda { @filebucket.backup }.should raise_error end it "should call the client backup method for each given parameter" do @filebucket.stubs(:puts) FileTest.stubs(:exists?).returns(true) FileTest.stubs(:readable?).returns(true) @filebucket.stubs(:args).returns(["file1", "file2"]) @client.expects(:backup).with("file1") @client.expects(:backup).with("file2") @filebucket.backup end end describe "the command restore" do it "should call the client getfile method with the given md5" do md5="DEADBEEF" file="testfile" @filebucket.stubs(:args).returns([file, md5]) @client.expects(:restore).with(file,md5) @filebucket.restore end end end end diff --git a/spec/unit/application/indirection_base_spec.rb b/spec/unit/application/indirection_base_spec.rb index 5d5660334..dfbc655b8 100755 --- a/spec/unit/application/indirection_base_spec.rb +++ b/spec/unit/application/indirection_base_spec.rb @@ -1,48 +1,48 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/indirection_base' require 'puppet/indirector/face' ######################################################################## # Stub for testing; the names are critical, sadly. --daniel 2011-03-30 class Puppet::Application::TestIndirection < Puppet::Application::IndirectionBase end face = Puppet::Indirector::Face.define(:test_indirection, '0.0.1') do summary "fake summary" copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" end # REVISIT: This horror is required because we don't allow anything to be # :current except for if it lives on, and is loaded from, disk. --daniel 2011-03-29 face.instance_variable_set('@version', :current) Puppet::Face.register(face) ######################################################################## describe Puppet::Application::IndirectionBase do subject { Puppet::Application::TestIndirection.new } it "should accept a terminus command line option" do # It would be nice not to have to stub this, but whatever... writing an # entire indirection stack would cause us more grief. --daniel 2011-03-31 terminus = stub_everything("test indirection terminus") terminus.stubs(:name).returns(:test_indirection) # This is necessary because Instrumentation tickles indirection, which # messes up our expectations. Puppet::Util::Instrumentation.stubs(:init) Puppet::Indirector::Indirection.expects(:instance). with(:test_indirection).returns(terminus) subject.command_line.instance_variable_set('@args', %w{--terminus foo save bar}) # Not a very nice thing. :( $stderr.stubs(:puts) Puppet.stubs(:err) expect { subject.run }.to exit_with 0 end end diff --git a/spec/unit/application/inspect_spec.rb b/spec/unit/application/inspect_spec.rb index 13db4123c..73d0f1fa6 100755 --- a/spec/unit/application/inspect_spec.rb +++ b/spec/unit/application/inspect_spec.rb @@ -1,285 +1,285 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/inspect' require 'puppet/resource/catalog' require 'puppet/indirector/catalog/yaml' require 'puppet/indirector/report/rest' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::Application::Inspect do include PuppetSpec::Files before :each do @inspect = Puppet::Application[:inspect] @inspect.preinit end it "should operate in agent run_mode" do @inspect.class.run_mode.name.should == :agent end describe "during setup" do it "should print its configuration if asked" do Puppet[:configprint] = "all" Puppet.settings.expects(:print_configs).returns(true) expect { @inspect.setup }.to exit_with 0 end it "should fail if reporting is turned off" do Puppet[:report] = false lambda { @inspect.setup }.should raise_error(/report=true/) end end describe "when executing" do before :each do Puppet[:report] = true @inspect.options[:logset] = true Puppet::Transaction::Report::Rest.any_instance.stubs(:save) @inspect.setup end it "should retrieve the local catalog" do Puppet::Resource::Catalog::Yaml.any_instance.expects(:find).with {|request| request.key == Puppet[:certname] }.returns(Puppet::Resource::Catalog.new) @inspect.run_command end it "should save the report to REST" do Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(Puppet::Resource::Catalog.new) Puppet::Transaction::Report::Rest.any_instance.expects(:save).with {|request| request.instance.host == Puppet[:certname] } @inspect.run_command end it "should audit the specified properties" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") file.binmode file.puts("file contents") file.close resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command properties = events.inject({}) do |property_values, event| property_values.merge(event.property => event.previous_value) end properties["ensure"].should == :file properties["content"].should == "{md5}#{Digest::MD5.hexdigest("file contents\n")}" properties.has_key?("target").should == false end it "should set audited to true for all events" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command events.each do |event| event.audited.should == true end end it "should not report irrelevent attributes if the resource is absent" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) file.close file.delete catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command properties = events.inject({}) do |property_values, event| property_values.merge(event.property => event.previous_value) end properties.should == {"ensure" => :absent} end describe "when archiving to a bucket" do before :each do Puppet[:archive_files] = true Puppet[:archive_file_server] = "filebucketserver" @catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(@catalog) end describe "when auditing files" do before :each do @file = tmpfile("foo") @resource = Puppet::Resource.new(:file, @file, :parameters => {:audit => "content"}) @catalog.add_resource(@resource) end it "should send an existing file to the file bucket" do File.open(@file, 'w') { |f| f.write('stuff') } Puppet::FileBucketFile::Rest.any_instance.expects(:head).with do |request| request.server == Puppet[:archive_file_server] end.returns(false) Puppet::FileBucketFile::Rest.any_instance.expects(:save).with do |request| request.server == Puppet[:archive_file_server] and request.instance.contents == 'stuff' end @inspect.run_command end it "should not send unreadable files", :unless => Puppet.features.microsoft_windows? do File.open(@file, 'w') { |f| f.write('stuff') } File.chmod(0, @file) Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should not try to send non-existent files" do Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should not try to send files whose content we are not auditing" do @resource[:audit] = "group" Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should continue if bucketing a file fails" do File.open(@file, 'w') { |f| f.write('stuff') } Puppet::FileBucketFile::Rest.any_instance.stubs(:head).returns false Puppet::FileBucketFile::Rest.any_instance.stubs(:save).raises "failure" Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| @report = request.instance end @inspect.run_command @report.logs.first.should_not == nil @report.logs.first.message.should =~ /Could not back up/ end end describe "when auditing non-files" do before :each do Puppet::Type.newtype(:stub_type) do newparam(:name) do desc "The name var" isnamevar end newproperty(:content) do desc "content" def retrieve :whatever end end end @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) end after :each do Puppet::Type.rmtype(:stub_type) end it "should not try to send non-files" do Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end end end describe "when there are failures" do before :each do Puppet::Type.newtype(:stub_type) do newparam(:name) do desc "The name var" isnamevar end newproperty(:content) do desc "content" def retrieve raise "failed" end end end @catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(@catalog) Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| @report = request.instance end end after :each do Puppet::Type.rmtype(:stub_type) end it "should mark the report failed and create failed events for each property" do @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) @inspect.run_command @report.status.should == "failed" @report.logs.select{|log| log.message =~ /Could not inspect/}.size.should == 1 @report.resource_statuses.size.should == 1 @report.resource_statuses['Stub_type[foo]'].events.size.should == 1 event = @report.resource_statuses['Stub_type[foo]'].events.first event.property.should == "content" event.status.should == "failure" event.audited.should == true event.instance_variables.should_not include "@previous_value" event.instance_variables.should_not include :@previous_value end it "should continue to the next resource" do @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @other_resource = Puppet::Resource.new(:stub_type, 'bar', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) @catalog.add_resource(@other_resource) @inspect.run_command @report.resource_statuses.size.should == 2 @report.resource_statuses.keys.should =~ ['Stub_type[foo]', 'Stub_type[bar]'] end end end after :all do Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet::Transaction::Report.indirection.terminus_class = :processor end end diff --git a/spec/unit/application/kick_spec.rb b/spec/unit/application/kick_spec.rb index 3acd92033..a0993499f 100755 --- a/spec/unit/application/kick_spec.rb +++ b/spec/unit/application/kick_spec.rb @@ -1,283 +1,283 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/kick' describe Puppet::Application::Kick, :if => Puppet.features.posix? do before :each do require 'puppet/util/ldap/connection' Puppet::Util::Ldap::Connection.stubs(:new).returns(stub_everything) @kick = Puppet::Application[:kick] Puppet::Util::Log.stubs(:newdestination) end describe ".new" do it "should take a command-line object as an argument" do command_line = stub_everything "command_line" lambda{ Puppet::Application::Kick.new( command_line ) }.should_not raise_error end end it "should declare a main command" do @kick.should respond_to(:main) end it "should declare a test command" do @kick.should respond_to(:test) end it "should declare a preinit block" do @kick.should respond_to(:preinit) end describe "during preinit" do before :each do @kick.stubs(:trap) end it "should catch INT and TERM" do @kick.stubs(:trap).with { |arg,block| arg == :INT or arg == :TERM } @kick.preinit end it "should set parallel option to 1" do @kick.preinit @kick.options[:parallel].should == 1 end it "should set verbose by default" do @kick.preinit @kick.options[:verbose].should be_true end it "should set fqdn by default" do @kick.preinit @kick.options[:fqdn].should be_true end it "should set ignoreschedules to 'false'" do @kick.preinit @kick.options[:ignoreschedules].should be_false end it "should set foreground to 'false'" do @kick.preinit @kick.options[:foreground].should be_false end end describe "when applying options" do before do @kick.preinit end [:all, :foreground, :debug, :ping, :test].each do |option| it "should declare handle_#{option} method" do @kick.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @kick.options.expects(:[]=).with(option, 'arg') @kick.send("handle_#{option}".to_sym, 'arg') end end it "should add to the host list with the host option" do @kick.handle_host('host') @kick.hosts.should == ['host'] end it "should add to the tag list with the tag option" do @kick.handle_tag('tag') @kick.tags.should == ['tag'] end it "should add to the class list with the class option" do @kick.handle_class('class') @kick.classes.should == ['class'] end end describe "during setup" do before :each do @kick.classes = [] @kick.tags = [] @kick.hosts = [] @kick.stubs(:trap) @kick.stubs(:puts) @kick.options.stubs(:[]).with(any_parameters) end it "should abort stating that kick is not supported on Windows" do Puppet.features.stubs(:microsoft_windows?).returns(true) expect { @kick.setup }.to raise_error(Puppet::Error, /Puppet kick is not supported on Microsoft Windows/) end it "should set log level to debug if --debug was passed" do @kick.options.stubs(:[]).with(:debug).returns(true) @kick.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @kick.options.stubs(:[]).with(:verbose).returns(true) @kick.setup Puppet::Log.level.should == :info end describe "when using the ldap node terminus" do before :each do Puppet.stubs(:[]).with(:node_terminus).returns("ldap") end it "should pass the fqdn option to search" do @kick.options.stubs(:[]).with(:fqdn).returns(:something) @kick.options.stubs(:[]).with(:all).returns(true) @kick.stubs(:puts) Puppet::Node.indirection.expects(:search).with("whatever",:fqdn => :something).returns([]) @kick.setup end it "should search for all nodes if --all" do @kick.options.stubs(:[]).with(:all).returns(true) @kick.stubs(:puts) Puppet::Node.indirection.expects(:search).with("whatever",:fqdn => nil).returns([]) @kick.setup end it "should search for nodes including given classes" do @kick.options.stubs(:[]).with(:all).returns(false) @kick.stubs(:puts) @kick.classes = ['class'] Puppet::Node.indirection.expects(:search).with("whatever", :class => "class", :fqdn => nil).returns([]) @kick.setup end end describe "when using regular nodes" do it "should fail if some classes have been specified" do $stderr.stubs(:puts) @kick.classes = ['class'] expect { @kick.setup }.to exit_with 24 end end end describe "when running" do before :each do @kick.stubs(:puts) end it "should dispatch to test if --test is used" do @kick.options.stubs(:[]).with(:test).returns(true) @kick.expects(:test) @kick.run_command end it "should dispatch to main if --test is not used" do @kick.options.stubs(:[]).with(:test).returns(false) @kick.expects(:main) @kick.run_command end describe "the test command" do it "should exit with exit code 0 " do expect { @kick.test }.to exit_with 0 end end describe "the main command" do before :each do @kick.options.stubs(:[]).with(:parallel).returns(1) @kick.options.stubs(:[]).with(:ping).returns(false) @kick.options.stubs(:[]).with(:ignoreschedules).returns(false) @kick.options.stubs(:[]).with(:foreground).returns(false) @kick.options.stubs(:[]).with(:debug).returns(false) @kick.stubs(:print) @kick.preinit @kick.stubs(:parse_options) @kick.setup $stderr.stubs(:puts) end it "should create as much childs as --parallel" do @kick.options.stubs(:[]).with(:parallel).returns(3) @kick.hosts = ['host1', 'host2', 'host3'] Process.stubs(:wait).returns(1).then.returns(2).then.returns(3).then.raises(Errno::ECHILD) @kick.expects(:safe_posix_fork).times(3).returns(1).then.returns(2).then.returns(3) expect { @kick.main }.to raise_error SystemExit end it "should delegate to run_for_host per host" do @kick.hosts = ['host1', 'host2'] @kick.stubs(:safe_posix_fork).returns(1).yields Process.stubs(:wait).returns(1).then.raises(Errno::ECHILD) @kick.expects(:run_for_host).times(2) lambda { @kick.main }.should raise_error end describe "during call of run_for_host" do before do require 'puppet/run' options = { :background => true, :ignoreschedules => false, :tags => [] } @kick.preinit @agent_run = Puppet::Run.new( options.dup ) @agent_run.stubs(:status).returns("success") Puppet::Run.indirection.expects(:terminus_class=).with( :rest ) Puppet::Run.expects(:new).with( options ).returns(@agent_run) end it "should call run on a Puppet::Run for the given host" do Puppet::Run.indirection.expects(:save).with(@agent_run, 'https://host:8139/production/run/host').returns(@agent_run) expect { @kick.run_for_host('host') }.to exit_with 0 end it "should exit the child with 0 on success" do @agent_run.stubs(:status).returns("success") expect { @kick.run_for_host('host') }.to exit_with 0 end it "should exit the child with 3 on running" do @agent_run.stubs(:status).returns("running") expect { @kick.run_for_host('host') }.to exit_with 3 end it "should exit the child with 12 on unknown answer" do @agent_run.stubs(:status).returns("whatever") expect { @kick.run_for_host('host') }.to exit_with 12 end end end end end diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb index de9fecf6f..4bdf85c2c 100755 --- a/spec/unit/application/master_spec.rb +++ b/spec/unit/application/master_spec.rb @@ -1,377 +1,377 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/master' require 'puppet/daemon' require 'puppet/network/server' describe Puppet::Application::Master, :unless => Puppet.features.microsoft_windows? do before :each do @master = Puppet::Application[:master] @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet::Util::Log.stubs(:newdestination) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::SSL::Host.stubs(:ca_location=) end it "should operate in master run_mode" do @master.class.run_mode.name.should equal(:master) end it "should declare a main command" do @master.should respond_to(:main) end it "should declare a compile command" do @master.should respond_to(:compile) end it "should declare a preinit block" do @master.should respond_to(:preinit) end describe "during preinit" do before :each do @master.stubs(:trap) end it "should catch INT" do @master.stubs(:trap).with { |arg,block| arg == :INT } @master.preinit end it "should create a Puppet Daemon" do Puppet::Daemon.expects(:new).returns(@daemon) @master.preinit end it "should give ARGV to the Daemon" do argv = stub 'argv' ARGV.stubs(:dup).returns(argv) @daemon.expects(:argv=).with(argv) @master.preinit end end [:debug,:verbose].each do |option| it "should declare handle_#{option} method" do @master.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @master.options.expects(:[]=).with(option, 'arg') @master.send("handle_#{option}".to_sym, 'arg') end end describe "when applying options" do before do @master.command_line.stubs(:args).returns([]) end it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @master.handle_logdest("console") end it "should put the setdest options to true" do @master.options.expects(:[]=).with(:setdest,true) @master.handle_logdest("console") end it "should parse the log destination from ARGV" do @master.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @master.parse_options end it "should support dns alt names from ARGV" do Puppet.settings.initialize_global_settings(["--dns_alt_names", "foo,bar,baz"]) @master.preinit @master.parse_options Puppet[:dns_alt_names].should == "foo,bar,baz" end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::SSL::CertificateAuthority.stubs(:instance) Puppet::SSL::CertificateAuthority.stubs(:ca?) Puppet.settings.stubs(:use) @master.options.stubs(:[]).with(any_parameters) end it "should abort stating that the master is not supported on Windows" do Puppet.features.stubs(:microsoft_windows?).returns(true) expect { @master.setup }.to raise_error(Puppet::Error, /Puppet master is not supported on Microsoft Windows/) end it "should set log level to debug if --debug was passed" do @master.options.stubs(:[]).with(:debug).returns(true) @master.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @master.options.stubs(:[]).with(:verbose).returns(true) @master.setup Puppet::Log.level.should == :info end it "should set console as the log destination if no --logdest and --daemonize" do @master.stubs(:[]).with(:daemonize).returns(:false) Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should set syslog as the log destination if no --logdest and not --daemonize" do Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should set syslog as the log destination if --rack" do @master.options.stubs(:[]).with(:rack).returns(:true) Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs).returns(true) expect { @master.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @master.setup }.to exit_with 1 end it "should tell Puppet.settings to use :main,:ssl,:master and :metrics category" do Puppet.settings.expects(:use).with(:main,:master,:ssl,:metrics) @master.setup end describe "with no ca" do it "should set the ca_location to none" do Puppet::SSL::Host.expects(:ca_location=).with(:none) @master.setup end end describe "with a ca configured" do before :each do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) end it "should set the ca_location to local" do Puppet::SSL::Host.expects(:ca_location=).with(:local) @master.setup end it "should tell Puppet.settings to use :ca category" do Puppet.settings.expects(:use).with(:ca) @master.setup end it "should instantiate the CertificateAuthority singleton" do Puppet::SSL::CertificateAuthority.expects(:instance) @master.setup end end end describe "when running" do before do @master.preinit end it "should dispatch to compile if called with --compile" do @master.options[:node] = "foo" @master.expects(:compile) @master.run_command end it "should dispatch to main otherwise" do @master.options[:node] = nil @master.expects(:main) @master.run_command end describe "the compile command" do before do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @master.stubs(:jj) Puppet.features.stubs(:pson?).returns true end it "should fail if pson isn't available" do Puppet.features.expects(:pson?).returns false lambda { @master.compile }.should raise_error end it "should compile a catalog for the specified node" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).with("foo").returns Puppet::Resource::Catalog.new $stdout.stubs(:puts) expect { @master.compile }.to exit_with 0 end it "should convert the catalog to a pure-resource catalog and use 'jj' to pretty-print the catalog" do catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.indirection.expects(:find).returns catalog catalog.expects(:to_resource).returns("rescat") @master.options[:node] = "foo" @master.expects(:jj).with("rescat") expect { @master.compile }.to exit_with 0 end it "should exit with error code 30 if no catalog can be found" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).returns nil $stderr.expects(:puts) expect { @master.compile }.to exit_with 30 end it "should exit with error code 30 if there's a failure" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).raises ArgumentError $stderr.expects(:puts) expect { @master.compile }.to exit_with 30 end end describe "the main command" do before :each do @master.preinit @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) @app = stub_everything 'app' Puppet::SSL::Host.stubs(:localhost) Puppet::SSL::CertificateAuthority.stubs(:ca?) Process.stubs(:uid).returns(1000) Puppet.stubs(:service) Puppet.stubs(:[]) Puppet.stubs(:notice) Puppet.stubs(:start) end it "should create a Server" do Puppet::Network::Server.expects(:new) @master.main end it "should give the server to the daemon" do @daemon.expects(:server=).with(@server) @master.main end it "should generate a SSL cert for localhost" do Puppet::SSL::Host.expects(:localhost) @master.main end it "should make sure to *only* hit the CA for data" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:only) @master.main end it "should drop privileges if running as root" do Puppet.features.stubs(:root?).returns true Puppet::Util.expects(:chuser) @master.main end it "should daemonize if needed" do Puppet.stubs(:[]).with(:daemonize).returns(true) @daemon.expects(:daemonize) @master.main end it "should start the service" do @daemon.expects(:start) @master.main end describe "with --rack", :if => Puppet.features.rack? do before do require 'puppet/network/http/rack' Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) end it "it should not start a daemon" do @master.options.stubs(:[]).with(:rack).returns(:true) @daemon.expects(:start).never @master.main end it "it should return the app" do @master.options.stubs(:[]).with(:rack).returns(:true) app = @master.main app.should equal(@app) end end end end end diff --git a/spec/unit/application/queue_spec.rb b/spec/unit/application/queue_spec.rb index accfe62b7..ad1c7eda0 100755 --- a/spec/unit/application/queue_spec.rb +++ b/spec/unit/application/queue_spec.rb @@ -1,168 +1,168 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/queue' require 'puppet/indirector/catalog/queue' describe Puppet::Application::Queue, :unless => Puppet.features.microsoft_windows? do before :each do @queue = Puppet::Application[:queue] @queue.stubs(:puts) @daemon = stub_everything 'daemon', :daemonize => nil Puppet::Util::Log.stubs(:newdestination) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) end it "should declare a main command" do @queue.should respond_to(:main) end it "should declare a preinit block" do @queue.should respond_to(:preinit) end describe "in preinit" do it "should catch INT" do Signal.expects(:trap).with { |arg,block| arg == :INT } @queue.preinit end it "should init :verbose to false" do @queue.preinit @queue.options[:verbose].should be_false end it "should init :debug to false" do @queue.preinit @queue.options[:debug].should be_false end it "should create a Daemon instance and copy ARGV to it" do ARGV.expects(:dup).returns "eh" daemon = mock("daemon") daemon.expects(:argv=).with("eh") Puppet::Daemon.expects(:new).returns daemon @queue.preinit end end describe "when handling options" do [:debug, :verbose].each do |option| it "should declare handle_#{option} method" do @queue.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @queue.options.expects(:[]=).with(option, 'arg') @queue.send("handle_#{option}".to_sym, 'arg') end end end describe "during setup" do before :each do @queue.options.stubs(:[]) @queue.daemon.stubs(:daemonize) Puppet.stubs(:info) Puppet.features.stubs(:stomp?).returns true Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet.stubs(:settraps) Puppet.settings.stubs(:print_config?) Puppet.settings.stubs(:print_config) end it "should fail if the stomp feature is missing" do Puppet.features.expects(:stomp?).returns false lambda { @queue.setup }.should raise_error(ArgumentError) end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs).returns(true) expect { @queue.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @queue.setup }.to exit_with 1 end it "should call setup_logs" do @queue.expects(:setup_logs) @queue.setup end describe "when setting up logs" do before :each do Puppet::Util::Log.stubs(:newdestination) end it "should set log level to debug if --debug was passed" do @queue.options.stubs(:[]).with(:debug).returns(true) @queue.setup_logs Puppet::Util::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @queue.options.stubs(:[]).with(:verbose).returns(true) @queue.setup_logs Puppet::Util::Log.level.should == :info end [:verbose, :debug].each do |level| it "should set console as the log destination with level #{level}" do @queue.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @queue.setup_logs end end end it "should configure the Catalog class to use StoreConfigs" do Puppet::Resource::Catalog.indirection.expects(:terminus_class=).with(:store_configs) @queue.setup end it "should daemonize if needed" do Puppet.expects(:[]).with(:daemonize).returns(true) @queue.daemon.expects(:daemonize) @queue.setup end end describe "when running" do before :each do @queue.stubs(:sleep_forever) Puppet::Resource::Catalog::Queue.stubs(:subscribe) Thread.list.each { |t| t.stubs(:join) } end it "should subscribe to the queue" do Puppet::Resource::Catalog::Queue.expects(:subscribe) @queue.main end it "should log and save each catalog passed by the queue" do catalog = Puppet::Resource::Catalog.new('eh') Puppet::Resource::Catalog.indirection.expects(:save).with(catalog) Puppet::Resource::Catalog::Queue.expects(:subscribe).yields(catalog) Puppet.expects(:notice).times(2) @queue.main end it "should join all of the running threads" do Thread.list.each { |t| t.expects(:join) } @queue.main end end end diff --git a/spec/unit/application/resource_spec.rb b/spec/unit/application/resource_spec.rb index 8cf9472d0..edf825941 100755 --- a/spec/unit/application/resource_spec.rb +++ b/spec/unit/application/resource_spec.rb @@ -1,214 +1,214 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/resource' describe Puppet::Application::Resource do include PuppetSpec::Files before :each do @resource_app = Puppet::Application[:resource] Puppet::Util::Log.stubs(:newdestination) Puppet::Resource.indirection.stubs(:terminus_class=) end describe "in preinit" do it "should init extra_params to empty array" do @resource_app.preinit @resource_app.extra_params.should == [] end it "should load Facter facts" do Facter.expects(:loadfacts).once @resource_app.preinit end end describe "when handling options" do [:debug, :verbose, :edit].each do |option| it "should store argument value when calling handle_#{option}" do @resource_app.options.expects(:[]=).with(option, 'arg') @resource_app.send("handle_#{option}".to_sym, 'arg') end end it "should set options[:host] to given host" do @resource_app.handle_host(:whatever) @resource_app.host.should == :whatever end it "should load an display all types with types option" do type1 = stub_everything 'type1', :name => :type1 type2 = stub_everything 'type2', :name => :type2 Puppet::Type.stubs(:loadall) Puppet::Type.stubs(:eachtype).multiple_yields(type1,type2) @resource_app.expects(:puts).with(['type1','type2']) expect { @resource_app.handle_types(nil) }.to exit_with 0 end it "should add param to extra_params list" do @resource_app.extra_params = [ :param1 ] @resource_app.handle_param("whatever") @resource_app.extra_params.should == [ :param1, :whatever ] end it "should get a parameter in the printed data if extra_params are passed" do tty = stub("tty", :tty? => true ) path = tmpfile('testfile') command_line = Puppet::Util::CommandLine.new("puppet", [ 'resource', 'file', path ], tty ) @resource_app.stubs(:command_line).returns command_line # provider is a parameter that should always be available @resource_app.extra_params = [ :provider ] expect { @resource_app.main }.to have_printed /provider\s+=>/ end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) end it "should set console as the log destination" do Puppet::Log.expects(:newdestination).with(:console) @resource_app.setup end it "should set log level to debug if --debug was passed" do @resource_app.options.stubs(:[]).with(:debug).returns(true) @resource_app.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @resource_app.options.stubs(:[]).with(:debug).returns(false) @resource_app.options.stubs(:[]).with(:verbose).returns(true) @resource_app.setup Puppet::Log.level.should == :info end end describe "when running" do before :each do @type = stub_everything 'type', :properties => [] @resource_app.command_line.stubs(:args).returns(['mytype']) Puppet::Type.stubs(:type).returns(@type) @res = stub_everything "resource" @res.stubs(:prune_parameters).returns(@res) @report = stub_everything "report" end it "should raise an error if no type is given" do @resource_app.command_line.stubs(:args).returns([]) lambda { @resource_app.main }.should raise_error(RuntimeError, "You must specify the type to display") end it "should raise an error when editing a remote host" do @resource_app.options.stubs(:[]).with(:edit).returns(true) @resource_app.host = 'host' lambda { @resource_app.main }.should raise_error(RuntimeError, "You cannot edit a remote host") end it "should raise an error if the type is not found" do Puppet::Type.stubs(:type).returns(nil) lambda { @resource_app.main }.should raise_error(RuntimeError, 'Could not find type mytype') end describe "with a host" do before :each do @resource_app.stubs(:puts) @resource_app.host = 'host' Puppet::Resource.indirection.stubs(:find ).never Puppet::Resource.indirection.stubs(:search).never Puppet::Resource.indirection.stubs(:save ).never end it "should search for resources" do @resource_app.command_line.stubs(:args).returns(['type']) Puppet::Resource.indirection.expects(:search).with('https://host:8139/production/resources/type/', {}).returns([]) @resource_app.main end it "should describe the given resource" do @resource_app.command_line.stubs(:args).returns(['type', 'name']) Puppet::Resource.indirection.expects(:find).with('https://host:8139/production/resources/type/name').returns(@res) @resource_app.main end it "should add given parameters to the object" do @resource_app.command_line.stubs(:args).returns(['type','name','param=temp']) Puppet::Resource.indirection.expects(:save). with(@res, 'https://host:8139/production/resources/type/name'). returns([@res, @report]) Puppet::Resource.expects(:new).with('type', 'name', :parameters => {'param' => 'temp'}).returns(@res) @resource_app.main end end describe "without a host" do before :each do @resource_app.stubs(:puts) @resource_app.host = nil Puppet::Resource.indirection.stubs(:find ).never Puppet::Resource.indirection.stubs(:search).never Puppet::Resource.indirection.stubs(:save ).never end it "should search for resources" do Puppet::Resource.indirection.expects(:search).with('mytype/', {}).returns([]) @resource_app.main end it "should describe the given resource" do @resource_app.command_line.stubs(:args).returns(['type','name']) Puppet::Resource.indirection.expects(:find).with('type/name').returns(@res) @resource_app.main end it "should add given parameters to the object" do @resource_app.command_line.stubs(:args).returns(['type','name','param=temp']) Puppet::Resource.indirection.expects(:save).with(@res, 'type/name').returns([@res, @report]) Puppet::Resource.expects(:new).with('type', 'name', :parameters => {'param' => 'temp'}).returns(@res) @resource_app.main end end end describe "when handling file type" do before :each do Facter.stubs(:loadfacts) @resource_app.preinit end it "should raise an exception if no file specified" do @resource_app.command_line.stubs(:args).returns(['file']) lambda { @resource_app.main }.should raise_error(RuntimeError, /Listing all file instances is not supported/) end it "should output a file resource when given a file path" do path = File.expand_path('/etc') res = Puppet::Type.type(:file).new(:path => path).to_resource Puppet::Resource.indirection.expects(:find).returns(res) @resource_app.command_line.stubs(:args).returns(['file', path]) @resource_app.expects(:puts).with do |args| args.should =~ /file \{ '#{Regexp.escape(path)}'/m end @resource_app.main end end end diff --git a/spec/unit/application/secret_agent_spec.rb b/spec/unit/application/secret_agent_spec.rb index d3923406d..a87f29fa5 100755 --- a/spec/unit/application/secret_agent_spec.rb +++ b/spec/unit/application/secret_agent_spec.rb @@ -1,34 +1,34 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application/secret_agent' require 'puppet/indirector/catalog/rest' require 'puppet/indirector/report/rest' require 'tempfile' describe "Puppet::Application::Secret_agent" do include PuppetSpec::Files it "should retrieve and apply a catalog and submit a report" do pending "REVISIT: 2.7 changes broke this, and we want the merge published" dirname = tmpdir("puppetdir") Puppet[:vardir] = dirname Puppet[:confdir] = dirname Puppet[:certname] = "foo" @catalog = Puppet::Resource::Catalog.new @file = Puppet::Resource.new(:file, File.join(dirname, "tmp_dir_resource"), :parameters => {:ensure => :present}) @catalog.add_resource(@file) @report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.stubs(:new).returns(@report) Puppet::Resource::Catalog::Rest.any_instance.stubs(:find).returns(@catalog) @report.expects(:save) Puppet::Util::Log.stubs(:newdestination) Puppet::Application::Secret_agent.new.run @report.status.should == "changed" end end diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb index 807df98f1..8e8e1d950 100755 --- a/spec/unit/application_spec.rb +++ b/spec/unit/application_spec.rb @@ -1,607 +1,607 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/application' require 'puppet' require 'getoptlong' require 'timeout' describe Puppet::Application do before(:each) do Puppet::Util::Instrumentation.stubs(:init) @app = Class.new(Puppet::Application).new @appclass = @app.class @app.stubs(:name).returns("test_app") end describe "application defaults" do it "should fail if required app default values are missing" do @app.stubs(:app_defaults).returns({ :foo => 'bar' }) Puppet.expects(:err).with(regexp_matches(/missing required app default setting/)) expect { @app.run }.to exit_with 1 end end describe "finding" do before do @klass = Puppet::Application @klass.stubs(:puts) end it "should find classes in the namespace" do @klass.find("Agent").should == @klass::Agent end it "should not find classes outside the namespace" do expect { @klass.find("String") }.to raise_error(LoadError) end it "should error if it can't find a class" do Puppet.expects(:err).with do |value| value =~ /Unable to find application 'ThisShallNeverEverEverExist'/ and value =~ /puppet\/application\/thisshallneverevereverexist/ and value =~ /no such file to load|cannot load such file/ end expect { @klass.find("ThisShallNeverEverEverExist") }.to raise_error(LoadError) end it "#12114: should prevent File namespace collisions" do # have to require the file face once, then the second time around it would fail @klass.find("File").should == Puppet::Application::File @klass.find("File").should == Puppet::Application::File end end describe ".run_mode" do it "should default to user" do @appclass.run_mode.name.should == :user end it "should set and get a value" do @appclass.run_mode :agent @appclass.run_mode.name.should == :agent end end # These tests may look a little weird and repetative in its current state; # it used to illustrate several ways that the run_mode could be changed # at run time; there are fewer ways now, but it would still be nice to # get to a point where it was entirely impossible. describe "when dealing with run_mode" do class TestApp < Puppet::Application run_mode :master def run_command # no-op end end it "should sadly and frighteningly allow run_mode to change at runtime via #initialize_app_defaults" do Puppet.features.stubs(:syslog?).returns(true) Puppet[:run_mode].should == :user expect { app = TestApp.new app.initialize_app_defaults Puppet[:run_mode].should == :master }.to_not raise_error end it "should sadly and frighteningly allow run_mode to change at runtime via #run" do expect { app = TestApp.new app.run app.class.run_mode.name.should == :master Puppet[:run_mode].should == :master }.should_not raise_error end end it "it should not allow initialize_app_defaults to be called multiple times" do app = Puppet::Application.new expect { app.initialize_app_defaults }.to_not raise_error expect { app.initialize_app_defaults }.to raise_error end it "should explode when an invalid run mode is set at runtime, for great victory" do class InvalidRunModeTestApp < Puppet::Application run_mode :abracadabra def run_command # no-op end end expect { app = InvalidRunModeTestApp.new app.initialize_app_defaults }.to raise_error end it "should have a run entry-point" do @app.should respond_to(:run) end it "should have a read accessor to options" do @app.should respond_to(:options) end it "should include a default setup method" do @app.should respond_to(:setup) end it "should include a default preinit method" do @app.should respond_to(:preinit) end it "should include a default run_command method" do @app.should respond_to(:run_command) end it "should invoke main as the default" do @app.expects( :main ) @app.run_command end it "should initialize the Puppet Instrumentation layer early in the life cycle" do # Not proud of this, but the fact that we are stubbing init_app_defaults # below means that we will get errors if anyone tries to access any # settings that depend on app_defaults. In general this whole test # seems to be testing too many implementation details rather than # functionality, but, hey. Puppet[:route_file] = "/dev/null" startup_sequence = sequence('startup') @app.expects(:initialize_app_defaults).in_sequence(startup_sequence) Puppet::Util::Instrumentation.expects(:init).in_sequence(startup_sequence) @app.expects(:preinit).in_sequence(startup_sequence) expect { @app.run }.to exit_with(1) end describe 'when invoking clear!' do before :each do Puppet::Application.run_status = :stop_requested Puppet::Application.clear! end it 'should have nil run_status' do Puppet::Application.run_status.should be_nil end it 'should return false for restart_requested?' do Puppet::Application.restart_requested?.should be_false end it 'should return false for stop_requested?' do Puppet::Application.stop_requested?.should be_false end it 'should return false for interrupted?' do Puppet::Application.interrupted?.should be_false end it 'should return true for clear?' do Puppet::Application.clear?.should be_true end end describe 'after invoking stop!' do before :each do Puppet::Application.run_status = nil Puppet::Application.stop! end after :each do Puppet::Application.run_status = nil end it 'should have run_status of :stop_requested' do Puppet::Application.run_status.should == :stop_requested end it 'should return true for stop_requested?' do Puppet::Application.stop_requested?.should be_true end it 'should return false for restart_requested?' do Puppet::Application.restart_requested?.should be_false end it 'should return true for interrupted?' do Puppet::Application.interrupted?.should be_true end it 'should return false for clear?' do Puppet::Application.clear?.should be_false end end describe 'when invoking restart!' do before :each do Puppet::Application.run_status = nil Puppet::Application.restart! end after :each do Puppet::Application.run_status = nil end it 'should have run_status of :restart_requested' do Puppet::Application.run_status.should == :restart_requested end it 'should return true for restart_requested?' do Puppet::Application.restart_requested?.should be_true end it 'should return false for stop_requested?' do Puppet::Application.stop_requested?.should be_false end it 'should return true for interrupted?' do Puppet::Application.interrupted?.should be_true end it 'should return false for clear?' do Puppet::Application.clear?.should be_false end end describe 'when performing a controlled_run' do it 'should not execute block if not :clear?' do Puppet::Application.run_status = :stop_requested target = mock 'target' target.expects(:some_method).never Puppet::Application.controlled_run do target.some_method end end it 'should execute block if :clear?' do Puppet::Application.run_status = nil target = mock 'target' target.expects(:some_method).once Puppet::Application.controlled_run do target.some_method end end describe 'on POSIX systems', :if => Puppet.features.posix? do it 'should signal process with HUP after block if restart requested during block execution' do Timeout::timeout(3) do # if the signal doesn't fire, this causes failure. has_run = false old_handler = trap('HUP') { has_run = true } begin Puppet::Application.controlled_run do Puppet::Application.run_status = :restart_requested end # Ruby 1.9 uses a separate OS level thread to run the signal # handler, so we have to poll - ideally, in a way that will kick # the OS into running other threads - for a while. # # You can't just use the Ruby Thread yield thing either, because # that is just an OS hint, and Linux ... doesn't take that # seriously. --daniel 2012-03-22 sleep 0.001 while not has_run ensure trap('HUP', old_handler) end end end end after :each do Puppet::Application.run_status = nil end end describe "when parsing command-line options" do before :each do @app.command_line.stubs(:args).returns([]) Puppet.settings.stubs(:optparse_addargs).returns([]) end it "should pass the banner to the option parser" do option_parser = stub "option parser" option_parser.stubs(:on) option_parser.stubs(:parse!) @app.class.instance_eval do banner "banner" end OptionParser.expects(:new).with("banner").returns(option_parser) @app.parse_options end it "should ask OptionParser to parse the command-line argument" do @app.command_line.stubs(:args).returns(%w{ fake args }) OptionParser.any_instance.expects(:parse!).with(%w{ fake args }) @app.parse_options end describe "when using --help" do it "should call exit" do @app.stubs(:puts) expect { @app.handle_help(nil) }.to exit_with 0 end end describe "when using --version" do it "should declare a version option" do @app.should respond_to(:handle_version) end it "should exit after printing the version" do @app.stubs(:puts) expect { @app.handle_version(nil) }.to exit_with 0 end end describe "when dealing with an argument not declared directly by the application" do it "should pass it to handle_unknown if this method exists" do Puppet.settings.stubs(:optparse_addargs).returns([["--not-handled", :REQUIRED]]) @app.expects(:handle_unknown).with("--not-handled", "value").returns(true) @app.command_line.stubs(:args).returns(["--not-handled", "value"]) @app.parse_options end it "should transform boolean option to normal form for Puppet.settings" do @app.expects(:handle_unknown).with("--option", true) @app.send(:handlearg, "--[no-]option", true) end it "should transform boolean option to no- form for Puppet.settings" do @app.expects(:handle_unknown).with("--no-option", false) @app.send(:handlearg, "--[no-]option", false) end end end describe "when calling default setup" do before :each do @app.options.stubs(:[]) end [ :debug, :verbose ].each do |level| it "should honor option #{level}" do @app.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.stubs(:newdestination) @app.setup Puppet::Util::Log.level.should == (level == :verbose ? :info : :debug) end end it "should honor setdest option" do @app.options.stubs(:[]).with(:setdest).returns(false) Puppet::Util::Log.expects(:setup_default) @app.setup end end describe "when configuring routes" do include PuppetSpec::Files before :each do Puppet::Node.indirection.reset_terminus_class end after :each do Puppet::Node.indirection.reset_terminus_class end it "should use the routes specified for only the active application" do Puppet[:route_file] = tmpfile('routes') File.open(Puppet[:route_file], 'w') do |f| f.print <<-ROUTES test_app: node: terminus: exec other_app: node: terminus: plain catalog: terminus: invalid ROUTES end @app.configure_indirector_routes Puppet::Node.indirection.terminus_class.should == 'exec' end it "should not fail if the route file doesn't exist" do Puppet[:route_file] = "/dev/null/non-existent" expect { @app.configure_indirector_routes }.should_not raise_error end it "should raise an error if the routes file is invalid" do Puppet[:route_file] = tmpfile('routes') File.open(Puppet[:route_file], 'w') do |f| f.print <<-ROUTES invalid : : yaml ROUTES end expect { @app.configure_indirector_routes }.should raise_error end end describe "when running" do before :each do @app.stubs(:preinit) @app.stubs(:setup) @app.stubs(:parse_options) end it "should call preinit" do @app.stubs(:run_command) @app.expects(:preinit) @app.run end it "should call parse_options" do @app.stubs(:run_command) @app.expects(:parse_options) @app.run end it "should call run_command" do @app.expects(:run_command) @app.run end it "should call run_command" do @app.expects(:run_command) @app.run end it "should call main as the default command" do @app.expects(:main) @app.run end it "should warn and exit if no command can be called" do Puppet.expects(:err) expect { @app.run }.to exit_with 1 end it "should raise an error if dispatch returns no command" do @app.stubs(:get_command).returns(nil) Puppet.expects(:err) expect { @app.run }.to exit_with 1 end it "should raise an error if dispatch returns an invalid command" do @app.stubs(:get_command).returns(:this_function_doesnt_exist) Puppet.expects(:err) expect { @app.run }.to exit_with 1 end end describe "when metaprogramming" do describe "when calling option" do it "should create a new method named after the option" do @app.class.option("--test1","-t") do end @app.should respond_to(:handle_test1) end it "should transpose in option name any '-' into '_'" do @app.class.option("--test-dashes-again","-t") do end @app.should respond_to(:handle_test_dashes_again) end it "should create a new method called handle_test2 with option(\"--[no-]test2\")" do @app.class.option("--[no-]test2","-t") do end @app.should respond_to(:handle_test2) end describe "when a block is passed" do it "should create a new method with it" do @app.class.option("--[no-]test2","-t") do raise "I can't believe it, it works!" end lambda { @app.handle_test2 }.should raise_error end it "should declare the option to OptionParser" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.expects(:on).with { |*arg| arg[0] == "--[no-]test3" } @app.class.option("--[no-]test3","-t") do end @app.parse_options end it "should pass a block that calls our defined method" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.stubs(:on).with('--test4','-t').yields(nil) @app.expects(:send).with(:handle_test4, nil) @app.class.option("--test4","-t") do end @app.parse_options end end describe "when no block is given" do it "should declare the option to OptionParser" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.expects(:on).with("--test4","-t") @app.class.option("--test4","-t") @app.parse_options end it "should give to OptionParser a block that adds the the value to the options array" do OptionParser.any_instance.stubs(:on) OptionParser.any_instance.stubs(:on).with("--test4","-t").yields(nil) @app.options.expects(:[]=).with(:test4,nil) @app.class.option("--test4","-t") @app.parse_options end end end end end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index 9dba85b31..0e0f86c09 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -1,218 +1,218 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/configurer/downloader' describe Puppet::Configurer::Downloader do require 'puppet_spec/files' include PuppetSpec::Files it "should require a name" do lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) end it "should require a path and a source at initialization" do lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) end it "should set the name, path and source appropriately" do dler = Puppet::Configurer::Downloader.new("facts", "path", "source") dler.name.should == "facts" dler.path.should == "path" dler.source.should == "source" end it "should be able to provide a timeout value" do Puppet::Configurer::Downloader.should respond_to(:timeout_interval) end it "should use the configtimeout, converted to an integer, as its timeout" do Puppet.settings.expects(:value).with(:configtimeout).returns "50" Puppet::Configurer::Downloader.timeout_interval.should == 50 end describe "when creating the file that does the downloading" do before do @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") end it "should create a file instance with the right path and source" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:path] == "path" and opts[:source] == "source" } @dler.file end it "should tag the file with the downloader name" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" } @dler.file end it "should always recurse" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true } @dler.file end it "should always purge" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true } @dler.file end it "should never be in noop" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false } @dler.file end describe "on POSIX" do before :each do Puppet.features.stubs(:microsoft_windows?).returns false end it "should always set the owner to the current UID" do Process.expects(:uid).returns 51 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } @dler.file end it "should always set the group to the current GID" do Process.expects(:gid).returns 61 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } @dler.file end end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should omit the owner" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == nil } @dler.file end it "should omit the group" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == nil } @dler.file end end it "should always force the download" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true } @dler.file end it "should never back up when downloading" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false } @dler.file end it "should support providing an 'ignore' parameter" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == [".svn"] } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn") @dler.file end it "should split the 'ignore' parameter on whitespace" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == %w{.svn CVS} } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn CVS") @dler.file end end describe "when creating the catalog to do the downloading" do before do @path = make_absolute("/download/path") @dler = Puppet::Configurer::Downloader.new("foo", @path, make_absolute("source")) end it "should create a catalog and add the file to it" do catalog = @dler.catalog catalog.resources.size.should == 1 catalog.resources.first.class.should == Puppet::Type::File catalog.resources.first.name.should == @path end it "should specify that it is not managing a host catalog" do @dler.catalog.host_config.should == false end end describe "when downloading" do before do @dl_name = tmpfile("downloadpath") source_name = tmpfile("source") File.open(source_name, 'w') {|f| f.write('hola mundo') } @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name) end it "should not skip downloaded resources when filtering on tags" do Puppet[:tags] = 'maytag' @dler.evaluate File.exists?(@dl_name).should be_true end it "should log that it is downloading" do Puppet.expects(:info) Timeout.stubs(:timeout) @dler.evaluate end it "should set a timeout for the download" do Puppet::Configurer::Downloader.expects(:timeout_interval).returns 50 Timeout.expects(:timeout).with(50) @dler.evaluate end it "should apply the catalog within the timeout block" do catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) Timeout.expects(:timeout).yields catalog.expects(:apply) @dler.evaluate end it "should return all changed file paths" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) @dler.evaluate.should == %w{/changed/file} end it "should yield the resources if a block is given" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) yielded = nil @dler.evaluate { |r| yielded = r } yielded.should == resource end it "should catch and log exceptions" do Puppet.expects(:err) Timeout.stubs(:timeout).raises(Puppet::Error, "testing") lambda { @dler.evaluate }.should_not raise_error end end end diff --git a/spec/unit/configurer/fact_handler_spec.rb b/spec/unit/configurer/fact_handler_spec.rb index 317a7a92f..b804c6fef 100755 --- a/spec/unit/configurer/fact_handler_spec.rb +++ b/spec/unit/configurer/fact_handler_spec.rb @@ -1,108 +1,108 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/configurer' require 'puppet/configurer/fact_handler' class FactHandlerTester include Puppet::Configurer::FactHandler end describe Puppet::Configurer::FactHandler do before do @facthandler = FactHandlerTester.new end describe "when finding facts" do before :each do @facthandler.stubs(:reload_facter) Puppet::Node::Facts.indirection.terminus_class = :memory end it "should use the node name value to retrieve the facts" do foo_facts = Puppet::Node::Facts.new('foo') bar_facts = Puppet::Node::Facts.new('bar') Puppet::Node::Facts.indirection.save(foo_facts) Puppet::Node::Facts.indirection.save(bar_facts) Puppet[:certname] = 'foo' Puppet[:node_name_value] = 'bar' @facthandler.find_facts.should == bar_facts end it "should set the facts name based on the node_name_fact" do facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name') Puppet::Node::Facts.indirection.save(facts) Puppet[:node_name_fact] = 'my_name_fact' @facthandler.find_facts.name.should == 'other_node_name' end it "should set the node_name_value based on the node_name_fact" do facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name') Puppet::Node::Facts.indirection.save(facts) Puppet[:node_name_fact] = 'my_name_fact' @facthandler.find_facts Puppet[:node_name_value].should == 'other_node_name' end it "should fail if finding facts fails" do Puppet[:trace] = false Puppet[:certname] = "myhost" Puppet::Node::Facts.indirection.expects(:find).raises RuntimeError lambda { @facthandler.find_facts }.should raise_error(Puppet::Error) end end it "should only load fact plugins once" do Puppet::Node::Facts.indirection.expects(:find).once @facthandler.find_facts end # I couldn't get marshal to work for this, only yaml, so we hard-code yaml. it "should serialize and CGI escape the fact values for uploading" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns true facts.expects(:render).returns "my text" text = CGI.escape("my text") @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} end it "should properly accept facts containing a '+'" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns true facts.expects(:render).returns "my+text" text = "my%2Btext" @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading.should == {:facts_format => :b64_zlib_yaml, :facts => text} end it "use compressed yaml as the serialization if zlib is supported" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns true facts.expects(:render).with(:b64_zlib_yaml).returns "my text" text = CGI.escape("my text") @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading end it "should use yaml as the serialization if zlib is not supported" do facts = stub 'facts' facts.expects(:support_format?).with(:b64_zlib_yaml).returns false facts.expects(:render).with(:yaml).returns "my text" text = CGI.escape("my text") @facthandler.expects(:find_facts).returns facts @facthandler.facts_for_uploading end end diff --git a/spec/unit/configurer/plugin_handler_spec.rb b/spec/unit/configurer/plugin_handler_spec.rb index e38e165cb..b207af16c 100755 --- a/spec/unit/configurer/plugin_handler_spec.rb +++ b/spec/unit/configurer/plugin_handler_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/configurer' require 'puppet/configurer/plugin_handler' class PluginHandlerTester include Puppet::Configurer::PluginHandler attr_accessor :environment end describe Puppet::Configurer::PluginHandler do before do @pluginhandler = PluginHandlerTester.new # PluginHandler#load_plugin has an extra-strong rescue clause # this mock is to make sure that we don't silently ignore errors Puppet.expects(:err).never end it "should have a method for downloading plugins" do @pluginhandler.should respond_to(:download_plugins) end it "should have a boolean method for determining whether plugins should be downloaded" do @pluginhandler.should respond_to(:download_plugins?) end it "should download plugins when :pluginsync is true" do Puppet.settings.expects(:value).with(:pluginsync).returns true @pluginhandler.should be_download_plugins end it "should not download plugins when :pluginsync is false" do Puppet.settings.expects(:value).with(:pluginsync).returns false @pluginhandler.should_not be_download_plugins end it "should not download plugins when downloading is disabled" do Puppet::Configurer::Downloader.expects(:new).never @pluginhandler.expects(:download_plugins?).returns false @pluginhandler.download_plugins end it "should use an Agent Downloader, with the name, source, destination, ignore, and environment set correctly, to download plugins when downloading is enabled" do downloader = mock 'downloader' # This is needed in order to make sure we pass on windows plugindest = File.expand_path("/tmp/pdest") Puppet[:pluginsource] = "psource" Puppet[:plugindest] = plugindest Puppet[:pluginsignore] = "pignore" Puppet::Configurer::Downloader.expects(:new).with("plugin", plugindest, "psource", "pignore", "myenv").returns downloader downloader.expects(:evaluate).returns [] @pluginhandler.environment = "myenv" @pluginhandler.expects(:download_plugins?).returns true @pluginhandler.download_plugins end end diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb index 570a500d0..942455036 100755 --- a/spec/unit/configurer_spec.rb +++ b/spec/unit/configurer_spec.rb @@ -1,610 +1,610 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/configurer' describe Puppet::Configurer do before do Puppet.settings.stubs(:use).returns(true) @agent = Puppet::Configurer.new @agent.stubs(:init_storage) Puppet::Util::Storage.stubs(:store) Puppet[:server] = "puppetmaster" Puppet[:report] = true end it "should include the Plugin Handler module" do Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::PluginHandler) end it "should include the Fact Handler module" do Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::FactHandler) end describe "when executing a pre-run hook" do it "should do nothing if the hook is set to an empty string" do Puppet.settings[:prerun_command] = "" Puppet::Util.expects(:exec).never @agent.execute_prerun_command end it "should execute any pre-run command provided via the 'prerun_command' setting" do Puppet.settings[:prerun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.execute_prerun_command end it "should fail if the command fails" do Puppet.settings[:prerun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.execute_prerun_command.should be_false end end describe "when executing a post-run hook" do it "should do nothing if the hook is set to an empty string" do Puppet.settings[:postrun_command] = "" Puppet::Util.expects(:exec).never @agent.execute_postrun_command end it "should execute any post-run command provided via the 'postrun_command' setting" do Puppet.settings[:postrun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.execute_postrun_command end it "should fail if the command fails" do Puppet.settings[:postrun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.execute_postrun_command.should be_false end end describe "when executing a catalog run" do before do Puppet.settings.stubs(:use).returns(true) @agent.stubs(:download_plugins) Puppet::Node::Facts.indirection.terminus_class = :memory @facts = Puppet::Node::Facts.new(Puppet[:node_name_value]) Puppet::Node::Facts.indirection.save(@facts) @catalog = Puppet::Resource::Catalog.new @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.indirection.terminus_class = :rest Puppet::Resource::Catalog.indirection.stubs(:find).returns(@catalog) @agent.stubs(:send_report) @agent.stubs(:save_last_run_summary) Puppet::Util::Log.stubs(:close_all) end after :all do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Resource::Catalog.indirection.reset_terminus_class end it "should initialize storage" do Puppet::Util::Storage.expects(:load) @agent.run end it "should download plugins" do @agent.expects(:download_plugins) @agent.run end it "should initialize a transaction report if one is not provided" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns report @agent.run end it "should respect node_name_fact when setting the host on a report" do Puppet[:node_name_fact] = 'my_name_fact' @facts.values = {'my_name_fact' => 'node_name_from_fact'} report = Puppet::Transaction::Report.new("apply") @agent.run(:report => report) report.host.should == 'node_name_from_fact' end it "should pass the new report to the catalog" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.stubs(:new).returns report @catalog.expects(:apply).with{|options| options[:report] == report} @agent.run end it "should use the provided report if it was passed one" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).never @catalog.expects(:apply).with{|options| options[:report] == report} @agent.run(:report => report) end it "should set the report as a log destination" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns report Puppet::Util::Log.expects(:newdestination).with(report) Puppet::Util::Log.expects(:close).with(report) @agent.run end it "should retrieve the catalog" do @agent.expects(:retrieve_catalog) @agent.run end it "should log a failure and do nothing if no catalog can be retrieved" do @agent.expects(:retrieve_catalog).returns nil Puppet.expects(:err).with "Could not retrieve catalog; skipping run" @agent.run end it "should apply the catalog with all options to :run" do @agent.expects(:retrieve_catalog).returns @catalog @catalog.expects(:apply).with { |args| args[:one] == true } @agent.run :one => true end it "should accept a catalog and use it instead of retrieving a different one" do @agent.expects(:retrieve_catalog).never @catalog.expects(:apply) @agent.run :one => true, :catalog => @catalog end it "should benchmark how long it takes to apply the catalog" do @agent.expects(:benchmark).with(:notice, "Finished catalog run") @agent.expects(:retrieve_catalog).returns @catalog @catalog.expects(:apply).never # because we're not yielding @agent.run end it "should execute post-run hooks after the run" do @agent.expects(:execute_postrun_command) @agent.run end it "should send the report" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report).with(report) @agent.run end it "should send the transaction report even if the catalog could not be retrieved" do @agent.expects(:retrieve_catalog).returns nil report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report) @agent.run end it "should send the transaction report even if there is a failure" do @agent.expects(:retrieve_catalog).raises "whatever" report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.expects(:send_report) @agent.run.should be_nil end it "should remove the report as a log destination when the run is finished" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) @agent.run Puppet::Util::Log.destinations.should_not include(report) end it "should return the report exit_status as the result of the run" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) report.expects(:exit_status).returns(1234) @agent.run.should == 1234 end it "should send the transaction report even if the pre-run command fails" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) Puppet.settings[:prerun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.expects(:send_report) @agent.run.should be_nil end it "should include the pre-run command failure in the report" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) Puppet.settings[:prerun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.run.should be_nil report.logs.find { |x| x.message =~ /Could not run command from prerun_command/ }.should be end it "should send the transaction report even if the post-run command fails" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) Puppet.settings[:postrun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @agent.expects(:send_report) @agent.run.should be_nil end it "should include the post-run command failure in the report" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) Puppet.settings[:postrun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") report.expects(:<<).with { |log| log.message.include?("Could not run command from postrun_command") } @agent.run.should be_nil end it "should execute post-run command even if the pre-run command fails" do Puppet.settings[:prerun_command] = "/my/precommand" Puppet.settings[:postrun_command] = "/my/postcommand" Puppet::Util::Execution.expects(:execute).with(["/my/precommand"]).raises(Puppet::ExecutionFailure, "Failed") Puppet::Util::Execution.expects(:execute).with(["/my/postcommand"]) @agent.run.should be_nil end it "should finalize the report" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) report.expects(:finalize_report) @agent.run end it "should not apply the catalog if the pre-run command fails" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) Puppet.settings[:prerun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @catalog.expects(:apply).never() @agent.expects(:send_report) @agent.run.should be_nil end it "should apply the catalog, send the report, and return nil if the post-run command fails" do report = Puppet::Transaction::Report.new("apply") Puppet::Transaction::Report.expects(:new).returns(report) Puppet.settings[:postrun_command] = "/my/command" Puppet::Util::Execution.expects(:execute).with(["/my/command"]).raises(Puppet::ExecutionFailure, "Failed") @catalog.expects(:apply) @agent.expects(:send_report) @agent.run.should be_nil end it "should refetch the catalog if the server specifies a new environment in the catalog" do @catalog.stubs(:environment).returns("second_env") @agent.expects(:retrieve_catalog).returns(@catalog).twice @agent.run end it "should change the environment setting if the server specifies a new environment in the catalog" do @catalog.stubs(:environment).returns("second_env") @agent.run @agent.environment.should == "second_env" end describe "when not using a REST terminus for catalogs" do it "should not pass any facts when retrieving the catalog" do Puppet::Resource::Catalog.indirection.terminus_class = :compiler @agent.expects(:facts_for_uploading).never Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:facts].nil? }.returns @catalog @agent.run end end describe "when using a REST terminus for catalogs" do it "should pass the prepared facts and the facts format as arguments when retrieving the catalog" do Puppet::Resource::Catalog.indirection.terminus_class = :rest @agent.expects(:facts_for_uploading).returns(:facts => "myfacts", :facts_format => :foo) Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:facts] == "myfacts" and options[:facts_format] == :foo }.returns @catalog @agent.run end end end describe "when sending a report" do include PuppetSpec::Files before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new Puppet[:lastrunfile] = tmpfile('last_run_file') @report = Puppet::Transaction::Report.new("apply") end it "should print a report summary if configured to do so" do Puppet.settings[:summarize] = true @report.expects(:summary).returns "stuff" @configurer.expects(:puts).with("stuff") @configurer.send_report(@report) end it "should not print a report summary if not configured to do so" do Puppet.settings[:summarize] = false @configurer.expects(:puts).never @configurer.send_report(@report) end it "should save the report if reporting is enabled" do Puppet.settings[:report] = true Puppet::Transaction::Report.indirection.expects(:save).with(@report, nil, instance_of(Hash)) @configurer.send_report(@report) end it "should not save the report if reporting is disabled" do Puppet.settings[:report] = false Puppet::Transaction::Report.indirection.expects(:save).with(@report, nil, instance_of(Hash)).never @configurer.send_report(@report) end it "should save the last run summary if reporting is enabled" do Puppet.settings[:report] = true @configurer.expects(:save_last_run_summary).with(@report) @configurer.send_report(@report) end it "should save the last run summary if reporting is disabled" do Puppet.settings[:report] = false @configurer.expects(:save_last_run_summary).with(@report) @configurer.send_report(@report) end it "should log but not fail if saving the report fails" do Puppet.settings[:report] = true Puppet::Transaction::Report.indirection.expects(:save).raises("whatever") Puppet.expects(:err) lambda { @configurer.send_report(@report) }.should_not raise_error end end describe "when saving the summary report file" do include PuppetSpec::Files before do Puppet.settings.stubs(:use).returns(true) @configurer = Puppet::Configurer.new @report = stub 'report', :raw_summary => {} Puppet[:lastrunfile] = tmpfile('last_run_file') end it "should write the last run file" do @configurer.save_last_run_summary(@report) FileTest.exists?(Puppet[:lastrunfile]).should be_true end it "should write the raw summary as yaml" do @report.expects(:raw_summary).returns("summary") @configurer.save_last_run_summary(@report) File.read(Puppet[:lastrunfile]).should == YAML.dump("summary") end it "should log but not fail if saving the last run summary fails" do # The mock will raise an exception on any method used. This should # simulate a nice hard failure from the underlying OS for us. fh = Class.new(Object) do def method_missing(*args) raise "failed to do #{args[0]}" end end.new Puppet::Util.expects(:replace_file).yields(fh) Puppet.expects(:err) expect { @configurer.save_last_run_summary(@report) }.should_not raise_error end end describe "when retrieving a catalog" do before do Puppet.settings.stubs(:use).returns(true) @agent.stubs(:facts_for_uploading).returns({}) @catalog = Puppet::Resource::Catalog.new # this is the default when using a Configurer instance Puppet::Resource::Catalog.indirection.stubs(:terminus_class).returns :rest @agent.stubs(:convert_catalog).returns @catalog end describe "and configured to only retrieve a catalog from the cache" do before do Puppet.settings[:use_cached_catalog] = true end it "should first look in the cache for a catalog" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.never @agent.retrieve_catalog({}).should == @catalog end it "should compile a new catalog if none is found in the cache" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog @agent.retrieve_catalog({}).should == @catalog end end it "should use the Catalog class to get its catalog" do Puppet::Resource::Catalog.indirection.expects(:find).returns @catalog @agent.retrieve_catalog({}) end it "should use its node_name_value to retrieve the catalog" do Facter.stubs(:value).returns "eh" Puppet.settings[:node_name_value] = "myhost.domain.com" Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| name == "myhost.domain.com" }.returns @catalog @agent.retrieve_catalog({}) end it "should default to returning a catalog retrieved directly from the server, skipping the cache" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog @agent.retrieve_catalog({}).should == @catalog end it "should log and return the cached catalog when no catalog can be retrieved from the server" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog Puppet.expects(:notice) @agent.retrieve_catalog({}).should == @catalog end it "should not look in the cache for a catalog if one is returned from the server" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns @catalog Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.never @agent.retrieve_catalog({}).should == @catalog end it "should return the cached catalog when retrieving the remote catalog throws an exception" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.raises "eh" Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns @catalog @agent.retrieve_catalog({}).should == @catalog end it "should log and return nil if no catalog can be retrieved from the server and :usecacheonfailure is disabled" do Puppet.stubs(:[]) Puppet.expects(:[]).with(:usecacheonfailure).returns false Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet.expects(:warning) @agent.retrieve_catalog({}).should be_nil end it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_cache] == true }.returns nil Puppet::Resource::Catalog.indirection.expects(:find).with { |name, options| options[:ignore_terminus] == true }.returns nil @agent.retrieve_catalog({}).should be_nil end it "should convert the catalog before returning" do Puppet::Resource::Catalog.indirection.stubs(:find).returns @catalog @agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog" @agent.retrieve_catalog({}).should == "converted catalog" end it "should return nil if there is an error while retrieving the catalog" do Puppet::Resource::Catalog.indirection.expects(:find).at_least_once.raises "eh" @agent.retrieve_catalog({}).should be_nil end end describe "when converting the catalog" do before do Puppet.settings.stubs(:use).returns(true) @catalog = Puppet::Resource::Catalog.new @oldcatalog = stub 'old_catalog', :to_ral => @catalog end it "should convert the catalog to a RAL-formed catalog" do @oldcatalog.expects(:to_ral).returns @catalog @agent.convert_catalog(@oldcatalog, 10).should equal(@catalog) end it "should finalize the catalog" do @catalog.expects(:finalize) @agent.convert_catalog(@oldcatalog, 10) end it "should record the passed retrieval time with the RAL catalog" do @catalog.expects(:retrieval_duration=).with 10 @agent.convert_catalog(@oldcatalog, 10) end it "should write the RAL catalog's class file" do @catalog.expects(:write_class_file) @agent.convert_catalog(@oldcatalog, 10) end it "should write the RAL catalog's resource file" do @catalog.expects(:write_resource_file) @agent.convert_catalog(@oldcatalog, 10) end end end diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb index 207db3df3..90ccd3efe 100755 --- a/spec/unit/daemon_spec.rb +++ b/spec/unit/daemon_spec.rb @@ -1,268 +1,268 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/daemon' require 'puppet/agent' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end class TestClient def lockfile_path "/dev/null" end end describe Puppet::Daemon, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before do @agent = Puppet::Agent.new(TestClient.new) @daemon = Puppet::Daemon.new @daemon.stubs(:close_streams).returns nil end it "should be able to manage an agent" do @daemon.should respond_to(:agent) end it "should be able to manage a network server" do @daemon.should respond_to(:server) end it "should reopen the Log logs when told to reopen logs" do Puppet::Util::Log.expects(:reopen) @daemon.reopen_logs end describe "when setting signal traps" do signals = {:INT => :stop, :TERM => :stop } signals.update({:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}) unless Puppet.features.microsoft_windows? signals.each do |signal, method| it "should log and call #{method} when it receives #{signal}" do Signal.expects(:trap).with(signal).yields Puppet.expects(:notice) @daemon.expects(method) @daemon.set_signal_traps end end end describe "when starting" do before do @daemon.stubs(:create_pidfile) @daemon.stubs(:set_signal_traps) @daemon.stubs(:run_event_loop) end it "should fail if it has neither agent nor server" do lambda { @daemon.start }.should raise_error(Puppet::DevError) end it "should create its pidfile" do @daemon.agent = @agent @daemon.expects(:create_pidfile) @daemon.start end it "should start its server if one is configured" do server = mock 'server' server.expects(:start) @daemon.stubs(:server).returns server @daemon.start end end describe "when stopping" do before do @daemon.stubs(:remove_pidfile) Puppet::Util::Log.stubs(:close_all) # to make the global safe to mock, set it to a subclass of itself, # then restore it in an after pass without_warnings { Puppet::Application = Class.new(Puppet::Application) } end after do # restore from the superclass so we lose the stub garbage without_warnings { Puppet::Application = Puppet::Application.superclass } end it "should stop its server if one is configured" do server = mock 'server' server.expects(:stop) @daemon.stubs(:server).returns server expect { @daemon.stop }.to exit_with 0 end it 'should request a stop from Puppet::Application' do Puppet::Application.expects(:stop!) expect { @daemon.stop }.to exit_with 0 end it "should remove its pidfile" do @daemon.expects(:remove_pidfile) expect { @daemon.stop }.to exit_with 0 end it "should close all logs" do Puppet::Util::Log.expects(:close_all) expect { @daemon.stop }.to exit_with 0 end it "should exit unless called with ':exit => false'" do expect { @daemon.stop }.to exit_with 0 end it "should not exit if called with ':exit => false'" do @daemon.stop :exit => false end end describe "when creating its pidfile" do it "should use an exclusive mutex" do Puppet.run_mode.expects(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @daemon.create_pidfile end it "should lock the pidfile using the Pidlock class" do pidfile = mock 'pidfile' Puppet.run_mode.expects(:name).returns "eh" Puppet.settings.expects(:value).with(:pidfile).returns make_absolute("/my/file") Puppet::Util::Pidlock.expects(:new).with(make_absolute("/my/file")).returns pidfile pidfile.expects(:lock).returns true @daemon.create_pidfile end it "should fail if it cannot lock" do pidfile = mock 'pidfile' Puppet.run_mode.expects(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns make_absolute("/my/file") Puppet::Util::Pidlock.expects(:new).with(make_absolute("/my/file")).returns pidfile pidfile.expects(:lock).returns false lambda { @daemon.create_pidfile }.should raise_error end end describe "when removing its pidfile" do it "should use an exclusive mutex" do Puppet.run_mode.expects(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @daemon.remove_pidfile end it "should do nothing if the pidfile is not present" do pidfile = mock 'pidfile', :unlock => false Puppet[:pidfile] = make_absolute("/my/file") Puppet::Util::Pidlock.expects(:new).with(make_absolute("/my/file")).returns pidfile @daemon.remove_pidfile end it "should unlock the pidfile using the Pidlock class" do pidfile = mock 'pidfile', :unlock => true Puppet[:pidfile] = make_absolute("/my/file") Puppet::Util::Pidlock.expects(:new).with(make_absolute("/my/file")).returns pidfile @daemon.remove_pidfile end end describe "when reloading" do it "should do nothing if no agent is configured" do @daemon.reload end it "should do nothing if the agent is running" do @agent.expects(:running?).returns true @daemon.agent = @agent @daemon.reload end it "should run the agent if one is available and it is not running" do @agent.expects(:running?).returns false @agent.expects :run @daemon.agent = @agent @daemon.reload end end describe "when restarting" do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should set Puppet::Application.restart!' do Puppet::Application.expects(:restart!) @daemon.stubs(:reexec) @daemon.restart end it "should reexec itself if no agent is available" do @daemon.expects(:reexec) @daemon.restart end it "should reexec itself if the agent is not running" do @agent.expects(:running?).returns false @daemon.agent = @agent @daemon.expects(:reexec) @daemon.restart end end describe "when reexecing it self" do before do @daemon.stubs(:exec) @daemon.stubs(:stop) end it "should fail if no argv values are available" do @daemon.expects(:argv).returns nil lambda { @daemon.reexec }.should raise_error(Puppet::DevError) end it "should shut down without exiting" do @daemon.argv = %w{foo} @daemon.expects(:stop).with(:exit => false) @daemon.reexec end it "should call 'exec' with the original executable and arguments" do @daemon.argv = %w{foo} @daemon.expects(:exec).with($0 + " foo") @daemon.reexec end end end diff --git a/spec/unit/dsl/resource_api_spec.rb b/spec/unit/dsl/resource_api_spec.rb index 559a43333..56e560f2f 100755 --- a/spec/unit/dsl/resource_api_spec.rb +++ b/spec/unit/dsl/resource_api_spec.rb @@ -1,180 +1,180 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/dsl/resource_api' describe Puppet::DSL::ResourceAPI do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => "foo") @resource = Puppet::Parser::Resource.new(:mytype, "myresource", :scope => @scope) @api = Puppet::DSL::ResourceAPI.new(@resource, @scope, proc { }) end it "should include the resource type collection helper" do Puppet::DSL::ResourceAPI.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should use the scope's environment as its environment" do @scope.expects(:environment).returns "myenv" @api.environment.should == "myenv" end it "should be able to set all of its parameters as instance variables" do @resource["foo"] = "myval" @api.set_instance_variables @api.instance_variable_get("@foo").should == "myval" end describe "when calling a function" do it "should return false if the function does not exist" do Puppet::Parser::Functions.expects(:function).with("myfunc").returns nil @api.call_function("myfunc", "foo").should be_false end it "should use the scope the call the provided function with the provided arguments and return the results" do scope = stub 'scope' @api.stubs(:scope).returns scope Puppet::Parser::Functions.expects(:function).with("myfunc").returns "myfunc_method" scope.expects(:myfunc_method).with("one", "two") @api.call_function("myfunc", ["one", "two"]) end it "should call 'include' when asked to call 'acquire'" do scope = stub 'scope' @api.stubs(:scope).returns scope @api.stubs(:valid_type?).returns false scope.expects(:function_include).with("one", "two") @api.acquire("one", "two") end end describe "when determining if a provided name is a valid type" do it "should be valid if it's :class" do @api.should be_valid_type(:class) end it "should be valid if it's :node" do @api.should be_valid_type(:node) end it "should be valid if it's a builtin type" do Puppet::Type.expects(:type).with(:mytype).returns "whatever" @api.should be_valid_type(:mytype) end it "should be valid if it's a defined resource type in the environment's known resource types" do collection = stub 'collection' @api.stubs(:known_resource_types).returns collection collection.expects(:definition).with(:mytype).returns "whatever" @api.should be_valid_type(:mytype) end it "should not be valid unless it's a node, class, builtin type, or defined resource" do collection = stub 'collection' @api.stubs(:known_resource_types).returns collection collection.expects(:definition).returns nil Puppet::Type.expects(:type).returns nil @api.should_not be_valid_type(:mytype) end end describe "when creating a resource" do before do @api.scope.stubs(:source).returns stub("source") @api.scope.compiler.stubs(:add_resource) @created_resource = Puppet::Parser::Resource.new("yay", "eh", :scope => @api.scope) end it "should create and return a resource of the type specified" do Puppet::Parser::Resource.expects(:new).with { |type, title, args| type == "mytype" }.returns @created_resource @api.create_resource("mytype", "myname", {:foo => "bar"}).should == [@created_resource] end it "should use the name from the first element of the provided argument array" do Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "myname" }.returns @created_resource @api.create_resource("mytype", "myname", {:foo => "bar"}) end it "should create multiple resources if the first element of the argument array is an array" do second_resource = Puppet::Parser::Resource.new('yay', "eh", :scope => @api.scope) Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "first" }.returns @created_resource Puppet::Parser::Resource.expects(:new).with { |type, title, args| title == "second" }.returns @created_resource @api.create_resource("mytype", ["first", "second"], {:foo => "bar"}) end it "should provide its scope as the scope" do Puppet::Parser::Resource.expects(:new).with { |type, title, args| args[:scope] == @api.scope }.returns @created_resource @api.create_resource("mytype", "myname", {:foo => "bar"}) end it "should set each provided argument as a parameter on the created resource" do result = @api.create_resource("mytype", "myname", {"foo" => "bar", "biz" => "baz"}).shift result["foo"].should == "bar" result["biz"].should == "baz" end it "should add the resource to the scope's copmiler" do Puppet::Parser::Resource.expects(:new).returns @created_resource @api.scope.compiler.expects(:add_resource).with(@api.scope, @created_resource) @api.create_resource("mytype", "myname", {:foo => "bar"}) end it "should fail if the resource parameters are not a hash" do lambda { @api.create_resource("mytype", "myname", %w{foo bar}) }.should raise_error(ArgumentError) end end describe "when an unknown method is called" do it "should create a resource if the method name is a valid type" do @api.expects(:valid_type?).with(:mytype).returns true @api.expects(:create_resource).with(:mytype, "myname", {:foo => "bar"}).returns true @api.mytype("myname", :foo => "bar") end it "should call any function whose name matches the undefined method if the name is not a valid type" do @api.expects(:valid_type?).with(:myfunc).returns false @api.expects(:create_resource).never Puppet::Parser::Functions.expects(:function).with(:myfunc).returns true @api.expects(:call_function).with(:myfunc, %w{foo bar}) @api.myfunc("foo", "bar") end it "should raise a method missing error if the method is neither a type nor a function" do @api.expects(:valid_type?).with(:myfunc).returns false @api.expects(:create_resource).never Puppet::Parser::Functions.expects(:function).with(:myfunc).returns false @api.expects(:call_function).never lambda { @api.myfunc("foo", "bar") }.should raise_error(NoMethodError) end end it "should mark the specified resource as exported when creating a single exported resource" do resources = @api.export @api.file("/my/file", :ensure => :present) resources[0].should be_exported end it "should mark all created resources as exported when creating exported resources using a block" do @compiler.expects(:add_resource).with { |s, res| res.exported == true } @api.export { file "/my/file", :ensure => :present } end it "should mark the specified resource as virtual when creating a single virtual resource" do resources = @api.virtual @api.file("/my/file", :ensure => :present) resources[0].should be_virtual end it "should mark all created resources as virtual when creating virtual resources using a block" do @compiler.expects(:add_resource).with { |s, res| res.virtual == true } @api.virtual { file "/my/file", :ensure => :present } end end diff --git a/spec/unit/dsl/resource_type_api_spec.rb b/spec/unit/dsl/resource_type_api_spec.rb index ea81f7da4..58fd216c4 100755 --- a/spec/unit/dsl/resource_type_api_spec.rb +++ b/spec/unit/dsl/resource_type_api_spec.rb @@ -1,53 +1,53 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/dsl/resource_type_api' describe Puppet::DSL::ResourceTypeAPI do # Verify that the block creates a single AST node through the API, # instantiate that AST node into a types, and return that type. def test_api_call(&block) main_object = Puppet::DSL::ResourceTypeAPI.new main_object.instance_eval(&block) created_ast_objects = main_object.instance_eval { @__created_ast_objects__ } created_ast_objects.length.should == 1 new_types = created_ast_objects[0].instantiate('') new_types.length.should == 1 new_types[0] ensure Thread.current[:ruby_file_parse_result] = nil end [:definition, :node, :hostclass].each do |type| method = type == :definition ? "define" : type it "should be able to create a #{type}" do newtype = test_api_call { send(method, "myname").should == nil } newtype.should be_a(Puppet::Resource::Type) newtype.type.should == type end it "should use the provided name when creating a #{type}" do newtype = test_api_call { send(method, "myname") } newtype.name.should == "myname" end unless type == :definition it "should pass in any provided options when creating a #{type}" do newtype = test_api_call { send(method, "myname", :line => 200) } newtype.line.should == 200 end end it "should set any provided block as the type's ruby code" do newtype = test_api_call { send(method, "myname") { 'method_result' } } newtype.ruby_code.call.should == 'method_result' end end describe "when creating a definition" do it "should use the provided options to define valid arguments for the resource type" do newtype = test_api_call { define("myname", :arg1, :arg2) } newtype.arguments.should == { 'arg1' => nil, 'arg2' => nil } end end end diff --git a/spec/unit/face/catalog_spec.rb b/spec/unit/face/catalog_spec.rb index c77a9d153..dae5a7bec 100755 --- a/spec/unit/face/catalog_spec.rb +++ b/spec/unit/face/catalog_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:catalog, '0.0.1'] do it "should actually have some testing..." end diff --git a/spec/unit/face/certificate_request_spec.rb b/spec/unit/face/certificate_request_spec.rb index e237800ff..b395f75b8 100755 --- a/spec/unit/face/certificate_request_spec.rb +++ b/spec/unit/face/certificate_request_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:certificate_request, '0.0.1'] do it "should actually have some tests..." end diff --git a/spec/unit/face/certificate_revocation_list_spec.rb b/spec/unit/face/certificate_revocation_list_spec.rb index 1033df7ff..bb91d5d6e 100755 --- a/spec/unit/face/certificate_revocation_list_spec.rb +++ b/spec/unit/face/certificate_revocation_list_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:certificate_revocation_list, '0.0.1'] do it "should actually have some tests..." end diff --git a/spec/unit/face/certificate_spec.rb b/spec/unit/face/certificate_spec.rb index e8c0d3cfb..04df1d10e 100755 --- a/spec/unit/face/certificate_spec.rb +++ b/spec/unit/face/certificate_spec.rb @@ -1,223 +1,223 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' require 'puppet/ssl/host' describe Puppet::Face[:certificate, '0.0.1'] do include PuppetSpec::Files let(:ca) { Puppet::SSL::CertificateAuthority.instance } before :each do Puppet[:confdir] = tmpdir('conf') Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true Puppet::SSL::Host.ca_location = :local # We can't cache the CA between tests, because each one has its own SSL dir. ca = Puppet::SSL::CertificateAuthority.new Puppet::SSL::CertificateAuthority.stubs(:new).returns ca Puppet::SSL::CertificateAuthority.stubs(:instance).returns ca end it "should have a ca-location option" do subject.should be_option :ca_location end it "should set the ca location when invoked" do Puppet::SSL::Host.expects(:ca_location=).with(:local) ca.expects(:sign).with do |name,options| name == "hello, friend" end subject.sign "hello, friend", :ca_location => :local end it "(#7059) should set the ca location when an inherited action is invoked" do Puppet::SSL::Host.expects(:ca_location=).with(:local) subject.indirection.expects(:find) subject.find "hello, friend", :ca_location => :local end it "should validate the option as required" do expect do subject.find 'hello, friend' end.to raise_exception ArgumentError, /required/i end it "should validate the option as a supported value" do expect do subject.find 'hello, friend', :ca_location => :foo end.to raise_exception ArgumentError, /valid values/i end describe "#generate" do let(:options) { {:ca_location => 'local'} } let(:host) { Puppet::SSL::Host.new(hostname) } let(:csr) { host.certificate_request } before :each do Puppet[:autosign] = false end describe "for the current host" do let(:hostname) { Puppet[:certname] } it "should generate a CSR for this host" do subject.generate(hostname, options) csr.content.subject.to_s.should == "/CN=#{Puppet[:certname]}" csr.name.should == Puppet[:certname] end it "should add dns_alt_names from the global config if not otherwise specified" do Puppet[:dns_alt_names] = 'from,the,config' subject.generate(hostname, options) expected = %W[DNS:from DNS:the DNS:config DNS:#{hostname}] csr.subject_alt_names.should =~ expected end it "should add the provided dns_alt_names if they are specified" do Puppet[:dns_alt_names] = 'from,the,config' subject.generate(hostname, options.merge(:dns_alt_names => 'explicit,alt,names')) expected = %W[DNS:explicit DNS:alt DNS:names DNS:#{hostname}] csr.subject_alt_names.should =~ expected end end describe "for another host" do let(:hostname) { Puppet[:certname] + 'different' } it "should generate a CSR for the specified host" do subject.generate(hostname, options) csr.content.subject.to_s.should == "/CN=#{hostname}" csr.name.should == hostname end it "should fail if a CSR already exists for the host" do subject.generate(hostname, options) expect do subject.generate(hostname, options) end.to raise_error(RuntimeError, /#{hostname} already has a requested certificate; ignoring certificate request/) end it "should add not dns_alt_names from the config file" do Puppet[:dns_alt_names] = 'from,the,config' subject.generate(hostname, options) csr.subject_alt_names.should be_empty end it "should add the provided dns_alt_names if they are specified" do Puppet[:dns_alt_names] = 'from,the,config' subject.generate(hostname, options.merge(:dns_alt_names => 'explicit,alt,names')) expected = %W[DNS:explicit DNS:alt DNS:names DNS:#{hostname}] csr.subject_alt_names.should =~ expected end it "should use the global setting if set by CLI" do Puppet.settings.set_value(:dns_alt_names, 'from,the,cli', :cli) subject.generate(hostname, options) expected = %W[DNS:from DNS:the DNS:cli DNS:#{hostname}] csr.subject_alt_names.should =~ expected end it "should generate an error if both set on CLI" do Puppet.settings.set_value(:dns_alt_names, 'from,the,cli', :cli) expect do subject.generate(hostname, options.merge(:dns_alt_names => 'explicit,alt,names')) end.to raise_error ArgumentError, /Can't specify both/ end end end describe "#sign" do let(:options) { {:ca_location => 'local'} } let(:host) { Puppet::SSL::Host.new(hostname) } let(:hostname) { "foobar" } it "should sign the certificate request if one is waiting", :unless => Puppet.features.microsoft_windows? do subject.generate(hostname, options) subject.sign(hostname, options) host.certificate_request.should be_nil host.certificate.should be_a(Puppet::SSL::Certificate) host.state.should == 'signed' end it "should fail if there is no waiting certificate request" do expect do subject.sign(hostname, options) end.to raise_error(ArgumentError, /Could not find certificate request for #{hostname}/) end describe "when ca_location is local", :unless => Puppet.features.microsoft_windows? do describe "when the request has dns alt names" do before :each do subject.generate(hostname, options.merge(:dns_alt_names => 'some,alt,names')) end it "should refuse to sign the request if allow_dns_alt_names is not set" do expect do subject.sign(hostname, options) end.to raise_error(Puppet::SSL::CertificateAuthority::CertificateSigningError, /CSR '#{hostname}' contains subject alternative names \(.*?\), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{hostname}` to sign this request./i) host.state.should == 'requested' end it "should sign the request if allow_dns_alt_names is set" do expect do subject.sign(hostname, options.merge(:allow_dns_alt_names => true)) end.not_to raise_error host.state.should == 'signed' end end describe "when the request has no dns alt names" do before :each do subject.generate(hostname, options) end it "should sign the request if allow_dns_alt_names is set" do expect { subject.sign(hostname, options.merge(:allow_dns_alt_names => true)) }.not_to raise_error host.state.should == 'signed' end it "should sign the request if allow_dns_alt_names is not set" do expect { subject.sign(hostname, options) }.not_to raise_error host.state.should == 'signed' end end end describe "when ca_location is remote" do let(:options) { {:ca_location => :remote} } it "should fail if allow-dns-alt-names is specified" do expect do subject.sign(hostname, options.merge(:allow_dns_alt_names => true)) end end end end end diff --git a/spec/unit/face/config_spec.rb b/spec/unit/face/config_spec.rb index d86812113..28fc00f6d 100755 --- a/spec/unit/face/config_spec.rb +++ b/spec/unit/face/config_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:config, '0.0.1'] do it "should use Settings#print_config_options when asked to print" do Puppet.settings.stubs(:puts) Puppet.settings.expects(:print_config_options) subject.print end it "should set 'configprint' to all desired values and call print_config_options when a specific value is provided" do Puppet.settings.stubs(:puts) Puppet.settings.expects(:print_config_options) subject.print("libdir", "ssldir") Puppet.settings[:configprint].should == "libdir,ssldir" end it "should always return nil" do Puppet.settings.stubs(:puts) Puppet.settings.expects(:print_config_options) subject.print("libdir").should be_nil end it "should default to all when no arguments are given" do Puppet.settings.stubs(:puts) Puppet.settings.expects(:print_config_options) subject.print Puppet.settings[:configprint].should == "all" end end diff --git a/spec/unit/face/facts_spec.rb b/spec/unit/face/facts_spec.rb index 27b5b9e3d..6dc06b803 100755 --- a/spec/unit/face/facts_spec.rb +++ b/spec/unit/face/facts_spec.rb @@ -1,23 +1,23 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:facts, '0.0.1'] do it "should define an 'upload' action" do subject.should be_action(:upload) end describe "when uploading" do it "should set the terminus_class to :facter" it "should set the cache_class to :rest" it "should find the current certname" end describe "#find" do it { should be_action :find } it "should fail without a key" do expect { subject.find }.to raise_error ArgumentError, /wrong number of arguments/ end end end diff --git a/spec/unit/face/file_spec.rb b/spec/unit/face/file_spec.rb index c3f05720f..6a6dd4d13 100755 --- a/spec/unit/face/file_spec.rb +++ b/spec/unit/face/file_spec.rb @@ -1,12 +1,12 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:file, '0.0.1'] do it_should_behave_like "an indirector face" [:download, :store].each do |action| it { should be_action action } it { should respond_to action } end end diff --git a/spec/unit/face/help_spec.rb b/spec/unit/face/help_spec.rb index 2c6582d72..06b138a73 100755 --- a/spec/unit/face/help_spec.rb +++ b/spec/unit/face/help_spec.rb @@ -1,145 +1,145 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:help, '0.0.1'] do it "should have a help action" do subject.should be_action :help end it "should have a default action of help" do subject.get_action('help').should be_default end it "should accept a call with no arguments" do expect { subject.help() }.should_not raise_error end it "should accept a face name" do expect { subject.help(:help) }.should_not raise_error end it "should accept a face and action name" do expect { subject.help(:help, :help) }.should_not raise_error end it "should fail if more than a face and action are given" do expect { subject.help(:help, :help, :for_the_love_of_god) }. should raise_error ArgumentError end it "should treat :current and 'current' identically" do subject.help(:help, :version => :current).should == subject.help(:help, :version => 'current') end it "should complain when the request version of a face is missing" do expect { subject.help(:huzzah, :bar, :version => '17.0.0') }. should raise_error Puppet::Error end it "should find a face by version" do face = Puppet::Face[:huzzah, :current] subject.help(:huzzah, :version => face.version). should == subject.help(:huzzah, :version => :current) end context "when listing subcommands" do subject { Puppet::Face[:help, :current].help } RSpec::Matchers.define :have_a_summary do match do |instance| instance.summary.is_a?(String) end end # Check a precondition for the next block; if this fails you have # something odd in your set of face, and we skip testing things that # matter. --daniel 2011-04-10 it "should have at least one face with a summary" do Puppet::Face.faces.should be_any do |name| Puppet::Face[name, :current].summary end end it "should list all faces which are runnable from the command line" do help_face = Puppet::Face[:help, :current] # The main purpose of the help face is to provide documentation for # command line users. It shouldn't show documentation for faces # that can't be run from the command line, so, rather than iterating # over all available faces, we need to iterate over the subcommands # that are available from the command line. Puppet::Util::CommandLine.available_subcommands.each do |name| next unless help_face.is_face_app?(name) next if help_face.exclude_from_docs?(name) face = Puppet::Face[name, :current] summary = face.summary subject.should =~ %r{ #{name} } summary and subject.should =~ %r{ #{name} +#{summary}} end end context "face summaries" do # we need to set a bunk module path here, because without doing so, # the autoloader will try to use it before it is initialized. Puppet[:modulepath] = "/dev/null" Puppet::Face.faces.each do |name| it "should have a summary for #{name}" do Puppet::Face[name, :current].should have_a_summary end end end it "should list all legacy applications" do Puppet::Face[:help, :current].legacy_applications.each do |appname| subject.should =~ %r{ #{appname} } summary = Puppet::Face[:help, :current].horribly_extract_summary_from(appname) summary and subject.should =~ %r{ #{summary}\b} end end end context "#legacy_applications" do subject { Puppet::Face[:help, :current].legacy_applications } # If we don't, these tests are ... less than useful, because they assume # it. When this breaks you should consider ditching the entire feature # and tests, but if not work out how to fake one. --daniel 2011-04-11 it { should have_at_least(1).item } # Meh. This is nasty, but we can't control the other list; the specific # bug that caused these to be listed is annoyingly subtle and has a nasty # fix, so better to have a "fail if you do something daft" trigger in # place here, I think. --daniel 2011-04-11 %w{face_base indirection_base}.each do |name| it { should_not include name } end end context "help for legacy applications" do subject { Puppet::Face[:help, :current] } let :appname do subject.legacy_applications.first end # This test is purposely generic, so that as we eliminate legacy commands # we don't get into a loop where we either test a face-based replacement # and fail to notice breakage, or where we have to constantly rewrite this # test and all. --daniel 2011-04-11 it "should return the legacy help when given the subcommand" do help = subject.help(appname) help.should =~ /puppet-#{appname}/ %w{SYNOPSIS USAGE DESCRIPTION OPTIONS COPYRIGHT}.each do |heading| help.should =~ /^#{heading}$/ end end it "should fail when asked for an action on a legacy command" do expect { subject.help(appname, :whatever) }. to raise_error ArgumentError, /Legacy subcommands don't take actions/ end end end diff --git a/spec/unit/face/instrumentation_data.rb b/spec/unit/face/instrumentation_data.rb index 2d4cc74f6..170b00b8d 100644 --- a/spec/unit/face/instrumentation_data.rb +++ b/spec/unit/face/instrumentation_data.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:instrumentation_data, '0.0.1'] do it_should_behave_like "an indirector face" end diff --git a/spec/unit/face/instrumentation_listener.rb b/spec/unit/face/instrumentation_listener.rb index 87f218855..bea0700d5 100644 --- a/spec/unit/face/instrumentation_listener.rb +++ b/spec/unit/face/instrumentation_listener.rb @@ -1,38 +1,38 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:instrumentation_listener, '0.0.1'] do it_should_behave_like "an indirector face" [:enable, :disable].each do |m| describe "when running ##{m}" do before(:each) do @listener = stub_everything 'listener' Puppet::Face[:instrumentation_listener, '0.0.1'].stubs(:find).returns(@listener) Puppet::Face[:instrumentation_listener, '0.0.1'].stubs(:save) Puppet::Util::Instrumentation::Listener.indirection.stubs(:terminus_class=) end it "should force the REST terminus" do Puppet::Util::Instrumentation::Listener.indirection.expects(:terminus_class=).with(:rest) subject.send(m, "dummy") end it "should find the named listener" do Puppet::Face[:instrumentation_listener, '0.0.1'].expects(:find).with("dummy").returns(@listener) subject.send(m, "dummy") end it "should #{m} the named listener" do @listener.expects(:enabled=).with( m == :enable ) subject.send(m, "dummy") end it "should save finally the listener" do Puppet::Face[:instrumentation_listener, '0.0.1'].expects(:save).with(@listener) subject.send(m, "dummy") end end end end diff --git a/spec/unit/face/instrumentation_probe_spec.rb b/spec/unit/face/instrumentation_probe_spec.rb index 3e475906d..bfbcfa809 100644 --- a/spec/unit/face/instrumentation_probe_spec.rb +++ b/spec/unit/face/instrumentation_probe_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:instrumentation_probe, '0.0.1'] do it_should_behave_like "an indirector face" describe 'when running #enable' do it 'should invoke #save' do subject.expects(:save).with(nil) subject.enable('hostname') end end describe 'when running #disable' do it 'should invoke #destroy' do subject.expects(:destroy).with(nil) subject.disable('hostname') end end end diff --git a/spec/unit/face/key_spec.rb b/spec/unit/face/key_spec.rb index 7de4c6e76..1aa7937e9 100755 --- a/spec/unit/face/key_spec.rb +++ b/spec/unit/face/key_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:key, '0.0.1'] do it "should actually have some tests..." end diff --git a/spec/unit/face/node_spec.rb b/spec/unit/face/node_spec.rb index 299f66fe5..80c2956ea 100755 --- a/spec/unit/face/node_spec.rb +++ b/spec/unit/face/node_spec.rb @@ -1,271 +1,271 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:node, '0.0.1'] do after :all do Puppet::SSL::Host.ca_location = :none end describe '#cleanup' do it "should clean everything" do { "cert" => ['hostname'], "cached_facts" => ['hostname'], "cached_node" => ['hostname'], "reports" => ['hostname'], "storeconfigs" => ['hostname', :unexport] }.each { |k, v| subject.expects("clean_#{k}".to_sym).with(*v) } subject.cleanup('hostname', :unexport) end end describe 'when running #clean' do before :each do Puppet::Node::Facts.indirection.stubs(:terminus_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.stubs(:terminus_class=) Puppet::Node.stubs(:cache_class=) end it 'should invoke #cleanup' do subject.expects(:cleanup).with('hostname', nil) subject.clean('hostname') end end describe "clean action" do before :each do Puppet::Node::Facts.indirection.stubs(:terminus_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Node.stubs(:terminus_class=) Puppet::Node.stubs(:cache_class=) subject.stubs(:cleanup) end it "should have a clean action" do subject.should be_action :clean end it "should not accept a call with no arguments" do expect { subject.clean() }.should raise_error end it "should accept a node name" do expect { subject.clean('hostname') }.should_not raise_error end it "should accept more than one node name" do expect do subject.clean('hostname', 'hostname2', {}) end.should_not raise_error expect do subject.clean('hostname', 'hostname2', 'hostname3', { :unexport => true }) end.should_not raise_error end it "should accept the option --unexport" do expect { subject.help('hostname', :unexport => true) }. should_not raise_error ArgumentError end context "clean action" do subject { Puppet::Face[:node, :current] } before :each do Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end describe "during setup" do it "should set facts terminus and cache class to yaml" do Puppet::Node::Facts.indirection.expects(:terminus_class=).with(:yaml) Puppet::Node::Facts.indirection.expects(:cache_class=).with(:yaml) subject.clean('hostname') end it "should run in master mode" do subject.clean('hostname') Puppet[:run_mode].should == :master end it "should set node cache as yaml" do Puppet::Node.indirection.expects(:terminus_class=).with(:yaml) Puppet::Node.indirection.expects(:cache_class=).with(:yaml) subject.clean('hostname') end it "should manage the certs if the host is a CA" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:local) subject.clean('hostname') end it "should not manage the certs if the host is not a CA" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(false) Puppet::SSL::Host.expects(:ca_location=).with(:none) subject.clean('hostname') end end describe "when cleaning certificate" do before :each do Puppet::SSL::Host.stubs(:destroy) @ca = mock() Puppet::SSL::CertificateAuthority.stubs(:instance).returns(@ca) end it "should send the :destroy order to the ca if we are a CA" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) @ca.expects(:revoke).with(@host) @ca.expects(:destroy).with(@host) subject.clean_cert(@host) end it "should not destroy the certs if we are not a CA" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(false) @ca.expects(:revoke).never @ca.expects(:destroy).never subject.clean_cert(@host) end end describe "when cleaning cached facts" do it "should destroy facts" do @host = 'node' Puppet::Node::Facts.indirection.expects(:destroy).with(@host) subject.clean_cached_facts(@host) end end describe "when cleaning cached node" do it "should destroy the cached node" do Puppet::Node.indirection.expects(:destroy).with(@host) subject.clean_cached_node(@host) end end describe "when cleaning archived reports" do it "should tell the reports to remove themselves" do Puppet::Transaction::Report.indirection.stubs(:destroy).with(@host) subject.clean_reports(@host) end end describe "when cleaning storeconfigs entries for host", :if => Puppet.features.rails? do before :each do # Stub this so we don't need access to the DB require 'puppet/rails/host' Puppet.stubs(:[]).with(:storeconfigs).returns(true) Puppet::Rails.stubs(:connect) @rails_node = stub_everything 'rails_node' Puppet::Rails::Host.stubs(:find_by_name).returns(@rails_node) end it "should connect to the database" do Puppet::Rails.expects(:connect) subject.clean_storeconfigs(@host, false) end it "should find the right host entry" do Puppet::Rails::Host.expects(:find_by_name).with(@host).returns(@rails_node) subject.clean_storeconfigs(@host, false) end describe "without unexport" do it "should remove the host and it's content" do @rails_node.expects(:destroy) subject.clean_storeconfigs(@host, false) end end describe "with unexport" do before :each do @rails_node.stubs(:id).returns(1234) @type = stub_everything 'type' @type.stubs(:validattr?).with(:ensure).returns(true) @ensure_name = stub_everything 'ensure_name', :id => 23453 Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(@ensure_name) @param_values = stub_everything 'param_values' @resource = stub_everything 'resource', :param_values => @param_values, :restype => "File" Puppet::Rails::Resource.stubs(:find).returns([@resource]) end it "should find all resources" do Puppet::Rails::Resource.expects(:find).with(:all, {:include => {:param_values => :param_name}, :conditions => ["exported=? AND host_id=?", true, 1234]}).returns([]) subject.clean_storeconfigs(@host, true) end describe "with an exported native type" do before :each do Puppet::Type.stubs(:type).returns(@type) @type.expects(:validattr?).with(:ensure).returns(true) end it "should test a native type for ensure as an attribute" do subject.clean_storeconfigs(@host, true) end it "should delete the old ensure parameter" do ensure_param = stub 'ensure_param', :id => 12345, :line => 12 @param_values.stubs(:find).returns(ensure_param) Puppet::Rails::ParamValue.expects(:delete).with(12345); subject.clean_storeconfigs(@host, true) end it "should add an ensure => absent parameter" do @param_values.expects(:create).with(:value => "absent", :line => 0, :param_name => @ensure_name) subject.clean_storeconfigs(@host, true) end end describe "with an exported definition" do it "should try to lookup a definition and test it for the ensure argument" do Puppet::Type.stubs(:type).returns(nil) definition = stub_everything 'definition', :arguments => { 'ensure' => 'present' } Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(definition) subject.clean_storeconfigs(@host, true) end end it "should not unexport the resource of an unkown type" do Puppet::Type.stubs(:type).returns(nil) Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(nil) Puppet::Rails::ParamName.expects(:find_or_create_by_name).never subject.clean_storeconfigs(@host, true) end it "should not unexport the resource of a not ensurable native type" do Puppet::Type.stubs(:type).returns(@type) @type.expects(:validattr?).with(:ensure).returns(false) Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(nil) Puppet::Rails::ParamName.expects(:find_or_create_by_name).never subject.clean_storeconfigs(@host, true) end it "should not unexport the resource of a not ensurable definition" do Puppet::Type.stubs(:type).returns(nil) definition = stub_everything 'definition', :arguments => { 'foobar' => 'someValue' } Puppet::Resource::TypeCollection.any_instance.expects(:find_definition).with('', "File").returns(definition) Puppet::Rails::ParamName.expects(:find_or_create_by_name).never subject.clean_storeconfigs(@host, true) end end end end end end diff --git a/spec/unit/face/plugin_spec.rb b/spec/unit/face/plugin_spec.rb index 383aaa3d3..2d014f145 100755 --- a/spec/unit/face/plugin_spec.rb +++ b/spec/unit/face/plugin_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:plugin, '0.0.1'] do [:download].each do |action| it { should be_action action } it { should respond_to action } end end diff --git a/spec/unit/face/report_spec.rb b/spec/unit/face/report_spec.rb index befc4e496..f7daf16d2 100755 --- a/spec/unit/face/report_spec.rb +++ b/spec/unit/face/report_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:report, '0.0.1'] do it "should actually have some tests..." end diff --git a/spec/unit/face/resource_spec.rb b/spec/unit/face/resource_spec.rb index 0671af4c2..236789060 100755 --- a/spec/unit/face/resource_spec.rb +++ b/spec/unit/face/resource_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:resource, '0.0.1'] do it "should actually have some tests..." end diff --git a/spec/unit/face/resource_type_spec.rb b/spec/unit/face/resource_type_spec.rb index 30a1adfcb..c385a79bb 100755 --- a/spec/unit/face/resource_type_spec.rb +++ b/spec/unit/face/resource_type_spec.rb @@ -1,7 +1,7 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' describe Puppet::Face[:resource_type, '0.0.1'] do it "should actually have some tests..." end diff --git a/spec/unit/face/secret_agent_spec.rb b/spec/unit/face/secret_agent_spec.rb index 2530d144d..f49e71a95 100755 --- a/spec/unit/face/secret_agent_spec.rb +++ b/spec/unit/face/secret_agent_spec.rb @@ -1,27 +1,27 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' require 'puppet/indirector/catalog/rest' require 'tempfile' describe Puppet::Face[:secret_agent, '0.0.1'] do include PuppetSpec::Files describe "#synchronize" do it "should retrieve and apply a catalog and return a report" do pending "This test doesn't work, but the code actually does - tested by LAK" dirname = tmpdir("puppetdir") Puppet[:vardir] = dirname Puppet[:confdir] = dirname @catalog = Puppet::Resource::Catalog.new @file = Puppet::Resource.new(:file, File.join(dirname, "tmp_dir_resource"), :parameters => {:ensure => :present}) @catalog.add_resource(@file) Puppet::Resource::Catalog::Rest.any_instance.stubs(:find).returns(@catalog) report = subject.synchronize report.kind.should == "apply" report.status.should == "changed" end end end diff --git a/spec/unit/file_bucket/dipper_spec.rb b/spec/unit/file_bucket/dipper_spec.rb index a5db657d0..79859b2e9 100755 --- a/spec/unit/file_bucket/dipper_spec.rb +++ b/spec/unit/file_bucket/dipper_spec.rb @@ -1,170 +1,170 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'pathname' require 'puppet/file_bucket/dipper' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::FileBucket::Dipper do include PuppetSpec::Files def make_tmp_file(contents) file = tmpfile("file_bucket_file") File.open(file, 'wb') { |f| f.write(contents) } file end it "should fail in an informative way when there are failures checking for the file on the server" do @dipper = Puppet::FileBucket::Dipper.new(:Path => make_absolute("/my/bucket")) file = make_tmp_file('contents') Puppet::FileBucket::File.indirection.expects(:head).raises ArgumentError lambda { @dipper.backup(file) }.should raise_error(Puppet::Error) end it "should fail in an informative way when there are failures backing up to the server" do @dipper = Puppet::FileBucket::Dipper.new(:Path => make_absolute("/my/bucket")) file = make_tmp_file('contents') Puppet::FileBucket::File.indirection.expects(:head).returns false Puppet::FileBucket::File.indirection.expects(:save).raises ArgumentError lambda { @dipper.backup(file) }.should raise_error(Puppet::Error) end it "should backup files to a local bucket" do Puppet[:bucketdir] = "/non/existent/directory" file_bucket = tmpdir("bucket") @dipper = Puppet::FileBucket::Dipper.new(:Path => file_bucket) file = make_tmp_file("my\r\ncontents") checksum = "f0d7d4e480ad698ed56aeec8b6bd6dea" Digest::MD5.hexdigest("my\r\ncontents").should == checksum @dipper.backup(file).should == checksum File.exists?("#{file_bucket}/f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea/contents").should == true end it "should not backup a file that is already in the bucket" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") file = make_tmp_file("my\r\ncontents") checksum = Digest::MD5.hexdigest("my\r\ncontents") Puppet::FileBucket::File.indirection.expects(:head).returns true Puppet::FileBucket::File.indirection.expects(:save).never @dipper.backup(file).should == checksum end it "should retrieve files from a local bucket" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") checksum = Digest::MD5.hexdigest('my contents') request = nil Puppet::FileBucketFile::File.any_instance.expects(:find).with{ |r| request = r }.once.returns(Puppet::FileBucket::File.new('my contents')) @dipper.getfile(checksum).should == 'my contents' request.key.should == "md5/#{checksum}" end it "should backup files to a remote server" do @dipper = Puppet::FileBucket::Dipper.new(:Server => "puppetmaster", :Port => "31337") file = make_tmp_file("my\r\ncontents") checksum = Digest::MD5.hexdigest("my\r\ncontents") real_path = Pathname.new(file).realpath request1 = nil request2 = nil Puppet::FileBucketFile::Rest.any_instance.expects(:head).with { |r| request1 = r }.once.returns(nil) Puppet::FileBucketFile::Rest.any_instance.expects(:save).with { |r| request2 = r }.once @dipper.backup(file).should == checksum [request1, request2].each do |r| r.server.should == 'puppetmaster' r.port.should == 31337 r.key.should == "md5/#{checksum}/#{real_path}" end end it "should retrieve files from a remote server" do @dipper = Puppet::FileBucket::Dipper.new(:Server => "puppetmaster", :Port => "31337") checksum = Digest::MD5.hexdigest('my contents') request = nil Puppet::FileBucketFile::Rest.any_instance.expects(:find).with { |r| request = r }.returns(Puppet::FileBucket::File.new('my contents')) @dipper.getfile(checksum).should == "my contents" request.server.should == 'puppetmaster' request.port.should == 31337 request.key.should == "md5/#{checksum}" end describe "#restore" do shared_examples_for "a restorable file" do let(:contents) { "my\ncontents" } let(:md5) { Digest::MD5.hexdigest(contents) } let(:dest) { tmpfile('file_bucket_dest') } it "should restore the file" do request = nil klass.any_instance.expects(:find).with { |r| request = r }.returns(Puppet::FileBucket::File.new(contents)) dipper.restore(dest, md5).should == md5 Digest::MD5.hexdigest(IO.binread(dest)).should == md5 request.key.should == "md5/#{md5}" request.server.should == server request.port.should == port end it "should skip restoring if existing file has the same checksum" do crnl = "my\r\ncontents" File.open(dest, 'wb') {|f| f.print(crnl) } dipper.expects(:getfile).never dipper.restore(dest, Digest::MD5.hexdigest(crnl)).should be_nil end it "should overwrite existing file if it has different checksum" do klass.any_instance.expects(:find).returns(Puppet::FileBucket::File.new(contents)) File.open(dest, 'wb') {|f| f.print('other contents') } dipper.restore(dest, md5).should == md5 end end describe "when restoring from a remote server" do let(:klass) { Puppet::FileBucketFile::Rest } let(:server) { "puppetmaster" } let(:port) { 31337 } it_behaves_like "a restorable file" do let (:dipper) { Puppet::FileBucket::Dipper.new(:Server => server, :Port => port.to_s) } end end describe "when restoring from a local server" do let(:klass) { Puppet::FileBucketFile::File } let(:server) { nil } let(:port) { nil } it_behaves_like "a restorable file" do let (:dipper) { Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") } end end end end diff --git a/spec/unit/file_bucket/file_spec.rb b/spec/unit/file_bucket/file_spec.rb index 27579647a..ec069ac93 100755 --- a/spec/unit/file_bucket/file_spec.rb +++ b/spec/unit/file_bucket/file_spec.rb @@ -1,106 +1,106 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_bucket/file' require 'digest/md5' require 'digest/sha1' describe Puppet::FileBucket::File do include PuppetSpec::Files let(:contents) { "file\r\n contents" } let(:digest) { "8b3702ad1aed1ace7e32bde76ffffb2d" } let(:checksum) { "{md5}#{digest}" } # this is the default from spec_helper, but it keeps getting reset at odd times let(:bucketdir) { Puppet[:bucketdir] = tmpdir('bucket') } let(:destdir) { File.join(bucketdir, "8/b/3/7/0/2/a/d/#{digest}") } it "should have a to_s method to return the contents" do Puppet::FileBucket::File.new(contents).to_s.should == contents end it "should raise an error if changing content" do x = Puppet::FileBucket::File.new("first") expect { x.contents = "new" }.to raise_error(NoMethodError, /undefined method .contents=/) end it "should require contents to be a string" do expect { Puppet::FileBucket::File.new(5) }.to raise_error(ArgumentError, /contents must be a String, got a Fixnum$/) end it "should complain about options other than :bucket_path" do expect { Puppet::FileBucket::File.new('5', :crazy_option => 'should not be passed') }.to raise_error(ArgumentError, /Unknown option\(s\): crazy_option/) end it "should set the contents appropriately" do Puppet::FileBucket::File.new(contents).contents.should == contents end it "should default to 'md5' as the checksum algorithm if the algorithm is not in the name" do Puppet::FileBucket::File.new(contents).checksum_type.should == "md5" end it "should calculate the checksum" do Puppet::FileBucket::File.new(contents).checksum.should == checksum end describe "when using back-ends" do it "should redirect using Puppet::Indirector" do Puppet::Indirector::Indirection.instance(:file_bucket_file).model.should equal(Puppet::FileBucket::File) end it "should have a :save instance method" do Puppet::FileBucket::File.indirection.should respond_to(:save) end end it "should return a url-ish name" do Puppet::FileBucket::File.new(contents).name.should == "md5/#{digest}" end it "should reject a url-ish name with an invalid checksum" do bucket = Puppet::FileBucket::File.new(contents) expect { bucket.name = "sha1/ae548c0cd614fb7885aaa0b6cb191c34/new/path" }.to raise_error(NoMethodError, /undefined method .name=/) end it "should convert the contents to PSON" do Puppet::FileBucket::File.new("file contents").to_pson.should == '{"contents":"file contents"}' end it "should load from PSON" do Puppet::FileBucket::File.from_pson({"contents"=>"file contents"}).contents.should == "file contents" end def make_bucketed_file FileUtils.mkdir_p(destdir) File.open("#{destdir}/contents", 'wb') { |f| f.write contents } end describe "using the indirector's find method" do it "should return nil if a file doesn't exist" do bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{digest}") bucketfile.should == nil end it "should find a filebucket if the file exists" do make_bucketed_file bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{digest}") bucketfile.checksum.should == checksum end describe "using RESTish digest notation" do it "should return nil if a file doesn't exist" do bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{digest}") bucketfile.should == nil end it "should find a filebucket if the file exists" do make_bucketed_file bucketfile = Puppet::FileBucket::File.indirection.find("md5/#{digest}") bucketfile.checksum.should == checksum end end end end diff --git a/spec/unit/file_collection/lookup_spec.rb b/spec/unit/file_collection/lookup_spec.rb index 2b0f8bfab..a2376f365 100755 --- a/spec/unit/file_collection/lookup_spec.rb +++ b/spec/unit/file_collection/lookup_spec.rb @@ -1,45 +1,45 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_collection/lookup' class LookupTester include Puppet::FileCollection::Lookup end describe Puppet::FileCollection::Lookup do before do @tester = LookupTester.new @file_collection = mock 'file_collection' Puppet::FileCollection.stubs(:collection).returns @file_collection end it "should use the file collection to determine the index of the file name" do @file_collection.expects(:index).with("/my/file").returns 50 @tester.file = "/my/file" @tester.file_index.should == 50 end it "should return nil as the file name if no index is set" do @tester.file.should be_nil end it "should use the file collection to convert the index to a file name" do @file_collection.expects(:path).with(25).returns "/path/to/file" @tester.file_index = 25 @tester.file.should == "/path/to/file" end it "should support a line attribute" do @tester.line = 50 @tester.line.should == 50 end it "should default to the global file collection" do Puppet::FileCollection.expects(:collection).returns "collection" @tester.file_collection.should == "collection" end end diff --git a/spec/unit/file_collection_spec.rb b/spec/unit/file_collection_spec.rb index 518763629..d5715cdf6 100755 --- a/spec/unit/file_collection_spec.rb +++ b/spec/unit/file_collection_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_collection' describe Puppet::FileCollection do before do @collection = Puppet::FileCollection.new end it "should be able to convert a file name into an index" do @collection.index("/my/file").should be_instance_of(Fixnum) end it "should be able to convert an index into a file name" do index = @collection.index("/path/to/file") @collection.path(index).should == "/path/to/file" end it "should always give the same file name for a given index" do index = @collection.index("/path/to/file") @collection.path(index).should == @collection.path(index) end it "should always give the same index for a given file name" do @collection.index("/my/file").should == @collection.index("/my/file") end it "should always correctly relate a file name and its index even when multiple files are in the collection" do indexes = %w{a b c d e f}.inject({}) do |hash, letter| hash[letter] = @collection.index("/path/to/file/#{letter}") hash end indexes.each do |letter, index| @collection.index("/path/to/file/#{letter}").should == indexes[letter] @collection.path(index).should == @collection.path(index) end end it "should return nil as the file name when an unknown index is provided" do @collection.path(50).should be_nil end it "should provide a global collection" do Puppet::FileCollection.collection.should be_instance_of(Puppet::FileCollection) end it "should reuse the global collection" do Puppet::FileCollection.collection.should equal(Puppet::FileCollection.collection) end end diff --git a/spec/unit/file_serving/base_spec.rb b/spec/unit/file_serving/base_spec.rb index cea22da57..bc3cbb8bd 100755 --- a/spec/unit/file_serving/base_spec.rb +++ b/spec/unit/file_serving/base_spec.rb @@ -1,149 +1,149 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/base' describe Puppet::FileServing::Base do let(:path) { File.expand_path('/module/dir/file') } let(:file) { File.expand_path('/my/file') } it "should accept a path" do Puppet::FileServing::Base.new(path).path.should == path end it "should require that paths be fully qualified" do lambda { Puppet::FileServing::Base.new("module/dir/file") }.should raise_error(ArgumentError) end it "should allow specification of whether links should be managed" do Puppet::FileServing::Base.new(path, :links => :manage).links.should == :manage end it "should have a :source attribute" do file = Puppet::FileServing::Base.new(path) file.should respond_to(:source) file.should respond_to(:source=) end it "should consider :ignore links equivalent to :manage links" do Puppet::FileServing::Base.new(path, :links => :ignore).links.should == :manage end it "should fail if :links is set to anything other than :manage, :follow, or :ignore" do proc { Puppet::FileServing::Base.new(path, :links => :else) }.should raise_error(ArgumentError) end it "should allow links values to be set as strings" do Puppet::FileServing::Base.new(path, :links => "follow").links.should == :follow end it "should default to :manage for :links" do Puppet::FileServing::Base.new(path).links.should == :manage end it "should allow specification of a path" do FileTest.stubs(:exists?).returns(true) Puppet::FileServing::Base.new(path, :path => file).path.should == file end it "should allow specification of a relative path" do FileTest.stubs(:exists?).returns(true) Puppet::FileServing::Base.new(path, :relative_path => "my/file").relative_path.should == "my/file" end it "should have a means of determining if the file exists" do Puppet::FileServing::Base.new(file).should respond_to(:exist?) end it "should correctly indicate if the file is present" do File.expects(:lstat).with(file).returns(mock("stat")) Puppet::FileServing::Base.new(file).exist?.should be_true end it "should correctly indicate if the file is absent" do File.expects(:lstat).with(file).raises RuntimeError Puppet::FileServing::Base.new(file).exist?.should be_false end describe "when setting the relative path" do it "should require that the relative path be unqualified" do @file = Puppet::FileServing::Base.new(path) FileTest.stubs(:exists?).returns(true) proc { @file.relative_path = File.expand_path("/qualified/file") }.should raise_error(ArgumentError) end end describe "when determining the full file path" do let(:path) { File.expand_path('/this/file') } let(:file) { Puppet::FileServing::Base.new(path) } it "should return the path if there is no relative path" do file.full_path.should == path end it "should return the path if the relative_path is set to ''" do file.relative_path = "" file.full_path.should == path end it "should return the path if the relative_path is set to '.'" do file.relative_path = "." file.full_path.should == path end it "should return the path joined with the relative path if there is a relative path and it is not set to '/' or ''" do file.relative_path = "not/qualified" file.full_path.should == File.join(path, "not/qualified") end it "should strip extra slashes" do file = Puppet::FileServing::Base.new(File.join(File.expand_path('/'), "//this//file")) file.full_path.should == path end end describe "when stat'ing files" do let(:path) { File.expand_path('/this/file') } let(:file) { Puppet::FileServing::Base.new(path) } it "should stat the file's full path" do File.expects(:lstat).with(path).returns stub("stat", :ftype => "file") file.stat end it "should fail if the file does not exist" do File.expects(:lstat).with(path).raises(Errno::ENOENT) proc { file.stat }.should raise_error(Errno::ENOENT) end it "should use :lstat if :links is set to :manage" do File.expects(:lstat).with(path).returns stub("stat", :ftype => "file") file.stat end it "should use :stat if :links is set to :follow" do File.expects(:stat).with(path).returns stub("stat", :ftype => "file") file.links = :follow file.stat end end describe "#absolute?" do it "should be accept POSIX paths" do Puppet::FileServing::Base.should be_absolute('/') end it "should accept Windows paths on Windows" do Puppet.features.stubs(:microsoft_windows?).returns(true) Puppet.features.stubs(:posix?).returns(false) Puppet::FileServing::Base.should be_absolute('c:/foo') end it "should reject Windows paths on POSIX" do Puppet.features.stubs(:microsoft_windows?).returns(false) Puppet::FileServing::Base.should_not be_absolute('c:/foo') end end end diff --git a/spec/unit/file_serving/configuration/parser_spec.rb b/spec/unit/file_serving/configuration/parser_spec.rb index 6c9aa5034..74b8efaa8 100755 --- a/spec/unit/file_serving/configuration/parser_spec.rb +++ b/spec/unit/file_serving/configuration/parser_spec.rb @@ -1,190 +1,190 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/configuration/parser' describe Puppet::FileServing::Configuration::Parser do it "should subclass the LoadedFile class" do Puppet::FileServing::Configuration::Parser.superclass.should equal(Puppet::Util::LoadedFile) end end module FSConfigurationParserTesting def mock_file_content(content) # We want an array, but we actually want our carriage returns on all of it. lines = content.split("\n").collect { |l| l + "\n" } @filehandle.stubs(:each_line).multiple_yields(*lines) @filehandle.expects(:each).never end end describe Puppet::FileServing::Configuration::Parser do before :each do @path = "/my/config.conf" FileTest.stubs(:exists?).with(@path).returns(true) FileTest.stubs(:readable?).with(@path).returns(true) @filehandle = mock 'filehandle' @filehandle.expects(:each).never File.expects(:open).with(@path).yields(@filehandle) @parser = Puppet::FileServing::Configuration::Parser.new(@path) end describe Puppet::FileServing::Configuration::Parser, " when parsing" do include FSConfigurationParserTesting it "should allow comments" do @filehandle.expects(:each_line).yields("# this is a comment\n") proc { @parser.parse }.should_not raise_error end it "should allow blank lines" do @filehandle.expects(:each_line).yields("\n") proc { @parser.parse }.should_not raise_error end it "should create a new mount for each section in the configuration" do mount1 = mock 'one', :validate => true mount2 = mock 'two', :validate => true Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) mock_file_content "[one]\n[two]\n" @parser.parse end # This test is almost the exact same as the previous one. it "should return a hash of the created mounts" do mount1 = mock 'one', :validate => true mount2 = mock 'two', :validate => true Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) Puppet::FileServing::Mount::File.expects(:new).with("two").returns(mount2) mock_file_content "[one]\n[two]\n" result = @parser.parse result["one"].should equal(mount1) result["two"].should equal(mount2) end it "should only allow mount names that are alphanumeric plus dashes" do mock_file_content "[a*b]\n" proc { @parser.parse }.should raise_error(ArgumentError) end it "should fail if the value for path/allow/deny starts with an equals sign" do mock_file_content "[one]\npath = /testing" proc { @parser.parse }.should raise_error(ArgumentError) end it "should validate each created mount" do mount1 = mock 'one' Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) mock_file_content "[one]\n" mount1.expects(:validate) @parser.parse end it "should fail if any mount does not pass validation" do mount1 = mock 'one' Puppet::FileServing::Mount::File.expects(:new).with("one").returns(mount1) mock_file_content "[one]\n" mount1.expects(:validate).raises RuntimeError lambda { @parser.parse }.should raise_error(RuntimeError) end end describe Puppet::FileServing::Configuration::Parser, " when parsing mount attributes" do include FSConfigurationParserTesting before do @mount = stub 'testmount', :name => "one", :validate => true Puppet::FileServing::Mount::File.expects(:new).with("one").returns(@mount) @parser.stubs(:add_modules_mount) end it "should set the mount path to the path attribute from that section" do mock_file_content "[one]\npath /some/path\n" @mount.expects(:path=).with("/some/path") @parser.parse end it "should tell the mount to allow any allow values from the section" do mock_file_content "[one]\nallow something\n" @mount.expects(:info) @mount.expects(:allow).with("something") @parser.parse end it "should support inline comments" do mock_file_content "[one]\nallow something \# will it work?\n" @mount.expects(:info) @mount.expects(:allow).with("something") @parser.parse end it "should tell the mount to deny any deny values from the section" do mock_file_content "[one]\ndeny something\n" @mount.expects(:info) @mount.expects(:deny).with("something") @parser.parse end it "should fail on any attributes other than path, allow, and deny" do mock_file_content "[one]\ndo something\n" proc { @parser.parse }.should raise_error(ArgumentError) end end describe Puppet::FileServing::Configuration::Parser, " when parsing the modules mount" do include FSConfigurationParserTesting before do @mount = stub 'modulesmount', :name => "modules", :validate => true end it "should create an instance of the Modules Mount class" do mock_file_content "[modules]\n" Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns @mount @parser.parse end it "should warn if a path is set" do mock_file_content "[modules]\npath /some/path\n" Puppet::FileServing::Mount::Modules.expects(:new).with("modules").returns(@mount) Puppet.expects(:warning) @parser.parse end end describe Puppet::FileServing::Configuration::Parser, " when parsing the plugins mount" do include FSConfigurationParserTesting before do @mount = stub 'pluginsmount', :name => "plugins", :validate => true end it "should create an instance of the Plugins Mount class" do mock_file_content "[plugins]\n" Puppet::FileServing::Mount::Plugins.expects(:new).with("plugins").returns @mount @parser.parse end it "should warn if a path is set" do mock_file_content "[plugins]\npath /some/path\n" Puppet.expects(:warning) @parser.parse end end end diff --git a/spec/unit/file_serving/configuration_spec.rb b/spec/unit/file_serving/configuration_spec.rb index 7344c6ec6..d16638442 100755 --- a/spec/unit/file_serving/configuration_spec.rb +++ b/spec/unit/file_serving/configuration_spec.rb @@ -1,237 +1,237 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/configuration' describe Puppet::FileServing::Configuration do include PuppetSpec::Files before :each do @path = make_absolute("/path/to/configuration/file.conf") Puppet.settings.stubs(:value).with(:trace).returns(false) Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) end after :each do Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil) end it "should make :new a private method" do proc { Puppet::FileServing::Configuration.new }.should raise_error end it "should return the same configuration each time 'configuration' is called" do Puppet::FileServing::Configuration.configuration.should equal(Puppet::FileServing::Configuration.configuration) end describe "when initializing" do it "should work without a configuration file" do FileTest.stubs(:exists?).with(@path).returns(false) proc { Puppet::FileServing::Configuration.configuration }.should_not raise_error end it "should parse the configuration file if present" do FileTest.stubs(:exists?).with(@path).returns(true) @parser = mock 'parser' @parser.expects(:parse).returns({}) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) Puppet::FileServing::Configuration.configuration end it "should determine the path to the configuration file from the Puppet settings" do Puppet::FileServing::Configuration.configuration end end describe "when parsing the configuration file" do before do FileTest.stubs(:exists?).with(@path).returns(true) @parser = mock 'parser' Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) end it "should set the mount list to the results of parsing" do @parser.expects(:parse).returns("one" => mock("mount")) config = Puppet::FileServing::Configuration.configuration config.mounted?("one").should be_true end it "should not raise exceptions" do @parser.expects(:parse).raises(ArgumentError) proc { Puppet::FileServing::Configuration.configuration }.should_not raise_error end it "should replace the existing mount list with the results of reparsing" do @parser.expects(:parse).returns("one" => mock("mount")) config = Puppet::FileServing::Configuration.configuration config.mounted?("one").should be_true # Now parse again @parser.expects(:parse).returns("two" => mock('other')) config.send(:readconfig, false) config.mounted?("one").should be_false config.mounted?("two").should be_true end it "should not replace the mount list until the file is entirely parsed successfully" do @parser.expects(:parse).returns("one" => mock("mount")) @parser.expects(:parse).raises(ArgumentError) config = Puppet::FileServing::Configuration.configuration # Now parse again, so the exception gets thrown config.send(:readconfig, false) config.mounted?("one").should be_true end it "should add modules and plugins mounts even if the file does not exist" do FileTest.expects(:exists?).returns false # the file doesn't exist config = Puppet::FileServing::Configuration.configuration config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end it "should allow all access to modules and plugins if no fileserver.conf exists" do FileTest.expects(:exists?).returns false # the file doesn't exist modules = stub 'modules', :empty? => true Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*') plugins = stub 'plugins', :empty? => true Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) plugins.expects(:allow).with('*') Puppet::FileServing::Configuration.configuration end it "should not allow access from all to modules and plugins if the fileserver.conf provided some rules" do FileTest.expects(:exists?).returns false # the file doesn't exist modules = stub 'modules', :empty? => false Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*').never plugins = stub 'plugins', :empty? => false Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) plugins.expects(:allow).with('*').never Puppet::FileServing::Configuration.configuration end it "should add modules and plugins mounts even if they are not returned by the parser" do @parser.expects(:parse).returns("one" => mock("mount")) FileTest.expects(:exists?).returns true # the file doesn't exist config = Puppet::FileServing::Configuration.configuration config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end end describe "when finding the specified mount" do it "should choose the named mount if one exists" do config = Puppet::FileServing::Configuration.configuration config.expects(:mounts).returns("one" => "foo") config.find_mount("one", mock('env')).should == "foo" end it "should use the provided environment to find a matching module if the named module cannot be found" do config = Puppet::FileServing::Configuration.configuration mod = mock 'module' env = mock 'environment' env.expects(:module).with("foo").returns mod mount = mock 'mount' config.stubs(:mounts).returns("modules" => mount) Puppet.expects(:deprecation_warning) config.find_mount("foo", env).should equal(mount) end it "should return nil if there is no such named mount and no module with the same name exists" do config = Puppet::FileServing::Configuration.configuration env = mock 'environment' env.expects(:module).with("foo").returns nil mount = mock 'mount' config.stubs(:mounts).returns("modules" => mount) config.find_mount("foo", env).should be_nil end end describe "when finding the mount name and relative path in a request key" do before do @config = Puppet::FileServing::Configuration.configuration @config.stubs(:find_mount) @request = stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env") end it "should reread the configuration" do @config.expects(:readconfig) @config.split_path(@request) end it "should treat the first field of the URI path as the mount name" do @config.expects(:find_mount).with { |name, node| name == "foo" } @config.split_path(@request) end it "should fail if the mount name is not alpha-numeric" do @request.expects(:key).returns "foo&bar/asdf" lambda { @config.split_path(@request) }.should raise_error(ArgumentError) end it "should support dashes in the mount name" do @request.expects(:key).returns "foo-bar/asdf" lambda { @config.split_path(@request) }.should_not raise_error(ArgumentError) end it "should use the mount name and environment to find the mount" do @config.expects(:find_mount).with { |name, env| name == "foo" and env == @request.environment } @request.stubs(:node).returns("mynode") @config.split_path(@request) end it "should return nil if the mount cannot be found" do @config.expects(:find_mount).returns nil @config.split_path(@request).should be_nil end it "should return the mount and the relative path if the mount is found" do mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "bar/baz"] end it "should remove any double slashes" do @request.stubs(:key).returns "foo/bar//baz" mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "bar/baz"] end it "should return the relative path as nil if it is an empty string" do @request.expects(:key).returns "foo" mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, nil] end it "should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility" do @request.expects(:key).returns "foo/bar" mount = stub 'mount', :name => "modules" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "foo/bar"] end end end diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb index 7a9642fbb..348f53d84 100755 --- a/spec/unit/file_serving/content_spec.rb +++ b/spec/unit/file_serving/content_spec.rb @@ -1,116 +1,116 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/content' describe Puppet::FileServing::Content do let(:path) { File.expand_path('/path') } it "should should be a subclass of Base" do Puppet::FileServing::Content.superclass.should equal(Puppet::FileServing::Base) end it "should indirect file_content" do Puppet::FileServing::Content.indirection.name.should == :file_content end it "should should include the IndirectionHooks module in its indirection" do Puppet::FileServing::Content.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) end it "should only support the raw format" do Puppet::FileServing::Content.supported_formats.should == [:raw] end it "should have a method for collecting its attributes" do Puppet::FileServing::Content.new(path).should respond_to(:collect) end it "should not retrieve and store its contents when its attributes are collected if the file is a normal file" do content = Puppet::FileServing::Content.new(path) result = "foo" File.stubs(:lstat).returns(stub("stat", :ftype => "file")) File.expects(:read).with(path).never content.collect content.instance_variable_get("@content").should be_nil end it "should not attempt to retrieve its contents if the file is a directory" do content = Puppet::FileServing::Content.new(path) result = "foo" File.stubs(:lstat).returns(stub("stat", :ftype => "directory")) File.expects(:read).with(path).never content.collect content.instance_variable_get("@content").should be_nil end it "should have a method for setting its content" do content = Puppet::FileServing::Content.new(path) content.should respond_to(:content=) end it "should make content available when set externally" do content = Puppet::FileServing::Content.new(path) content.content = "foo/bar" content.content.should == "foo/bar" end it "should be able to create a content instance from raw file contents" do Puppet::FileServing::Content.should respond_to(:from_raw) end it "should create an instance with a fake file name and correct content when converting from raw" do instance = mock 'instance' Puppet::FileServing::Content.expects(:new).with("/this/is/a/fake/path").returns instance instance.expects(:content=).with "foo/bar" Puppet::FileServing::Content.from_raw("foo/bar").should equal(instance) end it "should return an opened File when converted to raw" do content = Puppet::FileServing::Content.new(path) File.expects(:new).with(path, "rb").returns :file content.to_raw.should == :file end end describe Puppet::FileServing::Content, "when returning the contents" do let(:path) { File.expand_path('/my/path') } let(:content) { Puppet::FileServing::Content.new(path, :links => :follow) } it "should fail if the file is a symlink and links are set to :manage" do content.links = :manage File.expects(:lstat).with(path).returns stub("stat", :ftype => "symlink") proc { content.content }.should raise_error(ArgumentError) end it "should fail if a path is not set" do proc { content.content }.should raise_error(Errno::ENOENT) end it "should raise Errno::ENOENT if the file is absent" do content.path = File.expand_path("/there/is/absolutely/no/chance/that/this/path/exists") proc { content.content }.should raise_error(Errno::ENOENT) end it "should return the contents of the path if the file exists" do File.expects(:stat).with(path).returns stub("stat", :ftype => "file") IO.expects(:binread).with(path).returns(:mycontent) content.content.should == :mycontent end it "should cache the returned contents" do File.expects(:stat).with(path).returns stub("stat", :ftype => "file") IO.expects(:binread).with(path).returns(:mycontent) content.content # The second run would throw a failure if the content weren't being cached. content.content end end diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 858a69d55..9f3a1ed89 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -1,378 +1,378 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/fileset' describe Puppet::FileServing::Fileset, " when initializing" do include PuppetSpec::Files let(:request) { Puppet::Indirector::Request.new(:file_serving, :find, "foo", nil) } before :each do @somefile = make_absolute("/some/file") end it "should require a path" do proc { Puppet::FileServing::Fileset.new }.should raise_error(ArgumentError) end it "should fail if its path is not fully qualified" do proc { Puppet::FileServing::Fileset.new("some/file") }.should raise_error(ArgumentError) end it "should not fail if the path is fully qualified, with a trailing separator" do path_with_separator = "#{@somefile}#{File::SEPARATOR}" File.stubs(:lstat).with(@somefile).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path_with_separator) fileset.path.should == @somefile end it "should not fail if the path is just the file separator" do path = File.expand_path(File::SEPARATOR) File.stubs(:lstat).with(path).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path) fileset.path.should == path end it "should fail if its path does not exist" do File.expects(:lstat).with(@somefile).returns nil proc { Puppet::FileServing::Fileset.new(@somefile) }.should raise_error(ArgumentError) end it "should accept a 'recurse' option" do File.expects(:lstat).with(@somefile).returns stub("stat") set = Puppet::FileServing::Fileset.new(@somefile, :recurse => true) set.recurse.should be_true end it "should accept a 'recurselimit' option" do File.expects(:lstat).with(@somefile).returns stub("stat") set = Puppet::FileServing::Fileset.new(@somefile, :recurselimit => 3) set.recurselimit.should == 3 end it "should accept an 'ignore' option" do File.expects(:lstat).with(@somefile).returns stub("stat") set = Puppet::FileServing::Fileset.new(@somefile, :ignore => ".svn") set.ignore.should == [".svn"] end it "should accept a 'links' option" do File.expects(:lstat).with(@somefile).returns stub("stat") set = Puppet::FileServing::Fileset.new(@somefile, :links => :manage) set.links.should == :manage end it "should accept a 'checksum_type' option" do File.expects(:lstat).with(@somefile).returns stub("stat") set = Puppet::FileServing::Fileset.new(@somefile, :checksum_type => :test) set.checksum_type.should == :test end it "should fail if 'links' is set to anything other than :manage or :follow" do proc { Puppet::FileServing::Fileset.new(@somefile, :links => :whatever) }.should raise_error(ArgumentError) end it "should default to 'false' for recurse" do File.expects(:lstat).with(@somefile).returns stub("stat") Puppet::FileServing::Fileset.new(@somefile).recurse.should == false end it "should default to :infinite for recurselimit" do File.expects(:lstat).with(@somefile).returns stub("stat") Puppet::FileServing::Fileset.new(@somefile).recurselimit.should == :infinite end it "should default to an empty ignore list" do File.expects(:lstat).with(@somefile).returns stub("stat") Puppet::FileServing::Fileset.new(@somefile).ignore.should == [] end it "should default to :manage for links" do File.expects(:lstat).with(@somefile).returns stub("stat") Puppet::FileServing::Fileset.new(@somefile).links.should == :manage end it "should support using an Indirector Request for its options" do File.expects(:lstat).with(@somefile).returns stub("stat") lambda { Puppet::FileServing::Fileset.new(@somefile, request) }.should_not raise_error end describe "using an indirector request" do before do File.stubs(:lstat).returns stub("stat") @values = {:links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234} @myfile = make_absolute("/my/file") end [:recurse, :recurselimit, :ignore, :links].each do |option| it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present" do request.stubs(:options).returns(option => @values[option]) Puppet::FileServing::Fileset.new(@myfile, request).send(option).should == @values[option] end it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present with the keys stored as strings" do request.stubs(:options).returns(option.to_s => @values[option]) Puppet::FileServing::Fileset.new(@myfile, request).send(option).should == @values[option] end end it "should convert the integer as a string to their integer counterpart when setting options" do request.stubs(:options).returns(:recurselimit => "1234") Puppet::FileServing::Fileset.new(@myfile, request).recurselimit.should == 1234 end it "should convert the string 'true' to the boolean true when setting options" do request.stubs(:options).returns(:recurse => "true") Puppet::FileServing::Fileset.new(@myfile, request).recurse.should == true end it "should convert the string 'false' to the boolean false when setting options" do request.stubs(:options).returns(:recurse => "false") Puppet::FileServing::Fileset.new(@myfile, request).recurse.should == false end end end describe Puppet::FileServing::Fileset, " when determining whether to recurse" do include PuppetSpec::Files before do @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat") @fileset = Puppet::FileServing::Fileset.new(@path) end it "should always recurse if :recurse is set to 'true' and with infinite recursion" do @fileset.recurse = true @fileset.recurselimit = :infinite @fileset.recurse?(0).should be_true end it "should never recurse if :recurse is set to 'false'" do @fileset.recurse = false @fileset.recurse?(-1).should be_false end it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is less than that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(0).should be_true end it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is equal to that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(1).should be_true end it "should not recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is greater than that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(2).should be_false end end describe Puppet::FileServing::Fileset, " when recursing" do include PuppetSpec::Files before do @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @dirstat = stub 'dirstat', :directory? => true @filestat = stub 'filestat', :directory? => false end def mock_dir_structure(path, stat_method = :lstat) File.stubs(stat_method).with(path).returns(@dirstat) Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) # Keep track of the files we're stubbing. @files = %w{.} %w{one two .svn CVS}.each do |subdir| @files << subdir # relative path subpath = File.join(path, subdir) File.stubs(stat_method).with(subpath).returns(@dirstat) Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) %w{file1 file2 .svn CVS}.each do |file| @files << File.join(subdir, file) # relative path File.stubs(stat_method).with(File.join(subpath, file)).returns(@filestat) end end end it "should recurse through the whole file tree if :recurse is set to 'true'" do mock_dir_structure(@path) @fileset.stubs(:recurse?).returns(true) @fileset.files.sort.should == @files.sort end it "should not recurse if :recurse is set to 'false'" do mock_dir_structure(@path) @fileset.stubs(:recurse?).returns(false) @fileset.files.should == %w{.} end # It seems like I should stub :recurse? here, or that I shouldn't stub the # examples above, but... it "should recurse to the level set if :recurselimit is set to an integer" do mock_dir_structure(@path) @fileset.recurse = true @fileset.recurselimit = 1 @fileset.files.should == %w{. one two .svn CVS} end it "should ignore the '.' and '..' directories in subdirectories" do mock_dir_structure(@path) @fileset.recurse = true @fileset.files.sort.should == @files.sort end it "should function if the :ignore value provided is nil" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = nil lambda { @fileset.files }.should_not raise_error end it "should ignore files that match a single pattern in the ignore list" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = ".svn" @fileset.files.find { |file| file.include?(".svn") }.should be_nil end it "should ignore files that match any of multiple patterns in the ignore list" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = %w{.svn CVS} @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil end it "should use File.stat if :links is set to :follow" do mock_dir_structure(@path, :stat) @fileset.recurse = true @fileset.links = :follow @fileset.files.sort.should == @files.sort end it "should use File.lstat if :links is set to :manage" do mock_dir_structure(@path, :lstat) @fileset.recurse = true @fileset.links = :manage @fileset.files.sort.should == @files.sort end it "should succeed when paths have regexp significant characters" do @path = make_absolute("/my/path/rV1x2DafFr0R6tGG+1bbk++++TM") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) mock_dir_structure(@path) @fileset.recurse = true @fileset.files.sort.should == @files.sort end end describe Puppet::FileServing::Fileset, " when following links that point to missing files" do include PuppetSpec::Files before do @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @fileset.links = :follow @fileset.recurse = true @stat = stub 'stat', :directory? => true File.expects(:stat).with(@path).returns(@stat) File.expects(:stat).with(File.join(@path, "mylink")).raises(Errno::ENOENT) Dir.stubs(:entries).with(@path).returns(["mylink"]) end it "should not fail" do proc { @fileset.files }.should_not raise_error end it "should still manage the link" do @fileset.files.sort.should == %w{. mylink}.sort end end describe Puppet::FileServing::Fileset, " when ignoring" do include PuppetSpec::Files before do @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) end it "should use ruby's globbing to determine what files should be ignored" do @fileset.ignore = ".svn" File.expects(:fnmatch?).with(".svn", "my_file") @fileset.ignore?("my_file") end it "should ignore files whose paths match a single provided ignore value" do @fileset.ignore = ".svn" File.stubs(:fnmatch?).with(".svn", "my_file").returns true @fileset.ignore?("my_file").should be_true end it "should ignore files whose paths match any of multiple provided ignore values" do @fileset.ignore = [".svn", "CVS"] File.stubs(:fnmatch?).with(".svn", "my_file").returns false File.stubs(:fnmatch?).with("CVS", "my_file").returns true @fileset.ignore?("my_file").should be_true end end describe Puppet::FileServing::Fileset, "when merging other filesets" do include PuppetSpec::Files before do @paths = [make_absolute("/first/path"), make_absolute("/second/path"), make_absolute("/third/path")] File.stubs(:lstat).returns stub("stat", :directory? => false) @filesets = @paths.collect do |path| File.stubs(:lstat).with(path).returns stub("stat", :directory? => true) Puppet::FileServing::Fileset.new(path, :recurse => true) end Dir.stubs(:entries).returns [] end it "should return a hash of all files in each fileset with the value being the base path" do Dir.expects(:entries).with(make_absolute("/first/path")).returns(%w{one uno}) Dir.expects(:entries).with(make_absolute("/second/path")).returns(%w{two dos}) Dir.expects(:entries).with(make_absolute("/third/path")).returns(%w{three tres}) Puppet::FileServing::Fileset.merge(*@filesets).should == { "." => make_absolute("/first/path"), "one" => make_absolute("/first/path"), "uno" => make_absolute("/first/path"), "two" => make_absolute("/second/path"), "dos" => make_absolute("/second/path"), "three" => make_absolute("/third/path"), "tres" => make_absolute("/third/path"), } end it "should include the base directory from the first fileset" do Dir.expects(:entries).with(make_absolute("/first/path")).returns(%w{one}) Dir.expects(:entries).with(make_absolute("/second/path")).returns(%w{two}) Puppet::FileServing::Fileset.merge(*@filesets)["."].should == make_absolute("/first/path") end it "should use the base path of the first found file when relative file paths conflict" do Dir.expects(:entries).with(make_absolute("/first/path")).returns(%w{one}) Dir.expects(:entries).with(make_absolute("/second/path")).returns(%w{one}) Puppet::FileServing::Fileset.merge(*@filesets)["one"].should == make_absolute("/first/path") end end diff --git a/spec/unit/file_serving/indirection_hooks_spec.rb b/spec/unit/file_serving/indirection_hooks_spec.rb index c194c0931..86cad25cc 100755 --- a/spec/unit/file_serving/indirection_hooks_spec.rb +++ b/spec/unit/file_serving/indirection_hooks_spec.rb @@ -1,58 +1,58 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/indirection_hooks' describe Puppet::FileServing::IndirectionHooks do before do @object = Object.new @object.extend(Puppet::FileServing::IndirectionHooks) @request = stub 'request', :key => "mymod/myfile", :options => {:node => "whatever"}, :server => nil, :protocol => nil end describe "when being used to select termini" do it "should return :file if the request key is fully qualified" do @request.expects(:key).returns File.expand_path('/foo') @object.select_terminus(@request).should == :file end it "should return :file if the URI protocol is set to 'file'" do @request.expects(:protocol).returns "file" @object.select_terminus(@request).should == :file end it "should fail when a protocol other than :puppet or :file is used" do @request.stubs(:protocol).returns "http" proc { @object.select_terminus(@request) }.should raise_error(ArgumentError) end describe "and the protocol is 'puppet'" do before do @request.stubs(:protocol).returns "puppet" end it "should choose :rest when a server is specified" do @request.stubs(:protocol).returns "puppet" @request.expects(:server).returns "foo" @object.select_terminus(@request).should == :rest end # This is so a given file location works when bootstrapping with no server. it "should choose :rest when default_file_terminus is rest" do @request.stubs(:protocol).returns "puppet" Puppet[:server] = 'localhost' @object.select_terminus(@request).should == :rest end it "should choose :file_server when default_file_terminus is file_server and no server is specified on the request" do modules = mock 'modules' @request.expects(:protocol).returns "puppet" @request.expects(:server).returns nil Puppet[:default_file_terminus] = 'file_server' @object.select_terminus(@request).should == :file_server end end end end diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb index 4bc061b47..a22cb61fc 100755 --- a/spec/unit/file_serving/metadata_spec.rb +++ b/spec/unit/file_serving/metadata_spec.rb @@ -1,331 +1,331 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/metadata' describe Puppet::FileServing::Metadata do let(:foobar) { File.expand_path('/foo/bar') } it "should should be a subclass of Base" do Puppet::FileServing::Metadata.superclass.should equal(Puppet::FileServing::Base) end it "should indirect file_metadata" do Puppet::FileServing::Metadata.indirection.name.should == :file_metadata end it "should should include the IndirectionHooks module in its indirection" do Puppet::FileServing::Metadata.indirection.singleton_class.included_modules.should include(Puppet::FileServing::IndirectionHooks) end it "should have a method that triggers attribute collection" do Puppet::FileServing::Metadata.new(foobar).should respond_to(:collect) end it "should support pson serialization" do Puppet::FileServing::Metadata.new(foobar).should respond_to(:to_pson) end it "should support to_pson_data_hash" do Puppet::FileServing::Metadata.new(foobar).should respond_to(:to_pson_data_hash) end it "should support pson deserialization" do Puppet::FileServing::Metadata.should respond_to(:from_pson) end describe "when serializing" do let(:metadata) { Puppet::FileServing::Metadata.new(foobar) } it "should perform pson serialization by calling to_pson on it's pson_data_hash" do pdh = mock "data hash" pdh_as_pson = mock "data as pson" metadata.expects(:to_pson_data_hash).returns pdh pdh.expects(:to_pson).returns pdh_as_pson metadata.to_pson.should == pdh_as_pson end it "should serialize as FileMetadata" do metadata.to_pson_data_hash['document_type'].should == "FileMetadata" end it "the data should include the path, relative_path, links, owner, group, mode, checksum, type, and destination" do metadata.to_pson_data_hash['data'].keys.sort.should == %w{ path relative_path links owner group mode checksum type destination }.sort end it "should pass the path in the hash verbatum" do metadata.to_pson_data_hash['data']['path'] == metadata.path end it "should pass the relative_path in the hash verbatum" do metadata.to_pson_data_hash['data']['relative_path'] == metadata.relative_path end it "should pass the links in the hash verbatum" do metadata.to_pson_data_hash['data']['links'] == metadata.links end it "should pass the path owner in the hash verbatum" do metadata.to_pson_data_hash['data']['owner'] == metadata.owner end it "should pass the group in the hash verbatum" do metadata.to_pson_data_hash['data']['group'] == metadata.group end it "should pass the mode in the hash verbatum" do metadata.to_pson_data_hash['data']['mode'] == metadata.mode end it "should pass the ftype in the hash verbatum as the 'type'" do metadata.to_pson_data_hash['data']['type'] == metadata.ftype end it "should pass the destination verbatum" do metadata.to_pson_data_hash['data']['destination'] == metadata.destination end it "should pass the checksum in the hash as a nested hash" do metadata.to_pson_data_hash['data']['checksum'].should be_is_a(Hash) end it "should pass the checksum_type in the hash verbatum as the checksum's type" do metadata.to_pson_data_hash['data']['checksum']['type'] == metadata.checksum_type end it "should pass the checksum in the hash verbatum as the checksum's value" do metadata.to_pson_data_hash['data']['checksum']['value'] == metadata.checksum end end end describe Puppet::FileServing::Metadata do include PuppetSpec::Files shared_examples_for "metadata collector" do let(:metadata) do data = described_class.new(path) data.collect data end describe "when collecting attributes" do describe "when managing files" do let(:path) { tmpfile('file_serving_metadata') } before :each do FileUtils.touch(path) end it "should set the owner to the file's current owner" do metadata.owner.should == owner end it "should set the group to the file's current group" do metadata.group.should == group end it "should set the mode to the file's masked mode" do set_mode(33261, path) metadata.mode.should == 0755 end describe "#checksum" do let(:checksum) { Digest::MD5.hexdigest("some content\n") } before :each do File.open(path, "wb") {|f| f.print("some content\n")} end it "should default to a checksum of type MD5 with the file's current checksum" do metadata.checksum.should == "{md5}#{checksum}" end it "should give a mtime checksum when checksum_type is set" do time = Time.now metadata.checksum_type = "mtime" metadata.expects(:mtime_file).returns(@time) metadata.collect metadata.checksum.should == "{mtime}#{@time}" end end end describe "when managing directories" do let(:path) { tmpdir('file_serving_metadata_dir') } let(:time) { Time.now } before :each do metadata.expects(:ctime_file).returns(time) end it "should only use checksums of type 'ctime' for directories" do metadata.collect metadata.checksum.should == "{ctime}#{time}" end it "should only use checksums of type 'ctime' for directories even if checksum_type set" do metadata.checksum_type = "mtime" metadata.expects(:mtime_file).never metadata.collect metadata.checksum.should == "{ctime}#{time}" end end describe "when managing links", :unless => Puppet.features.microsoft_windows? do # 'path' is a link that points to 'target' let(:path) { tmpfile('file_serving_metadata_link') } let(:target) { tmpfile('file_serving_metadata_target') } let(:checksum) { Digest::MD5.hexdigest("some content\n") } let(:fmode) { File.lstat(path).mode & 0777 } before :each do File.open(target, "wb") {|f| f.print("some content\n")} set_mode(0644, target) FileUtils.symlink(target, path) end it "should read links instead of returning their checksums" do metadata.destination.should == target end end end describe Puppet::FileServing::Metadata, " when finding the file to use for setting attributes" do let(:path) { tmpfile('file_serving_metadata_find_file') } before :each do File.open(path, "wb") {|f| f.print("some content\n")} set_mode(0755, path) end it "should accept a base path to which the file should be relative" do dir = tmpdir('metadata_dir') metadata = described_class.new(dir) metadata.relative_path = 'relative_path' FileUtils.touch(metadata.full_path) metadata.collect end it "should use the set base path if one is not provided" do metadata.collect end it "should raise an exception if the file does not exist" do File.delete(path) proc { metadata.collect}.should raise_error(Errno::ENOENT) end end end describe "on POSIX systems", :if => Puppet.features.posix? do let(:owner) {10} let(:group) {20} before :each do File::Stat.any_instance.stubs(:uid).returns owner File::Stat.any_instance.stubs(:gid).returns group end it_should_behave_like "metadata collector" def set_mode(mode, path) File.chmod(mode, path) end end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do let(:owner) {'S-1-1-50'} let(:group) {'S-1-1-51'} before :each do require 'puppet/util/windows/security' Puppet::Util::Windows::Security.stubs(:get_owner).returns owner Puppet::Util::Windows::Security.stubs(:get_group).returns group end it_should_behave_like "metadata collector" describe "if ACL metadata cannot be collected" do let(:path) { tmpdir('file_serving_metadata_acl') } let(:metadata) do data = described_class.new(path) data.collect data end it "should default owner" do Puppet::Util::Windows::Security.stubs(:get_owner).returns nil metadata.owner.should == 'S-1-5-32-544' end it "should default group" do Puppet::Util::Windows::Security.stubs(:get_group).returns nil metadata.group.should == 'S-1-0-0' end it "should default mode" do Puppet::Util::Windows::Security.stubs(:get_mode).returns nil metadata.mode.should == 0644 end end def set_mode(mode, path) Puppet::Util::Windows::Security.set_mode(mode, path) end end end describe Puppet::FileServing::Metadata, " when pointing to a link", :unless => Puppet.features.microsoft_windows? do describe "when links are managed" do before do @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage) File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755) File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path" @checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed. @file.stubs(:md5_file).returns(@checksum) # end it "should store the destination of the link in :destination if links are :manage" do @file.collect @file.destination.should == "/some/other/path" end pending "should not collect the checksum if links are :manage" do # We'd like this to be true, but we need to always collect the checksum because in the server/client/server round trip we lose the distintion between manage and follow. @file.collect @file.checksum.should be_nil end it "should collect the checksum if links are :manage" do # see pending note above @file.collect @file.checksum.should == "{md5}#{@checksum}" end end describe "when links are followed" do before do @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :follow) File.expects(:stat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755) File.expects(:readlink).with("/base/path/my/file").never @checksum = Digest::MD5.hexdigest("some content\n") @file.stubs(:md5_file).returns(@checksum) end it "should not store the destination of the link in :destination if links are :follow" do @file.collect @file.destination.should be_nil end it "should collect the checksum if links are :follow" do @file.collect @file.checksum.should == "{md5}#{@checksum}" end end end diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb index 1ee004c10..49eb5fbd2 100755 --- a/spec/unit/file_serving/mount/file_spec.rb +++ b/spec/unit/file_serving/mount/file_spec.rb @@ -1,189 +1,189 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/mount/file' module FileServingMountTesting def stub_facter(hostname) Facter.stubs(:value).with("hostname").returns(hostname.sub(/\..+/, '')) Facter.stubs(:value).with("domain").returns(hostname.sub(/^[^.]+\./, '')) end end describe Puppet::FileServing::Mount::File do it "should be invalid if it does not have a path" do lambda { Puppet::FileServing::Mount::File.new("foo").validate }.should raise_error(ArgumentError) end it "should be valid if it has a path" do FileTest.stubs(:directory?).returns true FileTest.stubs(:readable?).returns true mount = Puppet::FileServing::Mount::File.new("foo") mount.path = "/foo" lambda { mount.validate }.should_not raise_error(ArgumentError) end describe "when setting the path" do before do @mount = Puppet::FileServing::Mount::File.new("test") @dir = "/this/path/does/not/exist" end it "should fail if the path is not a directory" do FileTest.expects(:directory?).returns(false) proc { @mount.path = @dir }.should raise_error(ArgumentError) end it "should fail if the path is not readable" do FileTest.expects(:directory?).returns(true) FileTest.expects(:readable?).returns(false) proc { @mount.path = @dir }.should raise_error(ArgumentError) end end describe "when substituting hostnames and ip addresses into file paths" do include FileServingMountTesting before do FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @host = "host.domain.com" end after :each do Puppet::FileServing::Mount::File.instance_variable_set(:@localmap, nil) end it "should replace incidences of %h in the path with the client's short name" do @mount.path = "/dir/%h/yay" @mount.path(@host).should == "/dir/host/yay" end it "should replace incidences of %H in the path with the client's fully qualified name" do @mount.path = "/dir/%H/yay" @mount.path(@host).should == "/dir/host.domain.com/yay" end it "should replace incidences of %d in the path with the client's domain name" do @mount.path = "/dir/%d/yay" @mount.path(@host).should == "/dir/domain.com/yay" end it "should perform all necessary replacements" do @mount.path = "/%h/%d/%H" @mount.path(@host).should == "/host/domain.com/host.domain.com" end it "should use local host information if no client data is provided" do stub_facter("myhost.mydomain.com") @mount.path = "/%h/%d/%H" @mount.path.should == "/myhost/mydomain.com/myhost.mydomain.com" end end describe "when determining the complete file path" do include FileServingMountTesting before do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @mount.path = "/mount" stub_facter("myhost.mydomain.com") @host = "host.domain.com" end it "should return nil if the file is absent" do FileTest.stubs(:exist?).returns(false) @mount.complete_path("/my/path", nil).should be_nil end it "should write a log message if the file is absent" do FileTest.stubs(:exist?).returns(false) Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path") @mount.complete_path("/my/path", nil) end it "should return the file path if the file is present" do FileTest.stubs(:exist?).with("/my/path").returns(true) @mount.complete_path("/my/path", nil).should == "/mount/my/path" end it "should treat a nil file name as the path to the mount itself" do FileTest.stubs(:exist?).returns(true) @mount.complete_path(nil, nil).should == "/mount" end it "should use the client host name if provided in the options" do @mount.path = "/mount/%h" @mount.complete_path("/my/path", @host).should == "/mount/host/my/path" end it "should perform replacements on the base path" do @mount.path = "/blah/%h" @mount.complete_path("/my/stuff", @host).should == "/blah/host/my/stuff" end it "should not perform replacements on the per-file path" do @mount.path = "/blah" @mount.complete_path("/%h/stuff", @host).should == "/blah/%h/stuff" end it "should look for files relative to its base directory" do @mount.complete_path("/my/stuff", @host).should == "/mount/my/stuff" end end describe "when finding files" do include FileServingMountTesting before do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @mount.path = "/mount" stub_facter("myhost.mydomain.com") @host = "host.domain.com" @request = stub 'request', :node => "foo" end it "should return the results of the complete file path" do FileTest.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" @mount.find("/my/path", @request).should == "eh" end end describe "when searching for files" do include FileServingMountTesting before do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) FileTest.stubs(:readable?).returns(true) @mount = Puppet::FileServing::Mount::File.new("test") @mount.path = "/mount" stub_facter("myhost.mydomain.com") @host = "host.domain.com" @request = stub 'request', :node => "foo" end it "should return the results of the complete file path as an array" do FileTest.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns "eh" @mount.search("/my/path", @request).should == ["eh"] end it "should return nil if the complete path is nil" do FileTest.stubs(:exist?).returns(false) @mount.expects(:complete_path).with("/my/path", "foo").returns nil @mount.search("/my/path", @request).should be_nil end end end diff --git a/spec/unit/file_serving/mount/modules_spec.rb b/spec/unit/file_serving/mount/modules_spec.rb index 2d582daa2..de5755010 100755 --- a/spec/unit/file_serving/mount/modules_spec.rb +++ b/spec/unit/file_serving/mount/modules_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/mount/modules' describe Puppet::FileServing::Mount::Modules do before do @mount = Puppet::FileServing::Mount::Modules.new("modules") @environment = stub 'environment', :module => nil @request = stub 'request', :environment => @environment end describe "when finding files" do it "should use the provided environment to find the module" do @environment.expects(:module) @mount.find("foo", @request) end it "should treat the first field of the relative path as the module name" do @environment.expects(:module).with("foo") @mount.find("foo/bar/baz", @request) end it "should return nil if the specified module does not exist" do @environment.expects(:module).with("foo").returns nil @mount.find("foo/bar/baz", @request) end it "should return the file path from the module" do mod = mock 'module' mod.expects(:file).with("bar/baz").returns "eh" @environment.expects(:module).with("foo").returns mod @mount.find("foo/bar/baz", @request).should == "eh" end end describe "when searching for files" do it "should use the node's environment to search the module" do @environment.expects(:module) @mount.search("foo", @request) end it "should treat the first field of the relative path as the module name" do @environment.expects(:module).with("foo") @mount.search("foo/bar/baz", @request) end it "should return nil if the specified module does not exist" do @environment.expects(:module).with("foo").returns nil @mount.search("foo/bar/baz", @request) end it "should return the file path as an array from the module" do mod = mock 'module' mod.expects(:file).with("bar/baz").returns "eh" @environment.expects(:module).with("foo").returns mod @mount.search("foo/bar/baz", @request).should == ["eh"] end end end diff --git a/spec/unit/file_serving/mount/plugins_spec.rb b/spec/unit/file_serving/mount/plugins_spec.rb index a5bc0501d..782d9b25b 100755 --- a/spec/unit/file_serving/mount/plugins_spec.rb +++ b/spec/unit/file_serving/mount/plugins_spec.rb @@ -1,73 +1,73 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/mount/plugins' describe Puppet::FileServing::Mount::Plugins do before do @mount = Puppet::FileServing::Mount::Plugins.new("plugins") @environment = stub 'environment', :module => nil @options = { :recurse => true } @request = stub 'request', :environment => @environment, :options => @options end describe "when finding files" do it "should use the provided environment to find the modules" do @environment.expects(:modules).returns [] @mount.find("foo", @request) end it "should return nil if no module can be found with a matching plugin" do mod = mock 'module' mod.stubs(:plugin).with("foo/bar").returns nil @environment.stubs(:modules).returns [mod] @mount.find("foo/bar", @request).should be_nil end it "should return the file path from the module" do mod = mock 'module' mod.stubs(:plugin).with("foo/bar").returns "eh" @environment.stubs(:modules).returns [mod] @mount.find("foo/bar", @request).should == "eh" end end describe "when searching for files" do it "should use the node's environment to find the modules" do @environment.expects(:modules).at_least_once.returns [] @environment.stubs(:modulepath).returns ["/tmp/modules"] @mount.search("foo", @request) end it "should return modulepath if no modules can be found that have plugins" do mod = mock 'module' mod.stubs(:plugins?).returns false @environment.stubs(:modules).returns [] @environment.stubs(:modulepath).returns ["/"] @options.expects(:[]=).with(:recurse, false) @mount.search("foo/bar", @request).should == ["/"] end it "should return nil if no modules can be found that have plugins and modulepath is invalid" do mod = mock 'module' mod.stubs(:plugins?).returns false @environment.stubs(:modules).returns [] @environment.stubs(:modulepath).returns [] @mount.search("foo/bar", @request).should be_nil end it "should return the plugin paths for each module that has plugins" do one = stub 'module', :plugins? => true, :plugin_directory => "/one" two = stub 'module', :plugins? => true, :plugin_directory => "/two" @environment.stubs(:modules).returns [one, two] @mount.search("foo/bar", @request).should == %w{/one /two} end end end diff --git a/spec/unit/file_serving/mount_spec.rb b/spec/unit/file_serving/mount_spec.rb index 5d8e64f82..3ecfe4e1a 100755 --- a/spec/unit/file_serving/mount_spec.rb +++ b/spec/unit/file_serving/mount_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/mount' describe Puppet::FileServing::Mount do it "should use 'mount[$name]' as its string form" do Puppet::FileServing::Mount.new("foo").to_s.should == "mount[foo]" end end describe Puppet::FileServing::Mount, " when initializing" do it "should fail on non-alphanumeric name" do proc { Puppet::FileServing::Mount.new("non alpha") }.should raise_error(ArgumentError) end it "should allow dashes in its name" do Puppet::FileServing::Mount.new("non-alpha").name.should == "non-alpha" end end describe Puppet::FileServing::Mount, " when finding files" do it "should fail" do lambda { Puppet::FileServing::Mount.new("test").find("foo", :one => "two") }.should raise_error(NotImplementedError) end end describe Puppet::FileServing::Mount, " when searching for files" do it "should fail" do lambda { Puppet::FileServing::Mount.new("test").search("foo", :one => "two") }.should raise_error(NotImplementedError) end end diff --git a/spec/unit/file_serving/terminus_helper_spec.rb b/spec/unit/file_serving/terminus_helper_spec.rb index 9fd27d9dc..9cfdf73d6 100755 --- a/spec/unit/file_serving/terminus_helper_spec.rb +++ b/spec/unit/file_serving/terminus_helper_spec.rb @@ -1,94 +1,94 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_serving/terminus_helper' describe Puppet::FileServing::TerminusHelper do before do @helper = Object.new @helper.extend(Puppet::FileServing::TerminusHelper) @model = mock 'model' @helper.stubs(:model).returns(@model) @request = stub 'request', :key => "url", :options => {} @fileset = stub 'fileset', :files => [], :path => "/my/file" Puppet::FileServing::Fileset.stubs(:new).with("/my/file", {}).returns(@fileset) end it "should use a fileset to find paths" do @fileset = stub 'fileset', :files => [], :path => "/my/files" Puppet::FileServing::Fileset.expects(:new).with { |key, options| key == "/my/file" }.returns(@fileset) @helper.path2instances(@request, "/my/file") end it "should support finding across multiple paths by merging the filesets" do first = stub 'fileset', :files => [], :path => "/first/file" Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/first/file" }.returns(first) second = stub 'fileset', :files => [], :path => "/second/file" Puppet::FileServing::Fileset.expects(:new).with { |path, options| path == "/second/file" }.returns(second) Puppet::FileServing::Fileset.expects(:merge).with(first, second).returns({}) @helper.path2instances(@request, "/first/file", "/second/file") end it "should pass the indirection request to the Fileset at initialization" do Puppet::FileServing::Fileset.expects(:new).with { |path, options| options == @request }.returns @fileset @helper.path2instances(@request, "/my/file") end describe "when creating instances" do before do @request.stubs(:key).returns "puppet://host/mount/dir" @one = stub 'one', :links= => nil, :collect => nil @two = stub 'two', :links= => nil, :collect => nil @fileset = stub 'fileset', :files => %w{one two}, :path => "/my/file" Puppet::FileServing::Fileset.stubs(:new).returns(@fileset) end it "should set each returned instance's path to the original path" do @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@one) @model.expects(:new).with { |key, options| key == "/my/file" }.returns(@two) @helper.path2instances(@request, "/my/file") end it "should set each returned instance's relative path to the file-specific path" do @model.expects(:new).with { |key, options| options[:relative_path] == "one" }.returns(@one) @model.expects(:new).with { |key, options| options[:relative_path] == "two" }.returns(@two) @helper.path2instances(@request, "/my/file") end it "should set the links value on each instance if one is provided" do @one.expects(:links=).with :manage @two.expects(:links=).with :manage @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @request.options[:links] = :manage @helper.path2instances(@request, "/my/file") end it "should set the request checksum_type if one is provided" do @one.expects(:checksum_type=).with :test @two.expects(:checksum_type=).with :test @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @request.options[:checksum_type] = :test @helper.path2instances(@request, "/my/file") end it "should collect the instance's attributes" do @one.expects(:collect) @two.expects(:collect) @model.expects(:new).returns(@one) @model.expects(:new).returns(@two) @helper.path2instances(@request, "/my/file") end end end diff --git a/spec/unit/indirector/active_record_spec.rb b/spec/unit/indirector/active_record_spec.rb index 2baef33fe..58f8b4f8e 100755 --- a/spec/unit/indirector/active_record_spec.rb +++ b/spec/unit/indirector/active_record_spec.rb @@ -1,75 +1,75 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' require 'puppet/indirector/active_record' describe Puppet::Indirector::ActiveRecord do before do Puppet::Rails.stubs(:init) Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @active_record_class = class Testing::MyActiveRecord < Puppet::Indirector::ActiveRecord self end @ar_model = mock 'ar_model' @active_record_class.use_ar_model @ar_model @terminus = @active_record_class.new @name = "me" @instance = stub 'instance', :name => @name @request = stub 'request', :key => @name, :instance => @instance end it "should allow declaration of an ActiveRecord model to use" do @active_record_class.use_ar_model "foo" @active_record_class.ar_model.should == "foo" end describe "when initializing" do it "should init Rails" do Puppet::Rails.expects(:init) @active_record_class.new end end describe "when finding an instance" do it "should use the ActiveRecord model to find the instance" do @ar_model.expects(:find_by_name).with(@name) @terminus.find(@request) end it "should return nil if no instance is found" do @ar_model.expects(:find_by_name).with(@name).returns nil @terminus.find(@request).should be_nil end it "should convert the instance to a Puppet object if it is found" do instance = mock 'rails_instance' instance.expects(:to_puppet).returns "mypuppet" @ar_model.expects(:find_by_name).with(@name).returns instance @terminus.find(@request).should == "mypuppet" end end describe "when saving an instance" do it "should use the ActiveRecord model to convert the instance into a Rails object and then save that rails object" do rails_object = mock 'rails_object' @ar_model.expects(:from_puppet).with(@instance).returns rails_object rails_object.expects(:save) @terminus.save(@request) end end end diff --git a/spec/unit/indirector/catalog/active_record_spec.rb b/spec/unit/indirector/catalog/active_record_spec.rb index 030bedc4b..5d4af7be7 100755 --- a/spec/unit/indirector/catalog/active_record_spec.rb +++ b/spec/unit/indirector/catalog/active_record_spec.rb @@ -1,98 +1,98 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "Puppet::Resource::Catalog::ActiveRecord", :if => can_use_scratch_database? do include PuppetSpec::Files require 'puppet/rails' before :each do require 'puppet/indirector/catalog/active_record' setup_scratch_database end let :terminus do Puppet::Resource::Catalog::ActiveRecord.new end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Resource::Catalog::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Resource::Catalog::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end describe "when finding an instance" do it "should return nil" do r = stub 'request', :key => "foo", :options => {:cache_integration_hack => false} terminus.find(r).should be_nil end # This used to make things go to the database, but that is code that is as # dead as a doornail. This just checks we don't blow up unexpectedly, and # can go away after a few releases. --daniel 2012-02-27 it "should always return nil" do r = stub 'request', :key => "foo", :options => {:cache_integration_hack => true} terminus.find(r).should be_nil end end describe "when saving an instance" do let :catalog do Puppet::Resource::Catalog.new("foo") end let :request do Puppet::Indirector::Request.new(:active_record, :save, nil, catalog) end let :node do Puppet::Node.new("foo", :environment => "environment") end before :each do Puppet::Node.indirection.stubs(:find).with("foo").returns(node) end it "should find the Rails host with the same name" do Puppet::Rails::Host.expects(:find_by_name).with("foo") terminus.save(request) end it "should create a new Rails host if none can be found" do Puppet::Rails::Host.find_by_name('foo').should be_nil terminus.save(request) Puppet::Rails::Host.find_by_name('foo').should be_valid end it "should set the catalog vertices as resources on the Rails host instance" do # We need to stub this so we get the same object, not just the same # content, otherwise the expect can't fire. :( host = Puppet::Rails::Host.create!(:name => "foo") Puppet::Rails::Host.expects(:find_by_name).with("foo").returns(host) catalog.expects(:vertices).returns("foo") host.expects(:merge_resources).with("foo") terminus.save(request) end it "should set host ip if we could find a matching node" do node.merge("ipaddress" => "192.168.0.1") terminus.save(request) Puppet::Rails::Host.find_by_name("foo").ip.should == '192.168.0.1' end it "should set host environment if we could find a matching node" do terminus.save(request) Puppet::Rails::Host.find_by_name("foo").environment.should == "environment" end it "should set the last compile time on the host" do now = Time.now terminus.save(request) Puppet::Rails::Host.find_by_name("foo").last_compile.should be_within(1).of(now) end it "should save the Rails host instance" do host = Puppet::Rails::Host.create!(:name => "foo") Puppet::Rails::Host.expects(:find_by_name).with("foo").returns(host) host.expects(:save) terminus.save(request) end end end diff --git a/spec/unit/indirector/catalog/compiler_spec.rb b/spec/unit/indirector/catalog/compiler_spec.rb index 54378aa99..09961293c 100755 --- a/spec/unit/indirector/catalog/compiler_spec.rb +++ b/spec/unit/indirector/catalog/compiler_spec.rb @@ -1,261 +1,261 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/catalog/compiler' require 'puppet/rails' describe Puppet::Resource::Catalog::Compiler do before do require 'puppet/rails' Puppet::Rails.stubs(:init) Facter.stubs(:to_hash).returns({}) Facter.stubs(:value).returns(Facter::Util::Fact.new("something")) end describe "when initializing" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") end it "should gather data about itself" do Puppet::Resource::Catalog::Compiler.new end it "should cache the server metadata and reuse it" do compiler = Puppet::Resource::Catalog::Compiler.new node1 = stub 'node1', :merge => nil node2 = stub 'node2', :merge => nil compiler.stubs(:compile) Puppet::Node.indirection.stubs(:find).with('node1', anything).returns(node1) Puppet::Node.indirection.stubs(:find).with('node2', anything).returns(node2) compiler.find(Puppet::Indirector::Request.new(:catalog, :find, 'node1', nil, :node => 'node1')) compiler.find(Puppet::Indirector::Request.new(:catalog, :find, 'node2', nil, :node => 'node2')) end it "should provide a method for determining if the catalog is networked" do compiler = Puppet::Resource::Catalog::Compiler.new compiler.should respond_to(:networked?) end end describe "when finding catalogs" do before do Facter.stubs(:value).returns("whatever") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = Puppet::Node.new @name @node.stubs(:merge) Puppet::Node.indirection.stubs(:find).returns @node @request = Puppet::Indirector::Request.new(:catalog, :find, @name, nil, :node => @name) end it "should directly use provided nodes" do Puppet::Node.indirection.expects(:find).never @compiler.expects(:compile).with(@node) @request.stubs(:options).returns(:use_node => @node) @compiler.find(@request) end it "should use the authenticated node name if no request key is provided" do @request.stubs(:key).returns(nil) Puppet::Node.indirection.expects(:find).with(@name, anything).returns(@node) @compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should use the provided node name by default" do @request.expects(:key).returns "my_node" Puppet::Node.indirection.expects(:find).with("my_node", anything).returns @node @compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should fail if no node is passed and none can be found" do Puppet::Node.indirection.stubs(:find).with(@name, anything).returns(nil) proc { @compiler.find(@request) }.should raise_error(ArgumentError) end it "should fail intelligently when searching for a node raises an exception" do Puppet::Node.indirection.stubs(:find).with(@name, anything).raises "eh" proc { @compiler.find(@request) }.should raise_error(Puppet::Error) end it "should pass the found node to the compiler for compiling" do Puppet::Node.indirection.expects(:find).with(@name, anything).returns(@node) config = mock 'config' Puppet::Parser::Compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should extract and save any facts from the request" do Puppet::Node.indirection.expects(:find).with(@name, anything).returns @node @compiler.expects(:extract_facts_from_request).with(@request) Puppet::Parser::Compiler.stubs(:compile) @compiler.find(@request) end it "should return the results of compiling as the catalog" do Puppet::Node.indirection.stubs(:find).returns(@node) config = mock 'config' result = mock 'result' Puppet::Parser::Compiler.expects(:compile).returns result @compiler.find(@request).should equal(result) end it "should benchmark the compile process" do Puppet::Node.indirection.stubs(:find).returns(@node) @compiler.stubs(:networked?).returns(true) @compiler.expects(:benchmark).with do |level, message| level == :notice and message =~ /^Compiled catalog/ end Puppet::Parser::Compiler.stubs(:compile) @compiler.find(@request) end it "should log the benchmark result" do Puppet::Node.indirection.stubs(:find).returns(@node) @compiler.stubs(:networked?).returns(true) Puppet::Parser::Compiler.stubs(:compile) Puppet.expects(:notice).with { |msg| msg =~ /Compiled catalog/ } @compiler.find(@request) end end describe "when extracting facts from the request" do before do Facter.stubs(:value).returns "something" @compiler = Puppet::Resource::Catalog::Compiler.new @request = stub 'request', :options => {} @facts = Puppet::Node::Facts.new('hostname', "fact" => "value", "architecture" => "i386") Puppet::Node::Facts.indirection.stubs(:save).returns(nil) end it "should do nothing if no facts are provided" do Puppet::Node::Facts.indirection.expects(:convert_from).never @request.options[:facts] = nil @compiler.extract_facts_from_request(@request) end it "should use the Facts class to deserialize the provided facts and update the timestamp" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts @facts.timestamp = Time.parse('2010-11-01') @now = Time.parse('2010-11-02') Time.expects(:now).returns(@now) @compiler.extract_facts_from_request(@request) @facts.timestamp.should == @now end it "should use the provided fact format" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).with { |format, text| format == "foo" }.returns @facts @compiler.extract_facts_from_request(@request) end it "should convert the facts into a fact instance and save it" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts Puppet::Node::Facts.indirection.expects(:save).with(@facts) @compiler.extract_facts_from_request(@request) end end describe "when finding nodes" do before do Facter.stubs(:value).returns("whatever") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = Puppet::Indirector::Request.new(:catalog, :find, @name, nil) @compiler.stubs(:compile) end it "should look node information up via the Node class with the provided key" do @node.stubs :merge Puppet::Node.indirection.expects(:find).with(@name, anything).returns(@node) @compiler.find(@request) end end describe "after finding nodes" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = Puppet::Indirector::Request.new(:catalog, :find, @name, nil) @compiler.stubs(:compile) Puppet::Node.indirection.stubs(:find).with(@name, anything).returns(@node) end it "should add the server's Puppet version to the node's parameters as 'serverversion'" do @node.expects(:merge).with { |args| args["serverversion"] == "1" } @compiler.find(@request) end it "should add the server's fqdn to the node's parameters as 'servername'" do @node.expects(:merge).with { |args| args["servername"] == "my.server.com" } @compiler.find(@request) end it "should add the server's IP address to the node's parameters as 'serverip'" do @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" } @compiler.find(@request) end end describe "when filtering resources" do before :each do Facter.stubs(:value) @compiler = Puppet::Resource::Catalog::Compiler.new @catalog = stub_everything 'catalog' @catalog.stubs(:respond_to?).with(:filter).returns(true) end it "should delegate to the catalog instance filtering" do @catalog.expects(:filter) @compiler.filter(@catalog) end it "should filter out virtual resources" do resource = mock 'resource', :virtual? => true @catalog.stubs(:filter).yields(resource) @compiler.filter(@catalog) end it "should return the same catalog if it doesn't support filtering" do @catalog.stubs(:respond_to?).with(:filter).returns(false) @compiler.filter(@catalog).should == @catalog end it "should return the filtered catalog" do catalog = stub 'filtered catalog' @catalog.stubs(:filter).returns(catalog) @compiler.filter(@catalog).should == catalog end end end diff --git a/spec/unit/indirector/catalog/queue_spec.rb b/spec/unit/indirector/catalog/queue_spec.rb index d396ad897..1d2225096 100755 --- a/spec/unit/indirector/catalog/queue_spec.rb +++ b/spec/unit/indirector/catalog/queue_spec.rb @@ -1,19 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/catalog/queue' describe Puppet::Resource::Catalog::Queue do it 'should be a subclass of the Queue terminus' do Puppet::Resource::Catalog::Queue.superclass.should equal(Puppet::Indirector::Queue) end it 'should be registered with the catalog store indirection' do indirection = Puppet::Indirector::Indirection.instance(:catalog) Puppet::Resource::Catalog::Queue.indirection.should equal(indirection) end it 'shall be dubbed ":queue"' do Puppet::Resource::Catalog::Queue.name.should == :queue end end diff --git a/spec/unit/indirector/catalog/rest_spec.rb b/spec/unit/indirector/catalog/rest_spec.rb index 3e674dde9..f980f5046 100755 --- a/spec/unit/indirector/catalog/rest_spec.rb +++ b/spec/unit/indirector/catalog/rest_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/catalog/rest' describe Puppet::Resource::Catalog::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Resource::Catalog::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/catalog/store_configs_spec.rb b/spec/unit/indirector/catalog/store_configs_spec.rb index 623ae2410..8dd711387 100755 --- a/spec/unit/indirector/catalog/store_configs_spec.rb +++ b/spec/unit/indirector/catalog/store_configs_spec.rb @@ -1,17 +1,17 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node' require 'puppet/indirector/memory' require 'puppet/indirector/catalog/store_configs' class Puppet::Resource::Catalog::StoreConfigsTesting < Puppet::Indirector::Memory end describe Puppet::Resource::Catalog::StoreConfigs do after :each do Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet::Resource::Catalog.indirection.cache_class = nil end it_should_behave_like "a StoreConfigs terminus" end diff --git a/spec/unit/indirector/catalog/yaml_spec.rb b/spec/unit/indirector/catalog/yaml_spec.rb index ddaa173c6..bb2332854 100755 --- a/spec/unit/indirector/catalog/yaml_spec.rb +++ b/spec/unit/indirector/catalog/yaml_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/catalog' require 'puppet/indirector/catalog/yaml' describe Puppet::Resource::Catalog::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Resource::Catalog::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Resource::Catalog::Yaml.doc.should_not be_nil end it "should be registered with the catalog store indirection" do indirection = Puppet::Indirector::Indirection.instance(:catalog) Puppet::Resource::Catalog::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do Puppet::Resource::Catalog::Yaml.name.should == :yaml end end diff --git a/spec/unit/indirector/certificate/ca_spec.rb b/spec/unit/indirector/certificate/ca_spec.rb index 25bd4dcbf..3fcf5d13b 100755 --- a/spec/unit/indirector/certificate/ca_spec.rb +++ b/spec/unit/indirector/certificate/ca_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate/ca' describe Puppet::SSL::Certificate::Ca do it "should have documentation" do Puppet::SSL::Certificate::Ca.doc.should be_instance_of(String) end it "should use the :signeddir as the collection directory" do Puppet.settings.expects(:value).with(:signeddir).returns "/cert/dir" Puppet::SSL::Certificate::Ca.collection_directory.should == "/cert/dir" end it "should store the ca certificate at the :cacert location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" file = Puppet::SSL::Certificate::Ca.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/cert" end end diff --git a/spec/unit/indirector/certificate/file_spec.rb b/spec/unit/indirector/certificate/file_spec.rb index 67b83589e..b98ac586d 100755 --- a/spec/unit/indirector/certificate/file_spec.rb +++ b/spec/unit/indirector/certificate/file_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate/file' describe Puppet::SSL::Certificate::File do it "should have documentation" do Puppet::SSL::Certificate::File.doc.should be_instance_of(String) end it "should use the :certdir as the collection directory" do Puppet.settings.expects(:value).with(:certdir).returns "/cert/dir" Puppet::SSL::Certificate::File.collection_directory.should == "/cert/dir" end it "should store the ca certificate at the :localcacert location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert" file = Puppet::SSL::Certificate::File.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/cert" end end diff --git a/spec/unit/indirector/certificate/rest_spec.rb b/spec/unit/indirector/certificate/rest_spec.rb index a409727eb..08a20c68c 100755 --- a/spec/unit/indirector/certificate/rest_spec.rb +++ b/spec/unit/indirector/certificate/rest_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate/rest' describe Puppet::SSL::Certificate::Rest do before do @searcher = Puppet::SSL::Certificate::Rest.new end it "should be a sublcass of Puppet::Indirector::REST" do Puppet::SSL::Certificate::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should set server_setting to :ca_server" do Puppet::SSL::Certificate::Rest.server_setting.should == :ca_server end it "should set port_setting to :ca_port" do Puppet::SSL::Certificate::Rest.port_setting.should == :ca_port end it "should use the :ca SRV service" do Puppet::SSL::Certificate::Rest.srv_service.should == :ca end it "should make sure found certificates have their names set to the search string" do terminus = Puppet::SSL::Certificate::Rest.new # This has 'boo.com' in the CN cert_string = "-----BEGIN CERTIFICATE----- MIICPzCCAaigAwIBAgIBBDANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtidWNr eS5sb2NhbDAeFw0wOTA5MTcxNzI1MzJaFw0xNDA5MTYxNzI1MzJaMBIxEDAOBgNV BAMMB2Jvby5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKG9B+DkTCNh F5xHchNDfnbC9NzWKM600oxrr84pgUVAG6B2wAZcdfoEtXszhsY9Jzpwqkvxk4Mx AbYqo9+TCi4UoiH6e+vAKOOJD3DHrlf+/RW4hGtyaI41DBhf4+B4/oFz5PH9mvKe NSfHFI/yPW+1IXYjxKLQNwF9E7q3JbnzAgMBAAGjgaAwgZ0wOAYJYIZIAYb4QgEN BCsWKVB1cHBldCBSdWJ5L09wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMAwG A1UdEwEB/wQCMAAwHQYDVR0OBBYEFJOxEUeyf4cNOBmf9zIaE1JTuNdLMAsGA1Ud DwQEAwIFoDAnBgNVHSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwME MA0GCSqGSIb3DQEBBQUAA4GBAFTJxKprMg6tfhGnvEvURPmlJrINn9c2b5Y4AGYp tO86PFFkWw/EIJvvJzbj3s+Butr+eUo//+f1xxX7UCwwGqGxKqjtVS219oU/wkx8 h7rW4Xk7MrLl0auSS1p4wLcAMm+ZImf94+j8Cj+tkr8eGozZceRV13b8+EkdaE3S rn/G -----END CERTIFICATE----- " network = stub 'network' terminus.stubs(:network).returns network response = stub 'response', :code => "200", :body => cert_string response.stubs(:[]).with('content-type').returns "text/plain" response.stubs(:[]).with('content-encoding') network.stubs(:verify_callback=) network.expects(:get).returns response request = Puppet::Indirector::Request.new(:certificate, :find, "foo.com", nil) result = terminus.find(request) result.should_not be_nil result.name.should == "foo.com" end end diff --git a/spec/unit/indirector/certificate_request/ca_spec.rb b/spec/unit/indirector/certificate_request/ca_spec.rb index e5443a26d..f43a2ee71 100755 --- a/spec/unit/indirector/certificate_request/ca_spec.rb +++ b/spec/unit/indirector/certificate_request/ca_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/host' require 'puppet/indirector/certificate_request/ca' describe Puppet::SSL::CertificateRequest::Ca, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before :each do Puppet[:ssldir] = tmpdir('ssl') Puppet::SSL::Host.ca_location = :local Puppet[:localcacert] = Puppet[:cacert] @ca = Puppet::SSL::CertificateAuthority.new end after :all do Puppet::SSL::Host.ca_location = :none end it "should have documentation" do Puppet::SSL::CertificateRequest::Ca.doc.should be_instance_of(String) end it "should use the :csrdir as the collection directory" do Puppet.settings.expects(:value).with(:csrdir).returns "/request/dir" Puppet::SSL::CertificateRequest::Ca.collection_directory.should == "/request/dir" end it "should overwrite the previous certificate request if allow_duplicate_certs is true" do Puppet[:allow_duplicate_certs] = true host = Puppet::SSL::Host.new("foo") host.generate_certificate_request @ca.sign(host.name) Puppet::SSL::Host.indirection.find("foo").generate_certificate_request Puppet::SSL::Certificate.indirection.find("foo").name.should == "foo" Puppet::SSL::CertificateRequest.indirection.find("foo").name.should == "foo" Puppet::SSL::Host.indirection.find("foo").state.should == "requested" end it "should reject a new certificate request if allow_duplicate_certs is false" do Puppet[:allow_duplicate_certs] = false host = Puppet::SSL::Host.new("bar") host.generate_certificate_request @ca.sign(host.name) expect { Puppet::SSL::Host.indirection.find("bar").generate_certificate_request }.should raise_error(/ignoring certificate request/) Puppet::SSL::Certificate.indirection.find("bar").name.should == "bar" Puppet::SSL::CertificateRequest.indirection.find("bar").should be_nil Puppet::SSL::Host.indirection.find("bar").state.should == "signed" end end diff --git a/spec/unit/indirector/certificate_request/file_spec.rb b/spec/unit/indirector/certificate_request/file_spec.rb index 654cd23f8..406a8afc9 100755 --- a/spec/unit/indirector/certificate_request/file_spec.rb +++ b/spec/unit/indirector/certificate_request/file_spec.rb @@ -1,15 +1,15 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate_request/file' describe Puppet::SSL::CertificateRequest::File do it "should have documentation" do Puppet::SSL::CertificateRequest::File.doc.should be_instance_of(String) end it "should use the :requestdir as the collection directory" do Puppet.settings.expects(:value).with(:requestdir).returns "/request/dir" Puppet::SSL::CertificateRequest::File.collection_directory.should == "/request/dir" end end diff --git a/spec/unit/indirector/certificate_request/rest_spec.rb b/spec/unit/indirector/certificate_request/rest_spec.rb index 398b91b84..292836728 100755 --- a/spec/unit/indirector/certificate_request/rest_spec.rb +++ b/spec/unit/indirector/certificate_request/rest_spec.rb @@ -1,22 +1,22 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate_request/rest' describe Puppet::SSL::CertificateRequest::Rest do before do @searcher = Puppet::SSL::CertificateRequest::Rest.new end it "should be a sublcass of Puppet::Indirector::REST" do Puppet::SSL::CertificateRequest::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should set server_setting to :ca_server" do Puppet::SSL::CertificateRequest::Rest.server_setting.should == :ca_server end it "should set port_setting to :ca_port" do Puppet::SSL::CertificateRequest::Rest.port_setting.should == :ca_port end end diff --git a/spec/unit/indirector/certificate_revocation_list/ca_spec.rb b/spec/unit/indirector/certificate_revocation_list/ca_spec.rb index 005b5aa1d..85fb72916 100755 --- a/spec/unit/indirector/certificate_revocation_list/ca_spec.rb +++ b/spec/unit/indirector/certificate_revocation_list/ca_spec.rb @@ -1,17 +1,17 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate_revocation_list/ca' describe Puppet::SSL::CertificateRevocationList::Ca do it "should have documentation" do Puppet::SSL::CertificateRevocationList::Ca.doc.should be_instance_of(String) end it "should use the :cacrl setting as the crl location" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).with(:cacrl).returns "/request/dir" Puppet::SSL::CertificateRevocationList::Ca.new.path("whatever").should == "/request/dir" end end diff --git a/spec/unit/indirector/certificate_revocation_list/file_spec.rb b/spec/unit/indirector/certificate_revocation_list/file_spec.rb index 380290079..d2f3325f8 100755 --- a/spec/unit/indirector/certificate_revocation_list/file_spec.rb +++ b/spec/unit/indirector/certificate_revocation_list/file_spec.rb @@ -1,16 +1,16 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate_revocation_list/file' describe Puppet::SSL::CertificateRevocationList::File do it "should have documentation" do Puppet::SSL::CertificateRevocationList::File.doc.should be_instance_of(String) end it "should always store the file to :hostcrl location" do Puppet.settings.expects(:value).with(:hostcrl).returns "/host/crl" Puppet.settings.stubs(:use) Puppet::SSL::CertificateRevocationList::File.file_location.should == "/host/crl" end end diff --git a/spec/unit/indirector/certificate_revocation_list/rest_spec.rb b/spec/unit/indirector/certificate_revocation_list/rest_spec.rb index 238ba75ff..e2c317105 100755 --- a/spec/unit/indirector/certificate_revocation_list/rest_spec.rb +++ b/spec/unit/indirector/certificate_revocation_list/rest_spec.rb @@ -1,22 +1,22 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/certificate_revocation_list/rest' describe Puppet::SSL::CertificateRevocationList::Rest do before do @searcher = Puppet::SSL::CertificateRevocationList::Rest.new end it "should be a sublcass of Puppet::Indirector::REST" do Puppet::SSL::CertificateRevocationList::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should set server_setting to :ca_server" do Puppet::SSL::CertificateRevocationList::Rest.server_setting.should == :ca_server end it "should set port_setting to :ca_port" do Puppet::SSL::CertificateRevocationList::Rest.port_setting.should == :ca_port end end diff --git a/spec/unit/indirector/certificate_status/file_spec.rb b/spec/unit/indirector/certificate_status/file_spec.rb index c26f6820f..acb4cf2d2 100755 --- a/spec/unit/indirector/certificate_status/file_spec.rb +++ b/spec/unit/indirector/certificate_status/file_spec.rb @@ -1,203 +1,203 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/host' require 'puppet/indirector/certificate_status' require 'tempfile' describe "Puppet::Indirector::CertificateStatus::File" do include PuppetSpec::Files before :all do Puppet::SSL::Host.configure_indirection(:file) end before do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true @terminus = Puppet::SSL::Host.indirection.terminus(:file) @tmpdir = tmpdir("certificate_status_ca_testing") Puppet[:confdir] = @tmpdir Puppet[:vardir] = @tmpdir # localcacert is where each client stores the CA certificate # cacert is where the master stores the CA certificate # Since we need to play the role of both for testing we need them to be the same and exist Puppet[:cacert] = Puppet[:localcacert] end def generate_csr(host) host.generate_key csr = Puppet::SSL::CertificateRequest.new(host.name) csr.generate(host.key.content) Puppet::SSL::CertificateRequest.indirection.save(csr) end def sign_csr(host) host.desired_state = "signed" @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host)) end def generate_signed_cert(host) generate_csr(host) sign_csr(host) @terminus.find(Puppet::Indirector::Request.new(:certificate_status, :find, host.name, host)) end def generate_revoked_cert(host) generate_signed_cert(host) host.desired_state = "revoked" @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host)) end it "should be a terminus on SSL::Host" do @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::File) end it "should create a CA instance if none is present" do @terminus.ca.should be_instance_of(Puppet::SSL::CertificateAuthority) end describe "when creating the CA" do it "should fail if it is not a valid CA" do Puppet::SSL::CertificateAuthority.expects(:ca?).returns false lambda { @terminus.ca }.should raise_error(ArgumentError, "This process is not configured as a certificate authority") end end it "should be indirected with the name 'certificate_status'" do Puppet::SSL::Host.indirection.name.should == :certificate_status end describe "when finding" do before do @host = Puppet::SSL::Host.new("foo") Puppet.settings.use(:main) end it "should return the Puppet::SSL::Host when a CSR exists for the host" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do generate_csr(@host) request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) retrieved_host = @terminus.find(request) retrieved_host.name.should == @host.name retrieved_host.certificate_request.content.to_s.chomp.should == @host.certificate_request.content.to_s.chomp end end it "should return the Puppet::SSL::Host when a public key exist for the host" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do generate_signed_cert(@host) request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) retrieved_host = @terminus.find(request) retrieved_host.name.should == @host.name retrieved_host.certificate.content.to_s.chomp.should == @host.certificate.content.to_s.chomp end end it "should return nil when neither a CSR nor public key exist for the host" do request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) @terminus.find(request).should == nil end end describe "when saving" do before do @host = Puppet::SSL::Host.new("foobar") Puppet.settings.use(:main) end describe "when signing a cert" do before do @host.desired_state = "signed" @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host) end it "should fail if no CSR is on disk" do lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /certificate request/) end it "should sign the on-disk CSR when it is present" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do signed_host = generate_signed_cert(@host) signed_host.state.should == "signed" Puppet::SSL::Certificate.indirection.find("foobar").should be_instance_of(Puppet::SSL::Certificate) end end end describe "when revoking a cert" do before do @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host) end it "should fail if no certificate is on disk" do @host.desired_state = "revoked" lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /Cannot revoke/) end it "should revoke the certificate when it is present" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do generate_revoked_cert(@host) @host.state.should == 'revoked' end end end end describe "when deleting" do before do Puppet.settings.use(:main) end it "should not delete anything if no certificate, request, or key is on disk" do host = Puppet::SSL::Host.new("clean_me") request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_me", host) @terminus.destroy(request).should == "Nothing was deleted" end it "should clean certs, cert requests, keys" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do signed_host = Puppet::SSL::Host.new("clean_signed_cert") generate_signed_cert(signed_host) signed_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_signed_cert", signed_host) @terminus.destroy(signed_request).should == "Deleted for clean_signed_cert: Puppet::SSL::Certificate, Puppet::SSL::Key" requested_host = Puppet::SSL::Host.new("clean_csr") generate_csr(requested_host) csr_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_csr", requested_host) @terminus.destroy(csr_request).should == "Deleted for clean_csr: Puppet::SSL::CertificateRequest, Puppet::SSL::Key" end end end describe "when searching" do it "should return a list of all hosts with certificate requests, signed certs, or revoked certs" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do Puppet.settings.use(:main) signed_host = Puppet::SSL::Host.new("signed_host") generate_signed_cert(signed_host) requested_host = Puppet::SSL::Host.new("requested_host") generate_csr(requested_host) revoked_host = Puppet::SSL::Host.new("revoked_host") generate_revoked_cert(revoked_host) retrieved_hosts = @terminus.search(Puppet::Indirector::Request.new(:certificate_status, :search, "all", signed_host)) results = retrieved_hosts.map {|h| [h.name, h.state]}.sort{ |h,i| h[0] <=> i[0] } results.should == [["ca","signed"],["requested_host","requested"],["revoked_host","revoked"],["signed_host","signed"]] end end end end diff --git a/spec/unit/indirector/certificate_status/rest_spec.rb b/spec/unit/indirector/certificate_status/rest_spec.rb index 39fbb7024..e439a1086 100755 --- a/spec/unit/indirector/certificate_status/rest_spec.rb +++ b/spec/unit/indirector/certificate_status/rest_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/host' require 'puppet/indirector/certificate_status' describe "Puppet::CertificateStatus::Rest" do before do @terminus = Puppet::SSL::Host.indirection.terminus(:rest) end it "should be a terminus on Puppet::SSL::Host" do @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::Rest) end end diff --git a/spec/unit/indirector/code_spec.rb b/spec/unit/indirector/code_spec.rb index 29369bf5e..58550d96e 100755 --- a/spec/unit/indirector/code_spec.rb +++ b/spec/unit/indirector/code_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/code' describe Puppet::Indirector::Code do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @code_class = class Testing::MyCode < Puppet::Indirector::Code self end @searcher = @code_class.new end it "should not have a find() method defined" do @searcher.should_not respond_to(:find) end it "should not have a save() method defined" do @searcher.should_not respond_to(:save) end it "should not have a destroy() method defined" do @searcher.should_not respond_to(:destroy) end end diff --git a/spec/unit/indirector/direct_file_server_spec.rb b/spec/unit/indirector/direct_file_server_spec.rb index 8213f5342..d6a7adcb7 100755 --- a/spec/unit/indirector/direct_file_server_spec.rb +++ b/spec/unit/indirector/direct_file_server_spec.rb @@ -1,80 +1,80 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/direct_file_server' describe Puppet::Indirector::DirectFileServer do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @direct_file_class = class Testing::Mytype < Puppet::Indirector::DirectFileServer self end @server = @direct_file_class.new @path = File.expand_path('/my/local') @uri = Puppet::Util.path_to_uri(@path).to_s @request = Puppet::Indirector::Request.new(:mytype, :find, @uri, nil) end describe Puppet::Indirector::DirectFileServer, "when finding a single file" do it "should return nil if the file does not exist" do FileTest.expects(:exists?).with(@path).returns false @server.find(@request).should be_nil end it "should return a Content instance created with the full path to the file if the file exists" do FileTest.expects(:exists?).with(@path).returns true @model.expects(:new).returns(:mycontent) @server.find(@request).should == :mycontent end end describe Puppet::Indirector::DirectFileServer, "when creating the instance for a single found file" do before do @data = mock 'content' @data.stubs(:collect) FileTest.expects(:exists?).with(@path).returns true end it "should pass the full path to the instance" do @model.expects(:new).with { |key, options| key == @path }.returns(@data) @server.find(@request) end it "should pass the :links setting on to the created Content instance if the file exists and there is a value for :links" do @model.expects(:new).returns(@data) @data.expects(:links=).with(:manage) @request.stubs(:options).returns(:links => :manage) @server.find(@request) end end describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do it "should return nil if the file does not exist" do FileTest.expects(:exists?).with(@path).returns false @server.find(@request).should be_nil end it "should use :path2instances from the terminus_helper to return instances if the file exists" do FileTest.expects(:exists?).with(@path).returns true @server.expects(:path2instances) @server.search(@request) end it "should pass the original request to :path2instances" do FileTest.expects(:exists?).with(@path).returns true @server.expects(:path2instances).with(@request, @path) @server.search(@request) end end end diff --git a/spec/unit/indirector/envelope_spec.rb b/spec/unit/indirector/envelope_spec.rb index e056b768c..17ceaea55 100755 --- a/spec/unit/indirector/envelope_spec.rb +++ b/spec/unit/indirector/envelope_spec.rb @@ -1,46 +1,46 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/envelope' describe Puppet::Indirector::Envelope do before do @instance = Object.new @instance.extend(Puppet::Indirector::Envelope) end it "should have an expiration accessor" do @instance.expiration = "testing" @instance.expiration.should == "testing" end it "should have an expiration setter" do @instance.should respond_to(:expiration=) end it "should have a means of testing whether it is expired" do @instance.should respond_to(:expired?) end describe "when testing if it is expired" do it "should return false if there is no expiration set" do @instance.should_not be_expired end it "should return true if the current date is after the expiration date" do @instance.expiration = Time.now - 10 @instance.should be_expired end it "should return false if the current date is prior to the expiration date" do @instance.expiration = Time.now + 10 @instance.should_not be_expired end it "should return false if the current date is equal to the expiration date" do now = Time.now Time.stubs(:now).returns(now) @instance.expiration = now @instance.should_not be_expired end end end diff --git a/spec/unit/indirector/exec_spec.rb b/spec/unit/indirector/exec_spec.rb index 45a087abf..55fda939c 100755 --- a/spec/unit/indirector/exec_spec.rb +++ b/spec/unit/indirector/exec_spec.rb @@ -1,56 +1,56 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/exec' describe Puppet::Indirector::Exec do before :all do @indirection = stub 'indirection', :name => :testing Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) module Testing; end @exec_class = class Testing::MyTesting < Puppet::Indirector::Exec attr_accessor :command self end end let(:path) { File.expand_path('/echo') } before :each do @searcher = @exec_class.new @searcher.command = [path] @request = stub 'request', :key => "foo" end it "should throw an exception if the command is not an array" do @searcher.command = path proc { @searcher.find(@request) }.should raise_error(Puppet::DevError) end it "should throw an exception if the command is not fully qualified" do @searcher.command = ["mycommand"] proc { @searcher.find(@request) }.should raise_error(ArgumentError) end it "should execute the command with the object name as the only argument" do @searcher.expects(:execute).with([path, 'foo'], :combine => false) @searcher.find(@request) end it "should return the output of the script" do @searcher.expects(:execute).with([path, 'foo'], :combine => false).returns("whatever") @searcher.find(@request).should == "whatever" end it "should return nil when the command produces no output" do @searcher.expects(:execute).with([path, 'foo'], :combine => false).returns(nil) @searcher.find(@request).should be_nil end it "should raise an exception if there's an execution failure" do @searcher.expects(:execute).with([path, 'foo'], :combine => false).raises(Puppet::ExecutionFailure.new("message")) lambda {@searcher.find(@request)}.should raise_exception(Puppet::Error, 'Failed to find foo via exec: message') end end diff --git a/spec/unit/indirector/face_spec.rb b/spec/unit/indirector/face_spec.rb index 8e324f019..009a33370 100755 --- a/spec/unit/indirector/face_spec.rb +++ b/spec/unit/indirector/face_spec.rb @@ -1,70 +1,70 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/face' describe Puppet::Indirector::Face do subject do instance = Puppet::Indirector::Face.new(:test, '0.0.1') indirection = stub('indirection', :name => :stub_indirection, :reset_terminus_class => nil) instance.stubs(:indirection).returns indirection instance end it { should be_option :extra } it "should be able to return a list of indirections" do Puppet::Indirector::Face.indirections.should be_include("catalog") end it "should return the sorted to_s list of terminus classes" do Puppet::Indirector::Terminus.expects(:terminus_classes).returns([ :yaml, :compiler, :rest ]) Puppet::Indirector::Face.terminus_classes(:catalog).should == [ 'compiler', 'rest', 'yaml' ] end describe "as an instance" do it "should be able to determine its indirection" do # Loading actions here an get, um, complicated Puppet::Face.stubs(:load_actions) Puppet::Indirector::Face.new(:catalog, '0.0.1').indirection.should equal(Puppet::Resource::Catalog.indirection) end end [:find, :search, :save, :destroy].each do |method| it "should define a '#{method}' action" do Puppet::Indirector::Face.should be_action(method) end it "should call the indirection method with options when the '#{method}' action is invoked" do subject.indirection.expects(method).with(:test, {}) subject.send(method, :test) end it "should forward passed options" do subject.indirection.expects(method).with(:test, {'one'=>'1'}) subject.send(method, :test, :extra => {'one'=>'1'}) end end it "should be able to override its indirection name" do subject.set_indirection_name :foo subject.indirection_name.should == :foo end it "should be able to set its terminus class" do subject.indirection.expects(:terminus_class=).with(:myterm) subject.set_terminus(:myterm) end it "should define a class-level 'info' action" do Puppet::Indirector::Face.should be_action(:info) end end diff --git a/spec/unit/indirector/facts/active_record_spec.rb b/spec/unit/indirector/facts/active_record_spec.rb index 01a906716..a6197d466 100755 --- a/spec/unit/indirector/facts/active_record_spec.rb +++ b/spec/unit/indirector/facts/active_record_spec.rb @@ -1,102 +1,102 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' require 'puppet/node/facts' describe "Puppet::Node::Facts::ActiveRecord", :if => Puppet.features.rails? do before do require 'puppet/indirector/facts/active_record' Puppet.features.stubs(:rails?).returns true Puppet::Rails.stubs(:init) @terminus = Puppet::Node::Facts::ActiveRecord.new end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Node::Facts::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Node::Facts::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end describe "when finding an instance" do before do @request = stub 'request', :key => "foo" end it "should use the Hosts ActiveRecord class to find the host" do Puppet::Rails::Host.expects(:find_by_name).with { |key, args| key == "foo" } @terminus.find(@request) end it "should include the fact names and values when finding the host" do Puppet::Rails::Host.expects(:find_by_name).with { |key, args| args[:include] == {:fact_values => :fact_name} } @terminus.find(@request) end it "should return nil if no host instance can be found" do Puppet::Rails::Host.expects(:find_by_name).returns nil @terminus.find(@request).should be_nil end it "should convert the node's parameters into a Facts instance if a host instance is found" do host = stub 'host', :name => "foo" host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")], "three" => [mock("three_value", :value => "four")]) Puppet::Rails::Host.expects(:find_by_name).returns host result = @terminus.find(@request) result.should be_instance_of(Puppet::Node::Facts) result.name.should == "foo" result.values.should == {"one" => "two", "three" => "four"} end it "should convert all single-member arrays into non-arrays" do host = stub 'host', :name => "foo" host.expects(:get_facts_hash).returns("one" => [mock("two_value", :value => "two")]) Puppet::Rails::Host.expects(:find_by_name).returns host @terminus.find(@request).values["one"].should == "two" end end describe "when saving an instance" do before do @host = stub 'host', :name => "foo", :save => nil, :merge_facts => nil Puppet::Rails::Host.stubs(:find_by_name).returns @host @facts = Puppet::Node::Facts.new("foo", "one" => "two", "three" => "four") @request = stub 'request', :key => "foo", :instance => @facts end it "should find the Rails host with the same name" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @terminus.save(@request) end it "should create a new Rails host if none can be found" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil Puppet::Rails::Host.expects(:create).with(:name => "foo").returns @host @terminus.save(@request) end it "should set the facts as facts on the Rails host instance" do # There is other stuff added to the hash. @host.expects(:merge_facts).with { |args| args["one"] == "two" and args["three"] == "four" } @terminus.save(@request) end it "should save the Rails host instance" do @host.expects(:save) @terminus.save(@request) end end end diff --git a/spec/unit/indirector/facts/couch_spec.rb b/spec/unit/indirector/facts/couch_spec.rb index d0862486c..cb85f1169 100755 --- a/spec/unit/indirector/facts/couch_spec.rb +++ b/spec/unit/indirector/facts/couch_spec.rb @@ -1,102 +1,102 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node/facts' require 'puppet/indirector/facts/couch' describe "Puppet::Node::Facts::Couch" do describe "when couchdb is not available", :unless => Puppet.features.couchdb? do it "should fail to initialize" do lambda { Puppet::Node::Facts::Couch.new }.should raise_error end end describe "when couchdb is available", :if => Puppet.features.couchdb? do before do @mock_db = mock('couch db') mock_document = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.values) mock_document.stubs(:database).returns(@mock_db) @mock_db.stubs(:get).with(fake_request.key).returns(mock_document) Puppet::Node::Facts::Couch.stubs(:db).returns(@mock_db) end subject { Puppet::Node::Facts::Couch } describe "#find" do describe "when the node document exists" do it "should find the request by key" do @mock_db.expects(:get).with(fake_request.key).returns({'_id' => fake_request.key, 'facts' => fake_request.instance.values}) subject.new.find(fake_request).should == fake_request.instance end end describe "when the node document does not exist" do before do @mock_db.expects(:get). with(fake_request.key). raises(RestClient::ResourceNotFound) end it "should return nil" do subject.new.find(fake_request).should be_nil end it "should send Puppet a debug message" do Puppet.expects(:debug).with("No couchdb document with id: test.local") subject.new.find(fake_request).should be_nil end end end describe "#save" do describe "with options" do subject do lambda { Puppet::Node::Facts::Couch.new.save(fake_request([1])) } end it { should raise_error(ArgumentError, "PUT does not accept options") } end it "should save the json to the CouchDB database" do @mock_db.expects(:save_doc).at_least_once.returns({'ok' => true }) subject.new.save(fake_request) end describe "when the document exists" do before do @doc = CouchRest::Document.new(:_id => fake_request.key, :facts => fake_request.instance.values) @mock_db.expects(:get).with(fake_request.key).returns(@doc) end it "saves the document" do @doc.expects(:save) subject.new.save(fake_request) end end describe "when the document does not exist" do before do @mock_db.expects(:get). with(fake_request.key). raises(RestClient::ResourceNotFound) end it "saves the document" do @mock_db.expects(:save_doc) subject.new.save(fake_request) end end end def fake_request(options={}) facts = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml', 'test.local.yaml')) Struct.new(:instance, :key, :options).new(facts, facts.name, options) end private :fake_request end end diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb index 0cb8037aa..5e897611c 100755 --- a/spec/unit/indirector/facts/facter_spec.rb +++ b/spec/unit/indirector/facts/facter_spec.rb @@ -1,161 +1,161 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/facts/facter' describe Puppet::Node::Facts::Facter do it "should be a subclass of the Code terminus" do Puppet::Node::Facts::Facter.superclass.should equal(Puppet::Indirector::Code) end it "should have documentation" do Puppet::Node::Facts::Facter.doc.should_not be_nil end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:facts) Puppet::Node::Facts::Facter.indirection.should equal(indirection) end it "should have its name set to :facter" do Puppet::Node::Facts::Facter.name.should == :facter end describe "when reloading Facter" do before do @facter_class = Puppet::Node::Facts::Facter Facter.stubs(:clear) Facter.stubs(:load) Facter.stubs(:loadfacts) end it "should clear Facter" do Facter.expects(:clear) @facter_class.reload_facter end it "should load all Facter facts" do Facter.expects(:loadfacts) @facter_class.reload_facter end end end describe Puppet::Node::Facts::Facter do before :each do Puppet::Node::Facts::Facter.stubs(:reload_facter) @facter = Puppet::Node::Facts::Facter.new Facter.stubs(:to_hash).returns({}) @name = "me" @request = stub 'request', :key => @name end describe Puppet::Node::Facts::Facter, " when finding facts" do it "should reset and load facts" do clear = sequence 'clear' Puppet::Node::Facts::Facter.expects(:reload_facter).in_sequence(clear) Puppet::Node::Facts::Facter.expects(:load_fact_plugins).in_sequence(clear) @facter.find(@request) end it "should return a Facts instance" do @facter.find(@request).should be_instance_of(Puppet::Node::Facts) end it "should return a Facts instance with the provided key as the name" do @facter.find(@request).name.should == @name end it "should return the Facter facts as the values in the Facts instance" do Facter.expects(:to_hash).returns("one" => "two") facts = @facter.find(@request) facts.values["one"].should == "two" end it "should add local facts" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:add_local_facts) @facter.find(@request) end it "should convert all facts into strings" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:stringify) @facter.find(@request) end it "should call the downcase hook" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:downcase_if_necessary) @facter.find(@request) end end describe Puppet::Node::Facts::Facter, " when saving facts" do it "should fail" do proc { @facter.save(@facts) }.should raise_error(Puppet::DevError) end end describe Puppet::Node::Facts::Facter, " when destroying facts" do it "should fail" do proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError) end end it "should skip files when asked to load a directory" do FileTest.expects(:directory?).with("myfile").returns false Puppet::Node::Facts::Facter.load_facts_in_dir("myfile") end it "should load each ruby file when asked to load a directory" do FileTest.expects(:directory?).with("mydir").returns true Dir.expects(:chdir).with("mydir").yields Dir.expects(:glob).with("*.rb").returns %w{a.rb b.rb} Puppet::Node::Facts::Facter.expects(:load).with("a.rb") Puppet::Node::Facts::Facter.expects(:load).with("b.rb") Puppet::Node::Facts::Facter.load_facts_in_dir("mydir") end describe Puppet::Node::Facts::Facter, "when loading fact plugins from disk" do it "should load each directory in the Fact path" do Puppet.settings.stubs(:value).returns "foo" Puppet.settings.expects(:value).with(:factpath).returns("one#{File::PATH_SEPARATOR}two") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("one") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("two") Puppet::Node::Facts::Facter.load_fact_plugins end it "should load all facts from the modules" do Puppet.settings.stubs(:value).returns "foo" Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir) Puppet.settings.expects(:value).with(:modulepath).returns("one#{File::PATH_SEPARATOR}two") Dir.stubs(:glob).returns [] Dir.expects(:glob).with("one/*/lib/facter").returns %w{oneA oneB} Dir.expects(:glob).with("two/*/lib/facter").returns %w{twoA twoB} Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneA") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("oneB") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoA") Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with("twoB") Puppet::Node::Facts::Facter.load_fact_plugins end end end diff --git a/spec/unit/indirector/facts/inventory_active_record_spec.rb b/spec/unit/indirector/facts/inventory_active_record_spec.rb index 856adfdb4..c7d6d9178 100755 --- a/spec/unit/indirector/facts/inventory_active_record_spec.rb +++ b/spec/unit/indirector/facts/inventory_active_record_spec.rb @@ -1,166 +1,166 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' describe "Puppet::Node::Facts::InventoryActiveRecord", :if => can_use_scratch_database? do include PuppetSpec::Files let(:terminus) { Puppet::Node::Facts::InventoryActiveRecord.new } before :all do require 'puppet/indirector/facts/inventory_active_record' end after :each do Puppet::Node::Facts.indirection.reset_terminus_class end before :each do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil Puppet::Node::Facts.indirection.terminus_class = :inventory_active_record setup_scratch_database end describe "#save" do let(:node) { Puppet::Rails::InventoryNode.new(:name => "foo", :timestamp => Time.now) } let(:facts) { Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") } it "should retry on ActiveRecord error" do Puppet::Rails::InventoryNode.expects(:create).twice.raises(ActiveRecord::StatementInvalid).returns node Puppet::Node::Facts.indirection.save(facts) end it "should use an existing node if possible" do node.save Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryNode.count.should == 1 Puppet::Rails::InventoryNode.first.should == node end it "should create a new node if one can't be found" do # This test isn't valid if there are nodes to begin with Puppet::Rails::InventoryNode.count.should == 0 Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryNode.count.should == 1 Puppet::Rails::InventoryNode.first.name.should == "foo" end it "should save the facts" do Puppet::Node::Facts.indirection.save(facts) Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should =~ [["uptime_days","60"],["kernel","Darwin"]] end it "should remove the previous facts for an existing node" do facts = Puppet::Node::Facts.new("foo", "uptime_days" => "30", "kernel" => "Darwin") Puppet::Node::Facts.indirection.save(facts) bar_facts = Puppet::Node::Facts.new("bar", "uptime_days" => "35", "kernel" => "Linux") foo_facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "is_virtual" => "false") Puppet::Node::Facts.indirection.save(bar_facts) Puppet::Node::Facts.indirection.save(foo_facts) Puppet::Node::Facts.indirection.find("bar").should == bar_facts Puppet::Node::Facts.indirection.find("foo").should == foo_facts Puppet::Rails::InventoryFact.all.map{|f| [f.name,f.value]}.should_not include(["uptime_days", "30"], ["kernel", "Darwin"]) end end describe "#find" do before do @foo_facts = Puppet::Node::Facts.new("foo", "uptime_days" => "60", "kernel" => "Darwin") @bar_facts = Puppet::Node::Facts.new("bar", "uptime_days" => "30", "kernel" => "Linux") Puppet::Node::Facts.indirection.save(@foo_facts) Puppet::Node::Facts.indirection.save(@bar_facts) end it "should identify facts by node name" do Puppet::Node::Facts.indirection.find("foo").should == @foo_facts end it "should return nil if no node instance can be found" do Puppet::Node::Facts.indirection.find("non-existent node").should == nil end end describe "#search" do def search_request(conditions) Puppet::Indirector::Request.new(:facts, :search, nil, nil, conditions) end before :each do @now = Time.now @foo = Puppet::Node::Facts.new("foo", "fact1" => "value1", "fact2" => "value2", "uptime_days" => "30") @bar = Puppet::Node::Facts.new("bar", "fact1" => "value1", "uptime_days" => "60") @baz = Puppet::Node::Facts.new("baz", "fact1" => "value2", "fact2" => "value1", "uptime_days" => "90") @bat = Puppet::Node::Facts.new("bat") @foo.timestamp = @now - 3600*1 @bar.timestamp = @now - 3600*3 @baz.timestamp = @now - 3600*5 @bat.timestamp = @now - 3600*7 [@foo, @bar, @baz, @bat].each {|facts| Puppet::Node::Facts.indirection.save(facts)} end it "should return node names that match 'equal' constraints" do request = search_request('facts.fact1.eq' => 'value1', 'facts.fact2.eq' => 'value2') terminus.search(request).should == ["foo"] end it "should return node names that match 'not equal' constraints" do request = search_request('facts.fact1.ne' => 'value2') terminus.search(request).should == ["bar","foo"] end it "should return node names that match strict inequality constraints" do request = search_request('facts.uptime_days.gt' => '20', 'facts.uptime_days.lt' => '70') terminus.search(request).should == ["bar","foo"] end it "should return node names that match non-strict inequality constraints" do request = search_request('facts.uptime_days.ge' => '30', 'facts.uptime_days.le' => '60') terminus.search(request).should == ["bar","foo"] end it "should return node names whose facts are within a given timeframe" do request = search_request('meta.timestamp.ge' => @now - 3600*5, 'meta.timestamp.le' => @now - 3600*1) terminus.search(request).should == ["bar","baz","foo"] end it "should return node names whose facts are from a specific time" do request = search_request('meta.timestamp.eq' => @now - 3600*3) terminus.search(request).should == ["bar"] end it "should return node names whose facts are not from a specific time" do request = search_request('meta.timestamp.ne' => @now - 3600*1) terminus.search(request).should == ["bar","bat","baz"] end it "should perform strict searches on nodes by timestamp" do request = search_request('meta.timestamp.gt' => @now - 3600*5, 'meta.timestamp.lt' => @now - 3600*1) terminus.search(request).should == ["bar"] end it "should search nodes based on both facts and timestamp values" do request = search_request('facts.uptime_days.gt' => '45', 'meta.timestamp.lt' => @now - 3600*4) terminus.search(request).should == ["baz"] end end end diff --git a/spec/unit/indirector/facts/inventory_service_spec.rb b/spec/unit/indirector/facts/inventory_service_spec.rb index 6976466f9..2fff92565 100644 --- a/spec/unit/indirector/facts/inventory_service_spec.rb +++ b/spec/unit/indirector/facts/inventory_service_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/facts/inventory_service' describe Puppet::Node::Facts::InventoryService do it "should suppress failures and warn when saving facts" do facts = Puppet::Node::Facts.new('foo') request = Puppet::Indirector::Request.new(:facts, :save, nil, facts) Net::HTTP.any_instance.stubs(:put).raises(Errno::ECONNREFUSED) Puppet.expects(:warning).with do |msg| msg =~ /Could not upload facts for foo to inventory service/ end expect { subject.save(request) }.should_not raise_error end end diff --git a/spec/unit/indirector/facts/network_device_spec.rb b/spec/unit/indirector/facts/network_device_spec.rb index 0ad2ca071..9a4fb507d 100755 --- a/spec/unit/indirector/facts/network_device_spec.rb +++ b/spec/unit/indirector/facts/network_device_spec.rb @@ -1,86 +1,86 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device' require 'puppet/indirector/facts/network_device' describe Puppet::Node::Facts::NetworkDevice do it "should be a subclass of the Code terminus" do Puppet::Node::Facts::NetworkDevice.superclass.should equal(Puppet::Indirector::Code) end it "should have documentation" do Puppet::Node::Facts::NetworkDevice.doc.should_not be_nil end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:facts) Puppet::Node::Facts::NetworkDevice.indirection.should equal(indirection) end it "should have its name set to :facter" do Puppet::Node::Facts::NetworkDevice.name.should == :network_device end end describe Puppet::Node::Facts::NetworkDevice do before :each do @remote_device = stub 'remote_device', :facts => {} Puppet::Util::NetworkDevice.stubs(:current).returns(@remote_device) @device = Puppet::Node::Facts::NetworkDevice.new @name = "me" @request = stub 'request', :key => @name end describe Puppet::Node::Facts::NetworkDevice, " when finding facts" do it "should return a Facts instance" do @device.find(@request).should be_instance_of(Puppet::Node::Facts) end it "should return a Facts instance with the provided key as the name" do @device.find(@request).name.should == @name end it "should return the device facts as the values in the Facts instance" do @remote_device.expects(:facts).returns("one" => "two") facts = @device.find(@request) facts.values["one"].should == "two" end it "should add local facts" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:add_local_facts) @device.find(@request) end it "should convert all facts into strings" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:stringify) @device.find(@request) end it "should call the downcase hook" do facts = Puppet::Node::Facts.new("foo") Puppet::Node::Facts.expects(:new).returns facts facts.expects(:downcase_if_necessary) @device.find(@request) end end describe Puppet::Node::Facts::NetworkDevice, " when saving facts" do it "should fail" do proc { @device.save(@facts) }.should raise_error(Puppet::DevError) end end describe Puppet::Node::Facts::NetworkDevice, " when destroying facts" do it "should fail" do proc { @device.destroy(@facts) }.should raise_error(Puppet::DevError) end end end diff --git a/spec/unit/indirector/facts/rest_spec.rb b/spec/unit/indirector/facts/rest_spec.rb index 6a2a23f8b..248a4ede2 100755 --- a/spec/unit/indirector/facts/rest_spec.rb +++ b/spec/unit/indirector/facts/rest_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/facts/rest' describe Puppet::Node::Facts::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Node::Facts::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/facts/store_configs_spec.rb b/spec/unit/indirector/facts/store_configs_spec.rb index d302d17f8..a54d80157 100755 --- a/spec/unit/indirector/facts/store_configs_spec.rb +++ b/spec/unit/indirector/facts/store_configs_spec.rb @@ -1,17 +1,17 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node' require 'puppet/indirector/memory' require 'puppet/indirector/facts/store_configs' class Puppet::Node::Facts::StoreConfigsTesting < Puppet::Indirector::Memory end describe Puppet::Node::Facts::StoreConfigs do after :all do Puppet::Node::Facts.indirection.reset_terminus_class Puppet::Node::Facts.indirection.cache_class = nil end it_should_behave_like "a StoreConfigs terminus" end diff --git a/spec/unit/indirector/facts/yaml_spec.rb b/spec/unit/indirector/facts/yaml_spec.rb index d6e1af75a..da40762a9 100755 --- a/spec/unit/indirector/facts/yaml_spec.rb +++ b/spec/unit/indirector/facts/yaml_spec.rb @@ -1,239 +1,239 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node/facts' require 'puppet/indirector/facts/yaml' describe Puppet::Node::Facts::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Node::Facts::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Node::Facts::Yaml.doc.should_not be_nil Puppet::Node::Facts::Yaml.doc.should_not be_empty end it "should be registered with the facts indirection" do indirection = Puppet::Indirector::Indirection.instance(:facts) Puppet::Node::Facts::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do Puppet::Node::Facts::Yaml.name.should == :yaml end describe "#search" do def assert_search_matches(matching, nonmatching, query) request = Puppet::Indirector::Request.new(:inventory, :search, nil, nil, query) Dir.stubs(:glob).returns(matching.keys + nonmatching.keys) [matching, nonmatching].each do |examples| examples.each do |key, value| YAML.stubs(:load_file).with(key).returns value end end Puppet::Node::Facts::Yaml.new.search(request).should =~ matching.values.map {|facts| facts.name} end it "should return node names that match the search query options" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '4'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "i386", 'processor_count' => '4', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), }, {'facts.architecture' => 'i386', 'facts.processor_count' => '4'} ) end it "should return empty array when no nodes match the search query options" do assert_search_matches({}, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '10'), "/path/to/nonmatching1.yaml" => Puppet::Node::Facts.new("nonmatchingnode1", "architecture" => "powerpc", 'processor_count' => '5'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3", 'processor_count' => '4'), }, {'facts.processor_count.lt' => '4', 'facts.processor_count.gt' => '4'} ) end it "should return node names that match the search query options with the greater than operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '10', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '4'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '3'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.gt' => '4'} ) end it "should return node names that match the search query options with the less than operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '30', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '50' ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '100'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.lt' => '50'} ) end it "should return node names that match the search query options with the less than or equal to operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '5'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '100' ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '5000'), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.le' => '50'} ) end it "should return node names that match the search query options with the greater than or equal to operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => "i386", 'processor_count' => '100'), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => "powerpc", 'processor_count' => '50', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "powerpc", 'processor_count' => '40'), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.processor_count.ge' => '50'} ) end it "should return node names that match the search query options with the not equal operator" do assert_search_matches({ '/path/to/matching.yaml' => Puppet::Node::Facts.new("matchingnode", "architecture" => 'arm' ), '/path/to/matching1.yaml' => Puppet::Node::Facts.new("matchingnode1", "architecture" => 'powerpc', 'randomfact' => 'foo') }, { "/path/to/nonmatching.yaml" => Puppet::Node::Facts.new("nonmatchingnode", "architecture" => "i386" ), "/path/to/nonmatching2.yaml" => Puppet::Node::Facts.new("nonmatchingnode2", "architecture" => "i386", 'processor_count' => '9' ), "/path/to/nonmatching3.yaml" => Puppet::Node::Facts.new("nonmatchingnode3" ), }, {'facts.architecture.ne' => 'i386'} ) end def apply_timestamp(facts, timestamp) facts.timestamp = timestamp facts end it "should be able to query based on meta.timestamp.gt" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), }, { '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.gt' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.le" do assert_search_matches({ '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), }, {'meta.timestamp.le' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.lt" do assert_search_matches({ '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.lt' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.ge" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp.ge' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.eq" do assert_search_matches({ '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp.eq' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp" do assert_search_matches({ '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, { '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, {'meta.timestamp' => '2010-10-15'} ) end it "should be able to query based on meta.timestamp.ne" do assert_search_matches({ '/path/to/2010-11-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-01", {}), Time.parse("2010-11-01")), '/path/to/2010-11-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-11-10", {}), Time.parse("2010-11-10")), '/path/to/2010-10-01.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-01", {}), Time.parse("2010-10-01")), '/path/to/2010-10-10.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-10", {}), Time.parse("2010-10-10")), }, { '/path/to/2010-10-15.yaml' => apply_timestamp(Puppet::Node::Facts.new("2010-10-15", {}), Time.parse("2010-10-15")), }, {'meta.timestamp.ne' => '2010-10-15'} ) end end end diff --git a/spec/unit/indirector/file_bucket_file/file_spec.rb b/spec/unit/indirector/file_bucket_file/file_spec.rb index 6f1a6823e..dbfee3210 100755 --- a/spec/unit/indirector/file_bucket_file/file_spec.rb +++ b/spec/unit/indirector/file_bucket_file/file_spec.rb @@ -1,270 +1,270 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_bucket_file/file' describe Puppet::FileBucketFile::File do include PuppetSpec::Files it "should be a subclass of the Code terminus class" do Puppet::FileBucketFile::File.superclass.should equal(Puppet::Indirector::Code) end it "should have documentation" do Puppet::FileBucketFile::File.doc.should be_instance_of(String) end describe "non-stubbing tests" do include PuppetSpec::Files before do Puppet[:bucketdir] = tmpdir('bucketdir') end def save_bucket_file(contents, path = "/who_cares") bucket_file = Puppet::FileBucket::File.new(contents) Puppet::FileBucket::File.indirection.save(bucket_file, "md5/#{Digest::MD5.hexdigest(contents)}#{path}") bucket_file.checksum_data end describe "when servicing a save request" do describe "when supplying a path" do it "should store the path if not already stored" do checksum = save_bucket_file("stuff\r\n", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/f/c/7/7/7/c/0/b/fc777c0bc467e1ab98b4c6915af802ec" IO.binread("#{dir_path}/contents").should == "stuff\r\n" File.read("#{dir_path}/paths").should == "foo/bar\n" end it "should leave the paths file alone if the path is already stored" do checksum = save_bucket_file("stuff", "/foo/bar") checksum = save_bucket_file("stuff", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\n" end it "should store an additional path if the new path differs from those already stored" do checksum = save_bucket_file("stuff", "/foo/bar") checksum = save_bucket_file("stuff", "/foo/baz") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\nfoo/baz\n" end end describe "when not supplying a path" do it "should save the file and create an empty paths file" do checksum = save_bucket_file("stuff", "") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "" end end end describe "when servicing a head/find request" do describe "when supplying a path" do it "should return false/nil if the file isn't bucketed" do Puppet::FileBucket::File.indirection.head("md5/0ae2ec1980410229885fe72f7b44fe55/foo/bar").should == false Puppet::FileBucket::File.indirection.find("md5/0ae2ec1980410229885fe72f7b44fe55/foo/bar").should == nil end it "should return false/nil if the file is bucketed but with a different path" do checksum = save_bucket_file("I'm the contents of a file", '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}/foo/baz").should == false Puppet::FileBucket::File.indirection.find("md5/#{checksum}/foo/baz").should == nil end it "should return true/file if the file is already bucketed with the given path" do contents = "I'm the contents of a file" checksum = save_bucket_file(contents, '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}/foo/bar").should == true find_result = Puppet::FileBucket::File.indirection.find("md5/#{checksum}/foo/bar") find_result.should be_a(Puppet::FileBucket::File) find_result.checksum.should == "{md5}#{checksum}" find_result.to_s.should == contents end end describe "when not supplying a path" do [false, true].each do |trailing_slash| describe "#{trailing_slash ? 'with' : 'without'} a trailing slash" do trailing_string = trailing_slash ? '/' : '' it "should return false/nil if the file isn't bucketed" do Puppet::FileBucket::File.indirection.head("md5/0ae2ec1980410229885fe72f7b44fe55#{trailing_string}").should == false Puppet::FileBucket::File.indirection.find("md5/0ae2ec1980410229885fe72f7b44fe55#{trailing_string}").should == nil end it "should return true/file if the file is already bucketed" do contents = "I'm the contents of a file" checksum = save_bucket_file(contents, '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}#{trailing_string}").should == true find_result = Puppet::FileBucket::File.indirection.find("md5/#{checksum}#{trailing_string}") find_result.should be_a(Puppet::FileBucket::File) find_result.checksum.should == "{md5}#{checksum}" find_result.to_s.should == contents end end end end end describe "when diffing files", :unless => Puppet.features.microsoft_windows? do it "should generate an empty string if there is no diff" do checksum = save_bucket_file("I'm the contents of a file") Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => checksum).should == '' end it "should generate a proper diff if there is a diff" do checksum1 = save_bucket_file("foo\nbar\nbaz") checksum2 = save_bucket_file("foo\nbiz\nbaz") diff = Puppet::FileBucket::File.indirection.find("md5/#{checksum1}", :diff_with => checksum2) diff.should == < biz HERE end it "should raise an exception if the hash to diff against isn't found" do checksum = save_bucket_file("whatever") bogus_checksum = "d1bf072d0e2c6e20e3fbd23f022089a1" lambda { Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => bogus_checksum) }.should raise_error "could not find diff_with #{bogus_checksum}" end it "should return nil if the hash to diff from isn't found" do checksum = save_bucket_file("whatever") bogus_checksum = "d1bf072d0e2c6e20e3fbd23f022089a1" Puppet::FileBucket::File.indirection.find("md5/#{bogus_checksum}", :diff_with => checksum).should == nil end end end describe "when initializing" do it "should use the filebucket settings section" do Puppet.settings.expects(:use).with(:filebucket) Puppet::FileBucketFile::File.new end end [true, false].each do |override_bucket_path| describe "when bucket path #{if override_bucket_path then 'is' else 'is not' end} overridden" do [true, false].each do |supply_path| describe "when #{supply_path ? 'supplying' : 'not supplying'} a path" do before :each do Puppet.settings.stubs(:use) @store = Puppet::FileBucketFile::File.new @contents = "my content" @digest = "f2bfa7fc155c4f42cb91404198dda01f" @digest.should == Digest::MD5.hexdigest(@contents) @bucket_dir = tmpdir("bucket") if override_bucket_path Puppet[:bucketdir] = "/bogus/path" # should not be used else Puppet[:bucketdir] = @bucket_dir end @dir = "#{@bucket_dir}/f/2/b/f/a/7/f/c/f2bfa7fc155c4f42cb91404198dda01f" @contents_path = "#{@dir}/contents" end describe "when retrieving files" do before :each do request_options = {} if override_bucket_path request_options[:bucket_path] = @bucket_dir end key = "md5/#{@digest}" if supply_path key += "/path/to/file" end @request = Puppet::Indirector::Request.new(:indirection_name, :find, key, nil, request_options) end def make_bucketed_file FileUtils.mkdir_p(@dir) File.open(@contents_path, 'w') { |f| f.write @contents } end it "should return an instance of Puppet::FileBucket::File created with the content if the file exists" do make_bucketed_file if supply_path @store.find(@request).should == nil @store.head(@request).should == false # because path didn't match else bucketfile = @store.find(@request) bucketfile.should be_a(Puppet::FileBucket::File) bucketfile.contents.should == @contents @store.head(@request).should == true end end it "should return nil if no file is found" do @store.find(@request).should be_nil @store.head(@request).should == false end end describe "when saving files" do it "should save the contents to the calculated path" do options = {} if override_bucket_path options[:bucket_path] = @bucket_dir end key = "md5/#{@digest}" if supply_path key += "//path/to/file" end file_instance = Puppet::FileBucket::File.new(@contents, options) request = Puppet::Indirector::Request.new(:indirection_name, :save, key, file_instance) @store.save(request) File.read("#{@dir}/contents").should == @contents end end end end end end describe "when verifying identical files" do let(:contents) { "file\r\n contents" } let(:digest) { "8b3702ad1aed1ace7e32bde76ffffb2d" } let(:checksum) { "{md5}#{@digest}" } let(:bucketdir) { tmpdir('file_bucket_file') } let(:destdir) { "#{bucketdir}/8/b/3/7/0/2/a/d/8b3702ad1aed1ace7e32bde76ffffb2d" } let(:bucket) { Puppet::FileBucket::File.new(contents) } before :each do Puppet[:bucketdir] = bucketdir FileUtils.mkdir_p(destdir) end it "should raise an error if the files don't match" do File.open(File.join(destdir, 'contents'), 'wb') { |f| f.print "corrupt contents" } lambda{ Puppet::FileBucketFile::File.new.send(:verify_identical_file!, bucket) }.should raise_error(Puppet::FileBucket::BucketError) end it "should do nothing if the files match" do File.open(File.join(destdir, 'contents'), 'wb') { |f| f.print contents } Puppet::FileBucketFile::File.new.send(:verify_identical_file!, bucket) end end end diff --git a/spec/unit/indirector/file_bucket_file/rest_spec.rb b/spec/unit/indirector/file_bucket_file/rest_spec.rb index ae2e033c6..44aa9b342 100755 --- a/spec/unit/indirector/file_bucket_file/rest_spec.rb +++ b/spec/unit/indirector/file_bucket_file/rest_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::FileBucketFile::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::FileBucketFile::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/file_content/file_server_spec.rb b/spec/unit/indirector/file_content/file_server_spec.rb index 6b09216d7..61e193b05 100755 --- a/spec/unit/indirector/file_content/file_server_spec.rb +++ b/spec/unit/indirector/file_content/file_server_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_content/file_server' describe Puppet::Indirector::FileContent::FileServer do it "should be registered with the file_content indirection" do Puppet::Indirector::Terminus.terminus_class(:file_content, :file_server).should equal(Puppet::Indirector::FileContent::FileServer) end it "should be a subclass of the FileServer terminus" do Puppet::Indirector::FileContent::FileServer.superclass.should equal(Puppet::Indirector::FileServer) end end diff --git a/spec/unit/indirector/file_content/file_spec.rb b/spec/unit/indirector/file_content/file_spec.rb index d1c6dc738..f84180d47 100755 --- a/spec/unit/indirector/file_content/file_spec.rb +++ b/spec/unit/indirector/file_content/file_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_content/file' describe Puppet::Indirector::FileContent::File do it "should be registered with the file_content indirection" do Puppet::Indirector::Terminus.terminus_class(:file_content, :file).should equal(Puppet::Indirector::FileContent::File) end it "should be a subclass of the DirectFileServer terminus" do Puppet::Indirector::FileContent::File.superclass.should equal(Puppet::Indirector::DirectFileServer) end end diff --git a/spec/unit/indirector/file_content/rest_spec.rb b/spec/unit/indirector/file_content/rest_spec.rb index 99925a842..53de7cd4d 100755 --- a/spec/unit/indirector/file_content/rest_spec.rb +++ b/spec/unit/indirector/file_content/rest_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_content/rest' describe Puppet::Indirector::FileContent::Rest do it "should add the node's cert name to the arguments" it "should set the content type to text/plain" it "should use the :fileserver SRV service" do Puppet::Indirector::FileContent::Rest.srv_service.should == :fileserver end end diff --git a/spec/unit/indirector/file_metadata/file_server_spec.rb b/spec/unit/indirector/file_metadata/file_server_spec.rb index 14177d702..c1229069b 100755 --- a/spec/unit/indirector/file_metadata/file_server_spec.rb +++ b/spec/unit/indirector/file_metadata/file_server_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_metadata/file_server' describe Puppet::Indirector::FileMetadata::FileServer do it "should be registered with the file_metadata indirection" do Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file_server).should equal(Puppet::Indirector::FileMetadata::FileServer) end it "should be a subclass of the FileServer terminus" do Puppet::Indirector::FileMetadata::FileServer.superclass.should equal(Puppet::Indirector::FileServer) end end diff --git a/spec/unit/indirector/file_metadata/file_spec.rb b/spec/unit/indirector/file_metadata/file_spec.rb index 3a108ca9b..bca19ef07 100755 --- a/spec/unit/indirector/file_metadata/file_spec.rb +++ b/spec/unit/indirector/file_metadata/file_spec.rb @@ -1,50 +1,50 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_metadata/file' describe Puppet::Indirector::FileMetadata::File do it "should be registered with the file_metadata indirection" do Puppet::Indirector::Terminus.terminus_class(:file_metadata, :file).should equal(Puppet::Indirector::FileMetadata::File) end it "should be a subclass of the DirectFileServer terminus" do Puppet::Indirector::FileMetadata::File.superclass.should equal(Puppet::Indirector::DirectFileServer) end describe "when creating the instance for a single found file" do before do @metadata = Puppet::Indirector::FileMetadata::File.new @path = File.expand_path('/my/local') @uri = Puppet::Util.path_to_uri(@path).to_s @data = mock 'metadata' @data.stubs(:collect) FileTest.expects(:exists?).with(@path).returns true @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil) end it "should collect its attributes when a file is found" do @data.expects(:collect) Puppet::FileServing::Metadata.expects(:new).returns(@data) @metadata.find(@request).should == @data end end describe "when searching for multiple files" do before do @metadata = Puppet::Indirector::FileMetadata::File.new @path = File.expand_path('/my/local') @uri = Puppet::Util.path_to_uri(@path).to_s @request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil) end it "should collect the attributes of the instances returned" do FileTest.expects(:exists?).with(@path).returns true @metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] ) @metadata.search(@request) end end end diff --git a/spec/unit/indirector/file_metadata/rest_spec.rb b/spec/unit/indirector/file_metadata/rest_spec.rb index 511f7c758..5e8b078ca 100755 --- a/spec/unit/indirector/file_metadata/rest_spec.rb +++ b/spec/unit/indirector/file_metadata/rest_spec.rb @@ -1,8 +1,8 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_metadata' describe "Puppet::Indirector::Metadata::Rest" do it "should add the node's cert name to the arguments" end diff --git a/spec/unit/indirector/file_server_spec.rb b/spec/unit/indirector/file_server_spec.rb index 8f7a5d1b3..a53c149aa 100755 --- a/spec/unit/indirector/file_server_spec.rb +++ b/spec/unit/indirector/file_server_spec.rb @@ -1,263 +1,263 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/file_server' require 'puppet/file_serving/configuration' describe Puppet::Indirector::FileServer do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @file_server_class = class Testing::MyFileServer < Puppet::Indirector::FileServer self end end before :each do @file_server = @file_server_class.new @uri = "puppet://host/my/local/file" @configuration = mock 'configuration' Puppet::FileServing::Configuration.stubs(:configuration).returns(@configuration) @request = Puppet::Indirector::Request.new(:myind, :mymethod, @uri, :environment => "myenv") end describe "when finding files" do before do @mount = stub 'mount', :find => nil @instance = stub('instance', :links= => nil, :collect => nil) end it "should use the configuration to find the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.find(@request) end it "should return nil if it cannot find the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) @file_server.find(@request).should be_nil end it "should use the mount to find the full path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" } @file_server.find(@request) end it "should pass the request when finding a file" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| request == @request } @file_server.find(@request) end it "should return nil if it cannot find a full path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns nil @file_server.find(@request).should be_nil end it "should create an instance with the found path" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" @model.expects(:new).with("/my/file").returns @instance @file_server.find(@request).should equal(@instance) end it "should set 'links' on the instance if it is set in the request options" do @request.options[:links] = true @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" @model.expects(:new).with("/my/file").returns @instance @instance.expects(:links=).with(true) @file_server.find(@request).should equal(@instance) end it "should collect the instance" do @request.options[:links] = true @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:find).with { |key, request| key == "rel/path" }.returns "/my/file" @model.expects(:new).with("/my/file").returns @instance @instance.expects(:collect) @file_server.find(@request).should equal(@instance) end end describe "when searching for instances" do before do @mount = stub 'mount', :search => nil @instance = stub('instance', :links= => nil, :collect => nil) end it "should use the configuration to search the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.search(@request) end it "should return nil if it cannot search the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) @file_server.search(@request).should be_nil end it "should use the mount to search for the full paths" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" } @file_server.search(@request) end it "should pass the request" do @configuration.stubs(:split_path).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| request == @request } @file_server.search(@request) end it "should return nil if searching does not find any full paths" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns nil @file_server.search(@request).should be_nil end it "should create a fileset with each returned path and merge them" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two} FileTest.stubs(:exist?).returns true one = mock 'fileset_one' Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one) two = mock 'fileset_two' Puppet::FileServing::Fileset.expects(:new).with("/two", @request).returns(two) Puppet::FileServing::Fileset.expects(:merge).with(one, two).returns [] @file_server.search(@request) end it "should create an instance with each path resulting from the merger of the filesets" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] FileTest.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two") one = stub 'one', :collect => nil @model.expects(:new).with("/one", :relative_path => "one").returns one two = stub 'two', :collect => nil @model.expects(:new).with("/two", :relative_path => "two").returns two # order can't be guaranteed result = @file_server.search(@request) result.should be_include(one) result.should be_include(two) result.length.should == 2 end it "should set 'links' on the instances if it is set in the request options" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, request| key == "rel/path" }.returns [] FileTest.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") one = stub 'one', :collect => nil @model.expects(:new).with("/one", :relative_path => "one").returns one one.expects(:links=).with true @request.options[:links] = true @file_server.search(@request) end it "should collect the instances" do @configuration.expects(:split_path).with(@request).returns([@mount, "rel/path"]) @mount.expects(:search).with { |key, options| key == "rel/path" }.returns [] FileTest.stubs(:exist?).returns true Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one") one = mock 'one' @model.expects(:new).with("/one", :relative_path => "one").returns one one.expects(:collect) @file_server.search(@request) end end describe "when checking authorization" do before do @request.method = :find @mount = stub 'mount' @configuration.stubs(:split_path).with(@request).returns([@mount, "rel/path"]) @request.stubs(:node).returns("mynode") @request.stubs(:ip).returns("myip") @mount.stubs(:allowed?).with("mynode", "myip").returns "something" end it "should return false when destroying" do @request.method = :destroy @file_server.should_not be_authorized(@request) end it "should return false when saving" do @request.method = :save @file_server.should_not be_authorized(@request) end it "should use the configuration to find the mount and relative path" do @configuration.expects(:split_path).with(@request) @file_server.authorized?(@request) end it "should return false if it cannot find the mount" do @configuration.expects(:split_path).with(@request).returns(nil, nil) @file_server.should_not be_authorized(@request) end it "should return the results of asking the mount whether the node and IP are authorized" do @file_server.authorized?(@request).should == "something" end end end diff --git a/spec/unit/indirector/indirection_spec.rb b/spec/unit/indirector/indirection_spec.rb index 72a0b0c57..8d591c27c 100755 --- a/spec/unit/indirector/indirection_spec.rb +++ b/spec/unit/indirector/indirection_spec.rb @@ -1,856 +1,856 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/indirection' shared_examples_for "Indirection Delegator" do it "should create a request object with the appropriate method name and all of the passed arguments" do request = Puppet::Indirector::Request.new(:indirection, :find, "me", nil) @indirection.expects(:request).with(@method, "mystuff", nil, :one => :two).returns request @terminus.stubs(@method) @indirection.send(@method, "mystuff", :one => :two) end it "should let the :select_terminus method choose the terminus using the created request if the :select_terminus method is available" do # Define the method, so our respond_to? hook matches. class << @indirection def select_terminus(request) end end request = Puppet::Indirector::Request.new(:indirection, :find, "me", nil) @indirection.stubs(:request).returns request @indirection.expects(:select_terminus).with(request).returns :test_terminus @indirection.stubs(:check_authorization) @terminus.expects(@method) @indirection.send(@method, "me") end it "should fail if the :select_terminus hook does not return a terminus name" do # Define the method, so our respond_to? hook matches. class << @indirection def select_terminus(request) end end request = stub 'request', :key => "me", :options => {} @indirection.stubs(:request).returns request @indirection.expects(:select_terminus).with(request).returns nil lambda { @indirection.send(@method, "me") }.should raise_error(ArgumentError) end it "should choose the terminus returned by the :terminus_class method if no :select_terminus method is available" do @indirection.expects(:terminus_class).returns :test_terminus @terminus.expects(@method) @indirection.send(@method, "me") end it "should let the appropriate terminus perform the lookup" do @terminus.expects(@method).with { |r| r.is_a?(Puppet::Indirector::Request) } @indirection.send(@method, "me") end end shared_examples_for "Delegation Authorizer" do before do # So the :respond_to? turns out correctly. class << @terminus def authorized? end end end it "should not check authorization if a node name is not provided" do @terminus.expects(:authorized?).never @terminus.stubs(@method) # The quotes are necessary here, else it looks like a block. @request.stubs(:options).returns({}) @indirection.send(@method, "/my/key") end it "should pass the request to the terminus's authorization method" do @terminus.expects(:authorized?).with { |r| r.is_a?(Puppet::Indirector::Request) }.returns(true) @terminus.stubs(@method) @indirection.send(@method, "/my/key", :node => "mynode") end it "should fail if authorization returns false" do @terminus.expects(:authorized?).returns(false) @terminus.stubs(@method) proc { @indirection.send(@method, "/my/key", :node => "mynode") }.should raise_error(ArgumentError) end it "should continue if authorization returns true" do @terminus.expects(:authorized?).returns(true) @terminus.stubs(@method) @indirection.send(@method, "/my/key", :node => "mynode") end end describe Puppet::Indirector::Indirection do describe "when initializing" do # (LAK) I've no idea how to test this, really. it "should store a reference to itself before it consumes its options" do proc { @indirection = Puppet::Indirector::Indirection.new(Object.new, :testingness, :not_valid_option) }.should raise_error Puppet::Indirector::Indirection.instance(:testingness).should be_instance_of(Puppet::Indirector::Indirection) Puppet::Indirector::Indirection.instance(:testingness).delete end it "should keep a reference to the indirecting model" do model = mock 'model' @indirection = Puppet::Indirector::Indirection.new(model, :myind) @indirection.model.should equal(model) end it "should set the name" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :myind) @indirection.name.should == :myind end it "should require indirections to have unique names" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) proc { Puppet::Indirector::Indirection.new(:test) }.should raise_error(ArgumentError) end it "should extend itself with any specified module" do mod = Module.new @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test, :extend => mod) @indirection.singleton_class.included_modules.should include(mod) end after do @indirection.delete if defined?(@indirection) end end describe "when an instance" do before :each do @terminus_class = mock 'terminus_class' @terminus = mock 'terminus' @terminus_class.stubs(:new).returns(@terminus) @cache = stub 'cache', :name => "mycache" @cache_class = mock 'cache_class' Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @indirection.terminus_class = :test_terminus @instance = stub 'instance', :expiration => nil, :expiration= => nil, :name => "whatever" @name = :mything #@request = stub 'instance', :key => "/my/key", :instance => @instance, :options => {} @request = mock 'instance' end it "should allow setting the ttl" do @indirection.ttl = 300 @indirection.ttl.should == 300 end it "should default to the :runinterval setting, converted to an integer, for its ttl" do Puppet.settings.expects(:value).returns "1800" @indirection.ttl.should == 1800 end it "should calculate the current expiration by adding the TTL to the current time" do @indirection.stubs(:ttl).returns(100) now = Time.now Time.stubs(:now).returns now @indirection.expiration.should == (Time.now + 100) end it "should have a method for creating an indirection request instance" do @indirection.should respond_to(:request) end describe "creates a request" do it "should create it with its name as the request's indirection name" do Puppet::Indirector::Request.expects(:new).with { |name, *other| @indirection.name == name } @indirection.request(:funtest, "yayness") end it "should require a method and key" do Puppet::Indirector::Request.expects(:new).with { |name, method, key, *other| method == :funtest and key == "yayness" } @indirection.request(:funtest, "yayness") end it "should support optional arguments" do Puppet::Indirector::Request.expects(:new).with { |name, method, key, other| other == {:one => :two} } @indirection.request(:funtest, "yayness", :one => :two) end it "should not pass options if none are supplied" do Puppet::Indirector::Request.expects(:new).with { |*args| args.length < 4 } @indirection.request(:funtest, "yayness") end it "should return the request" do request = mock 'request' Puppet::Indirector::Request.expects(:new).returns request @indirection.request(:funtest, "yayness").should equal(request) end end describe "and looking for a model instance" do before { @method = :find } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should return the results of the delegation" do @terminus.expects(:find).returns(@instance) @indirection.find("me").should equal(@instance) end it "should set the expiration date on any instances without one set" do @terminus.stubs(:find).returns(@instance) @indirection.expects(:expiration).returns :yay @instance.expects(:expiration).returns(nil) @instance.expects(:expiration=).with(:yay) @indirection.find("/my/key") end it "should not override an already-set expiration date on returned instances" do @terminus.stubs(:find).returns(@instance) @indirection.expects(:expiration).never @instance.expects(:expiration).returns(:yay) @instance.expects(:expiration=).never @indirection.find("/my/key") end it "should filter the result instance if the terminus supports it" do @terminus.stubs(:find).returns(@instance) @terminus.stubs(:respond_to?).with(:filter).returns(true) @terminus.expects(:filter).with(@instance) @indirection.find("/my/key") end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.stubs(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should first look in the cache for an instance" do @terminus.stubs(:find).never @cache.expects(:find).returns @instance @indirection.find("/my/key") end it "should not look in the cache if the request specifies not to use the cache" do @terminus.expects(:find).returns @instance @cache.expects(:find).never @cache.stubs(:save) @indirection.find("/my/key", :ignore_cache => true) end it "should still save to the cache even if the cache is being ignored during readin" do @terminus.expects(:find).returns @instance @cache.expects(:save) @indirection.find("/my/key", :ignore_cache => true) end it "should only look in the cache if the request specifies not to use the terminus" do @terminus.expects(:find).never @cache.expects(:find) @indirection.find("/my/key", :ignore_terminus => true) end it "should use a request to look in the cache for cached objects" do @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns @instance @cache.stubs(:save) @indirection.find("/my/key") end it "should return the cached object if it is not expired" do @instance.stubs(:expired?).returns false @cache.stubs(:find).returns @instance @indirection.find("/my/key").should equal(@instance) end it "should not fail if the cache fails" do @terminus.stubs(:find).returns @instance @cache.expects(:find).raises ArgumentError @cache.stubs(:save) lambda { @indirection.find("/my/key") }.should_not raise_error end it "should look in the main terminus if the cache fails" do @terminus.expects(:find).returns @instance @cache.expects(:find).raises ArgumentError @cache.stubs(:save) @indirection.find("/my/key").should equal(@instance) end it "should send a debug log if it is using the cached object" do Puppet.expects(:debug) @cache.stubs(:find).returns @instance @indirection.find("/my/key") end it "should not return the cached object if it is expired" do @instance.stubs(:expired?).returns true @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil @indirection.find("/my/key").should be_nil end it "should send an info log if it is using the cached object" do Puppet.expects(:info) @instance.stubs(:expired?).returns true @cache.stubs(:find).returns @instance @terminus.stubs(:find).returns nil @indirection.find("/my/key") end it "should cache any objects not retrieved from the cache" do @cache.expects(:find).returns nil @terminus.expects(:find).returns(@instance) @cache.expects(:save) @indirection.find("/my/key") end it "should use a request to look in the cache for cached objects" do @cache.expects(:find).with { |r| r.method == :find and r.key == "/my/key" }.returns nil @terminus.stubs(:find).returns(@instance) @cache.stubs(:save) @indirection.find("/my/key") end it "should cache the instance using a request with the instance set to the cached object" do @cache.stubs(:find).returns nil @terminus.stubs(:find).returns(@instance) @cache.expects(:save).with { |r| r.method == :save and r.instance == @instance } @indirection.find("/my/key") end it "should send an info log that the object is being cached" do @cache.stubs(:find).returns nil @terminus.stubs(:find).returns(@instance) @cache.stubs(:save) Puppet.expects(:info) @indirection.find("/my/key") end end end describe "and doing a head operation" do before { @method = :head } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should return true if the head method returned true" do @terminus.expects(:head).returns(true) @indirection.head("me").should == true end it "should return false if the head method returned false" do @terminus.expects(:head).returns(false) @indirection.head("me").should == false end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.stubs(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should first look in the cache for an instance" do @terminus.stubs(:find).never @terminus.stubs(:head).never @cache.expects(:find).returns @instance @indirection.head("/my/key").should == true end it "should not save to the cache" do @cache.expects(:find).returns nil @cache.expects(:save).never @terminus.expects(:head).returns true @indirection.head("/my/key").should == true end it "should not fail if the cache fails" do @terminus.stubs(:head).returns true @cache.expects(:find).raises ArgumentError lambda { @indirection.head("/my/key") }.should_not raise_error end it "should look in the main terminus if the cache fails" do @terminus.expects(:head).returns true @cache.expects(:find).raises ArgumentError @indirection.head("/my/key").should == true end it "should send a debug log if it is using the cached object" do Puppet.expects(:debug) @cache.stubs(:find).returns @instance @indirection.head("/my/key") end it "should not accept the cached object if it is expired" do @instance.stubs(:expired?).returns true @cache.stubs(:find).returns @instance @terminus.stubs(:head).returns false @indirection.head("/my/key").should == false end end end describe "and storing a model instance" do before { @method = :save } it "should return the result of the save" do @terminus.stubs(:save).returns "foo" @indirection.save(@instance).should == "foo" end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.stubs(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should return the result of saving to the terminus" do request = stub 'request', :instance => @instance, :node => nil @indirection.expects(:request).returns request @cache.stubs(:save) @terminus.stubs(:save).returns @instance @indirection.save(@instance).should equal(@instance) end it "should use a request to save the object to the cache" do request = stub 'request', :instance => @instance, :node => nil @indirection.expects(:request).returns request @cache.expects(:save).with(request) @terminus.stubs(:save) @indirection.save(@instance) end it "should not save to the cache if the normal save fails" do request = stub 'request', :instance => @instance, :node => nil @indirection.expects(:request).returns request @cache.expects(:save).never @terminus.expects(:save).raises "eh" lambda { @indirection.save(@instance) }.should raise_error end end end describe "and removing a model instance" do before { @method = :destroy } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should return the result of removing the instance" do @terminus.stubs(:destroy).returns "yayness" @indirection.destroy("/my/key").should == "yayness" end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.expects(:new).returns(@cache) @instance.stubs(:expired?).returns false end it "should use a request instance to search in and remove objects from the cache" do destroy = stub 'destroy_request', :key => "/my/key", :node => nil find = stub 'destroy_request', :key => "/my/key", :node => nil @indirection.expects(:request).with(:destroy, "/my/key", nil, optionally(instance_of(Hash))).returns destroy @indirection.expects(:request).with(:find, "/my/key", nil, optionally(instance_of(Hash))).returns find cached = mock 'cache' @cache.expects(:find).with(find).returns cached @cache.expects(:destroy).with(destroy) @terminus.stubs(:destroy) @indirection.destroy("/my/key") end end end describe "and searching for multiple model instances" do before { @method = :search } it_should_behave_like "Indirection Delegator" it_should_behave_like "Delegation Authorizer" it "should set the expiration date on any instances without one set" do @terminus.stubs(:search).returns([@instance]) @indirection.expects(:expiration).returns :yay @instance.expects(:expiration).returns(nil) @instance.expects(:expiration=).with(:yay) @indirection.search("/my/key") end it "should not override an already-set expiration date on returned instances" do @terminus.stubs(:search).returns([@instance]) @indirection.expects(:expiration).never @instance.expects(:expiration).returns(:yay) @instance.expects(:expiration=).never @indirection.search("/my/key") end it "should return the results of searching in the terminus" do @terminus.expects(:search).returns([@instance]) @indirection.search("/my/key").should == [@instance] end end describe "and expiring a model instance" do describe "when caching is not enabled" do it "should do nothing" do @cache_class.expects(:new).never @indirection.expire("/my/key") end end describe "when caching is enabled" do before do @indirection.cache_class = :cache_terminus @cache_class.expects(:new).returns(@cache) @instance.stubs(:expired?).returns false @cached = stub 'cached', :expiration= => nil, :name => "/my/key" end it "should use a request to find within the cache" do @cache.expects(:find).with { |r| r.is_a?(Puppet::Indirector::Request) and r.method == :find } @indirection.expire("/my/key") end it "should do nothing if no such instance is cached" do @cache.expects(:find).returns nil @indirection.expire("/my/key") end it "should log when expiring a found instance" do @cache.expects(:find).returns @cached @cache.stubs(:save) Puppet.expects(:info) @indirection.expire("/my/key") end it "should set the cached instance's expiration to a time in the past" do @cache.expects(:find).returns @cached @cache.stubs(:save) @cached.expects(:expiration=).with { |t| t < Time.now } @indirection.expire("/my/key") end it "should save the now expired instance back into the cache" do @cache.expects(:find).returns @cached @cached.expects(:expiration=).with { |t| t < Time.now } @cache.expects(:save) @indirection.expire("/my/key") end it "should use a request to save the expired resource to the cache" do @cache.expects(:find).returns @cached @cached.expects(:expiration=).with { |t| t < Time.now } @cache.expects(:save).with { |r| r.is_a?(Puppet::Indirector::Request) and r.instance == @cached and r.method == :save }.returns(@cached) @indirection.expire("/my/key") end end end after :each do @indirection.delete end end describe "when managing indirection instances" do it "should allow an indirection to be retrieved by name" do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) Puppet::Indirector::Indirection.instance(:test).should equal(@indirection) end it "should return nil when the named indirection has not been created" do Puppet::Indirector::Indirection.instance(:test).should be_nil end it "should allow an indirection's model to be retrieved by name" do mock_model = mock('model') @indirection = Puppet::Indirector::Indirection.new(mock_model, :test) Puppet::Indirector::Indirection.model(:test).should equal(mock_model) end it "should return nil when no model matches the requested name" do Puppet::Indirector::Indirection.model(:test).should be_nil end after do @indirection.delete if defined?(@indirection) end end describe "when routing to the correct the terminus class" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = stub 'terminus class', :new => @terminus Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :default).returns(@terminus_class) end it "should fail if no terminus class can be picked" do proc { @indirection.terminus_class }.should raise_error(Puppet::DevError) end it "should choose the default terminus class if one is specified" do @indirection.terminus_class = :default @indirection.terminus_class.should equal(:default) end it "should use the provided Puppet setting if told to do so" do Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :my_terminus).returns(mock("terminus_class2")) Puppet.settings.expects(:value).with(:my_setting).returns("my_terminus") @indirection.terminus_setting = :my_setting @indirection.terminus_class.should equal(:my_terminus) end it "should fail if the provided terminus class is not valid" do Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :nosuchclass).returns(nil) proc { @indirection.terminus_class = :nosuchclass }.should raise_error(ArgumentError) end after do @indirection.delete if defined?(@indirection) end end describe "when specifying the terminus class to use" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = stub 'terminus class', :new => @terminus end it "should allow specification of a terminus type" do @indirection.should respond_to(:terminus_class=) end it "should fail to redirect if no terminus type has been specified" do proc { @indirection.find("blah") }.should raise_error(Puppet::DevError) end it "should fail when the terminus class name is an empty string" do proc { @indirection.terminus_class = "" }.should raise_error(ArgumentError) end it "should fail when the terminus class name is nil" do proc { @indirection.terminus_class = nil }.should raise_error(ArgumentError) end it "should fail when the specified terminus class cannot be found" do Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) proc { @indirection.terminus_class = :foo }.should raise_error(ArgumentError) end it "should select the specified terminus class if a terminus class name is provided" do Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(@terminus_class) @indirection.terminus(:foo).should equal(@terminus) end it "should use the configured terminus class if no terminus name is specified" do Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) @indirection.terminus_class = :foo @indirection.terminus.should equal(@terminus) end after do @indirection.delete if defined?(@indirection) end end describe "when managing terminus instances" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = mock 'terminus class' Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) end it "should create an instance of the chosen terminus class" do @terminus_class.stubs(:new).returns(@terminus) @indirection.terminus(:foo).should equal(@terminus) end # Make sure it caches the terminus. it "should return the same terminus instance each time for a given name" do @terminus_class.stubs(:new).returns(@terminus) @indirection.terminus(:foo).should equal(@terminus) @indirection.terminus(:foo).should equal(@terminus) end it "should not create a terminus instance until one is actually needed" do Puppet::Indirector.expects(:terminus).never indirection = Puppet::Indirector::Indirection.new(mock('model'), :lazytest) end after do @indirection.delete end end describe "when deciding whether to cache" do before do @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @terminus = mock 'terminus' @terminus_class = mock 'terminus class' @terminus_class.stubs(:new).returns(@terminus) Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :foo).returns(@terminus_class) @indirection.terminus_class = :foo end it "should provide a method for setting the cache terminus class" do @indirection.should respond_to(:cache_class=) end it "should fail to cache if no cache type has been specified" do proc { @indirection.cache }.should raise_error(Puppet::DevError) end it "should fail to set the cache class when the cache class name is an empty string" do proc { @indirection.cache_class = "" }.should raise_error(ArgumentError) end it "should allow resetting the cache_class to nil" do @indirection.cache_class = nil @indirection.cache_class.should be_nil end it "should fail to set the cache class when the specified cache class cannot be found" do Puppet::Indirector::Terminus.expects(:terminus_class).with(:test, :foo).returns(nil) proc { @indirection.cache_class = :foo }.should raise_error(ArgumentError) end after do @indirection.delete end end describe "when using a cache" do before :each do Puppet.settings.stubs(:value).with("test_terminus").returns("test_terminus") @terminus_class = mock 'terminus_class' @terminus = mock 'terminus' @terminus_class.stubs(:new).returns(@terminus) @cache = mock 'cache' @cache_class = mock 'cache_class' Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :cache_terminus).returns(@cache_class) Puppet::Indirector::Terminus.stubs(:terminus_class).with(:test, :test_terminus).returns(@terminus_class) @indirection = Puppet::Indirector::Indirection.new(mock('model'), :test) @indirection.terminus_class = :test_terminus end describe "and managing the cache terminus" do it "should not create a cache terminus at initialization" do # This is weird, because all of the code is in the setup. If we got # new called on the cache class, we'd get an exception here. end it "should reuse the cache terminus" do @cache_class.expects(:new).returns(@cache) Puppet.settings.stubs(:value).with("test_cache").returns("cache_terminus") @indirection.cache_class = :cache_terminus @indirection.cache.should equal(@cache) @indirection.cache.should equal(@cache) end end describe "and saving" do end describe "and finding" do end after :each do @indirection.delete end end end diff --git a/spec/unit/indirector/instrumentation_data/local_spec.rb b/spec/unit/indirector/instrumentation_data/local_spec.rb index 45ca1e07e..1ca50b8a8 100644 --- a/spec/unit/indirector/instrumentation_data/local_spec.rb +++ b/spec/unit/indirector/instrumentation_data/local_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation/listener' require 'puppet/indirector/instrumentation_data/local' describe Puppet::Indirector::InstrumentationData::Local do it "should be a subclass of the Code terminus" do Puppet::Indirector::InstrumentationData::Local.superclass.should equal(Puppet::Indirector::Code) end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:instrumentation_data) Puppet::Indirector::InstrumentationData::Local.indirection.should equal(indirection) end it "should have its name set to :local" do Puppet::Indirector::InstrumentationData::Local.name.should == :local end end describe Puppet::Indirector::InstrumentationData::Local do before :each do Puppet::Util::Instrumentation.stubs(:listener) @data = Puppet::Indirector::InstrumentationData::Local.new @name = "me" @request = stub 'request', :key => @name end describe "when finding instrumentation data" do it "should return a Instrumentation Data instance matching the key" do end end describe "when searching listeners" do it "should raise an error" do lambda { @data.search(@request) }.should raise_error(Puppet::DevError) end end describe "when saving listeners" do it "should raise an error" do lambda { @data.save(@request) }.should raise_error(Puppet::DevError) end end describe "when destroying listeners" do it "should raise an error" do lambda { @data.destroy(@reques) }.should raise_error(Puppet::DevError) end end end diff --git a/spec/unit/indirector/instrumentation_data/rest_spec.rb b/spec/unit/indirector/instrumentation_data/rest_spec.rb index 762667ea7..5cac4d9be 100644 --- a/spec/unit/indirector/instrumentation_data/rest_spec.rb +++ b/spec/unit/indirector/instrumentation_data/rest_spec.rb @@ -1,11 +1,11 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation/data' require 'puppet/indirector/instrumentation_data/rest' describe Puppet::Indirector::InstrumentationData::Rest do it "should be a subclass of Puppet::Indirector::REST" do Puppet::Indirector::InstrumentationData::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/instrumentation_listener/local_spec.rb b/spec/unit/indirector/instrumentation_listener/local_spec.rb index b251736b4..49b791c44 100644 --- a/spec/unit/indirector/instrumentation_listener/local_spec.rb +++ b/spec/unit/indirector/instrumentation_listener/local_spec.rb @@ -1,65 +1,65 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation/listener' require 'puppet/indirector/instrumentation_listener/local' describe Puppet::Indirector::InstrumentationListener::Local do it "should be a subclass of the Code terminus" do Puppet::Indirector::InstrumentationListener::Local.superclass.should equal(Puppet::Indirector::Code) end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:instrumentation_listener) Puppet::Indirector::InstrumentationListener::Local.indirection.should equal(indirection) end it "should have its name set to :local" do Puppet::Indirector::InstrumentationListener::Local.name.should == :local end end describe Puppet::Indirector::InstrumentationListener::Local do before :each do Puppet::Util::Instrumentation.stubs(:listener) @listener = Puppet::Indirector::InstrumentationListener::Local.new @name = "me" @request = stub 'request', :key => @name end describe "when finding listeners" do it "should return a Instrumentation Listener instance matching the key" do Puppet::Util::Instrumentation.expects(:[]).with("me").returns(:instance) @listener.find(@request).should == :instance end end describe "when searching listeners" do it "should return a list of all loaded Instrumentation Listenesrs irregardless of the given key" do Puppet::Util::Instrumentation.expects(:listeners).returns([:instance1, :instance2]) @listener.search(@request).should == [:instance1, :instance2] end end describe "when saving listeners" do it "should set the new listener to the global listener list" do newlistener = stub 'listener', :name => @name @request.stubs(:instance).returns(newlistener) Puppet::Util::Instrumentation.expects(:[]=).with("me", newlistener) @listener.save(@request) end end describe "when destroying listeners" do it "should raise an error if listener wasn't subscribed" do Puppet::Util::Instrumentation.expects(:[]).with("me").returns(nil) lambda { @listener.destroy(@request) }.should raise_error end it "should unsubscribe the listener" do Puppet::Util::Instrumentation.expects(:[]).with("me").returns(:instancce) Puppet::Util::Instrumentation.expects(:unsubscribe).with(:instancce) @listener.destroy(@request) end end end diff --git a/spec/unit/indirector/instrumentation_listener/rest_spec.rb b/spec/unit/indirector/instrumentation_listener/rest_spec.rb index 6355a1c53..f2e5041ce 100644 --- a/spec/unit/indirector/instrumentation_listener/rest_spec.rb +++ b/spec/unit/indirector/instrumentation_listener/rest_spec.rb @@ -1,11 +1,11 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation/listener' require 'puppet/indirector/instrumentation_listener/rest' describe Puppet::Indirector::InstrumentationListener::Rest do it "should be a subclass of Puppet::Indirector::REST" do Puppet::Indirector::InstrumentationListener::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/instrumentation_probe/local_spec.rb b/spec/unit/indirector/instrumentation_probe/local_spec.rb index 40d64dc2b..1e9a1f6e1 100644 --- a/spec/unit/indirector/instrumentation_probe/local_spec.rb +++ b/spec/unit/indirector/instrumentation_probe/local_spec.rb @@ -1,65 +1,65 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation/indirection_probe' require 'puppet/indirector/instrumentation_probe/local' require 'puppet/util/instrumentation/instrumentable' describe Puppet::Indirector::InstrumentationProbe::Local do it "should be a subclass of the Code terminus" do Puppet::Indirector::InstrumentationProbe::Local.superclass.should equal(Puppet::Indirector::Code) end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:instrumentation_probe) Puppet::Indirector::InstrumentationProbe::Local.indirection.should equal(indirection) end it "should have its name set to :local" do Puppet::Indirector::InstrumentationProbe::Local.name.should == :local end end describe Puppet::Indirector::InstrumentationProbe::Local do before :each do Puppet::Util::Instrumentation.stubs(:listener) @probe = Puppet::Indirector::InstrumentationProbe::Local.new @name = "me" @request = stub 'request', :key => @name end describe "when finding probes" do it "should do nothing" do @probe.find(@request).should be_nil end end describe "when searching probes" do it "should return a list of all loaded probes irregardless of the given key" do instance1 = stub 'instance1', :method => "probe1", :klass => "Klass1" instance2 = stub 'instance2', :method => "probe2", :klass => "Klass2" Puppet::Util::Instrumentation::IndirectionProbe.expects(:new).with("Klass1.probe1").returns(:instance1) Puppet::Util::Instrumentation::IndirectionProbe.expects(:new).with("Klass2.probe2").returns(:instance2) Puppet::Util::Instrumentation::Instrumentable.expects(:each_probe).multiple_yields([instance1], [instance2]) @probe.search(@request).should == [ :instance1, :instance2 ] end end describe "when saving probes" do it "should enable probes" do newprobe = stub 'probe', :name => @name @request.stubs(:instance).returns(newprobe) Puppet::Util::Instrumentation::Instrumentable.expects(:enable_probes) @probe.save(@request) end end describe "when destroying probes" do it "should disable probes" do newprobe = stub 'probe', :name => @name @request.stubs(:instance).returns(newprobe) Puppet::Util::Instrumentation::Instrumentable.expects(:disable_probes) @probe.destroy(@request) end end end diff --git a/spec/unit/indirector/instrumentation_probe/rest_spec.rb b/spec/unit/indirector/instrumentation_probe/rest_spec.rb index 0b73fbdf5..7b9f69766 100644 --- a/spec/unit/indirector/instrumentation_probe/rest_spec.rb +++ b/spec/unit/indirector/instrumentation_probe/rest_spec.rb @@ -1,11 +1,11 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation/indirection_probe' require 'puppet/indirector/instrumentation_probe/rest' describe Puppet::Indirector::InstrumentationProbe::Rest do it "should be a subclass of Puppet::Indirector::REST" do Puppet::Indirector::InstrumentationProbe::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/key/ca_spec.rb b/spec/unit/indirector/key/ca_spec.rb index 990adc975..adafd5274 100755 --- a/spec/unit/indirector/key/ca_spec.rb +++ b/spec/unit/indirector/key/ca_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/key/ca' describe Puppet::SSL::Key::Ca do it "should have documentation" do Puppet::SSL::Key::Ca.doc.should be_instance_of(String) end it "should use the :privatekeydir as the collection directory" do Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" Puppet::SSL::Key::Ca.collection_directory.should == "/key/dir" end it "should store the ca key at the :cakey location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" file = Puppet::SSL::Key::Ca.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/key" end end diff --git a/spec/unit/indirector/key/file_spec.rb b/spec/unit/indirector/key/file_spec.rb index f9abb958f..d1ffea7fd 100755 --- a/spec/unit/indirector/key/file_spec.rb +++ b/spec/unit/indirector/key/file_spec.rb @@ -1,100 +1,100 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/key/file' describe Puppet::SSL::Key::File do it "should have documentation" do Puppet::SSL::Key::File.doc.should be_instance_of(String) end it "should use the :privatekeydir as the collection directory" do Puppet.settings.expects(:value).with(:privatekeydir).returns "/key/dir" Puppet::SSL::Key::File.collection_directory.should == "/key/dir" end it "should store the ca key at the :cakey location" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:cakey).returns "/ca/key" file = Puppet::SSL::Key::File.new file.stubs(:ca?).returns true file.path("whatever").should == "/ca/key" end describe "when choosing the path for the public key" do it "should use the :capub setting location if the key is for the certificate authority" do Puppet.settings.stubs(:value).returns "/fake/dir" Puppet.settings.stubs(:value).with(:capub).returns "/ca/pubkey" Puppet.settings.stubs(:use) @searcher = Puppet::SSL::Key::File.new @searcher.stubs(:ca?).returns true @searcher.public_key_path("whatever").should == "/ca/pubkey" end it "should use the host name plus '.pem' in :publickeydir for normal hosts" do Puppet.settings.stubs(:value).with(:privatekeydir).returns "/private/key/dir" Puppet.settings.stubs(:value).with(:publickeydir).returns "/public/key/dir" Puppet.settings.stubs(:use) @searcher = Puppet::SSL::Key::File.new @searcher.stubs(:ca?).returns false @searcher.public_key_path("whatever").should == "/public/key/dir/whatever.pem" end end describe "when managing private keys" do before do @searcher = Puppet::SSL::Key::File.new @private_key_path = File.join("/fake/key/path") @public_key_path = File.join("/other/fake/key/path") @searcher.stubs(:public_key_path).returns @public_key_path @searcher.stubs(:path).returns @private_key_path FileTest.stubs(:directory?).returns true FileTest.stubs(:writable?).returns true @public_key = stub 'public_key' @real_key = stub 'sslkey', :public_key => @public_key @key = stub 'key', :name => "myname", :content => @real_key @request = stub 'request', :key => "myname", :instance => @key end it "should save the public key when saving the private key" do Puppet.settings.stubs(:writesub) fh = mock 'filehandle' Puppet.settings.expects(:writesub).with(:publickeydir, @public_key_path).yields fh @public_key.expects(:to_pem).returns "my pem" fh.expects(:print).with "my pem" @searcher.save(@request) end it "should destroy the public key when destroying the private key" do File.stubs(:unlink).with(@private_key_path) FileTest.stubs(:exist?).with(@private_key_path).returns true FileTest.expects(:exist?).with(@public_key_path).returns true File.expects(:unlink).with(@public_key_path) @searcher.destroy(@request) end it "should not fail if the public key does not exist when deleting the private key" do File.stubs(:unlink).with(@private_key_path) FileTest.stubs(:exist?).with(@private_key_path).returns true FileTest.expects(:exist?).with(@public_key_path).returns false File.expects(:unlink).with(@public_key_path).never @searcher.destroy(@request) end end end diff --git a/spec/unit/indirector/ldap_spec.rb b/spec/unit/indirector/ldap_spec.rb index 2b40325de..8e01dde39 100755 --- a/spec/unit/indirector/ldap_spec.rb +++ b/spec/unit/indirector/ldap_spec.rb @@ -1,137 +1,137 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/ldap' describe Puppet::Indirector::Ldap do before do @indirection = stub 'indirection', :name => :testing Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @ldap_class = class Testing::MyLdap < Puppet::Indirector::Ldap self end @connection = mock 'ldap' @searcher = @ldap_class.new end describe "when searching ldap" do before do # Stub everything, and we can selectively replace with an expect as # we need to for testing. @searcher.stubs(:connection).returns(@connection) @searcher.stubs(:search_filter).returns(:filter) @searcher.stubs(:search_base).returns(:base) @searcher.stubs(:process) @request = stub 'request', :key => "yay" end it "should call the ldapsearch method with the search filter" do @searcher.expects(:search_filter).with("yay").returns("yay's filter") @searcher.expects(:ldapsearch).with("yay's filter") @searcher.find @request end it "should fail if no block is passed to the ldapsearch method" do proc { @searcher.ldapsearch("blah") }.should raise_error(ArgumentError) end it "should use the results of the ldapbase method as the ldap search base" do @searcher.stubs(:search_base).returns("mybase") @connection.expects(:search).with do |*args| args[0].should == "mybase" true end @searcher.find @request end it "should default to the value of the :search_base setting as the result of the ldapbase method" do Puppet.expects(:[]).with(:ldapbase).returns("myldapbase") searcher = @ldap_class.new searcher.search_base.should == "myldapbase" end it "should use the results of the :search_attributes method as the list of attributes to return" do @searcher.stubs(:search_attributes).returns(:myattrs) @connection.expects(:search).with do |*args| args[3].should == :myattrs true end @searcher.find @request end it "should use depth 2 when searching" do @connection.expects(:search).with do |*args| args[1].should == 2 true end @searcher.find @request end it "should call process() on the first found entry" do @connection.expects(:search).yields("myresult") @searcher.expects(:process).with("myresult") @searcher.find @request end it "should reconnect and retry the search if there is a failure" do run = false @connection.stubs(:search).with do |*args| if run true else run = true raise "failed" end end.yields("myresult") @searcher.expects(:process).with("myresult") @searcher.find @request end it "should not reconnect on failure more than once" do count = 0 @connection.stubs(:search).with do |*args| count += 1 raise ArgumentError, "yay" end proc { @searcher.find(@request) }.should raise_error(Puppet::Error) count.should == 2 end it "should return true if an entry is found" do @connection.expects(:search).yields("result") @searcher.ldapsearch("whatever") { |r| }.should be_true end end describe "when connecting to ldap", :if => Puppet.features.ldap? do it "should create and start a Util::Ldap::Connection instance" do conn = mock 'connection', :connection => "myconn", :start => nil Puppet::Util::Ldap::Connection.expects(:instance).returns conn @searcher.connection.should == "myconn" end it "should only create the ldap connection when asked for it the first time" do conn = mock 'connection', :connection => "myconn", :start => nil Puppet::Util::Ldap::Connection.expects(:instance).returns conn @searcher.connection end it "should cache the connection" do conn = mock 'connection', :connection => "myconn", :start => nil Puppet::Util::Ldap::Connection.expects(:instance).returns conn @searcher.connection.should equal(@searcher.connection) end end describe "when reconnecting to ldap", :if => (Puppet.features.root? and Facter.value("hostname") == "culain") do it "should reconnect to ldap when connections are lost" end end diff --git a/spec/unit/indirector/memory_spec.rb b/spec/unit/indirector/memory_spec.rb index 676acfcca..a52a00ed9 100755 --- a/spec/unit/indirector/memory_spec.rb +++ b/spec/unit/indirector/memory_spec.rb @@ -1,27 +1,27 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/memory' require 'shared_behaviours/memory_terminus' describe Puppet::Indirector::Memory do it_should_behave_like "A Memory Terminus" before do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @memory_class = class Testing::MyMemory < Puppet::Indirector::Memory self end @searcher = @memory_class.new @name = "me" @instance = stub 'instance', :name => @name @request = stub 'request', :key => @name, :instance => @instance end end diff --git a/spec/unit/indirector/node/active_record_spec.rb b/spec/unit/indirector/node/active_record_spec.rb index ffc9511b5..4e9040ebf 100755 --- a/spec/unit/indirector/node/active_record_spec.rb +++ b/spec/unit/indirector/node/active_record_spec.rb @@ -1,41 +1,41 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node' describe "Puppet::Node::ActiveRecord", :if => Puppet.features.rails? && Puppet.features.sqlite? do include PuppetSpec::Files let(:nodename) { "mynode" } let(:fact_values) { {:afact => "a value"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } let(:environment) { Puppet::Node::Environment.new("myenv") } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } let(:node_indirection) { Puppet::Node::ActiveRecord.new } before do require 'puppet/indirector/node/active_record' end it "should be a subclass of the ActiveRecord terminus class" do Puppet::Node::ActiveRecord.ancestors.should be_include(Puppet::Indirector::ActiveRecord) end it "should use Puppet::Rails::Host as its ActiveRecord model" do Puppet::Node::ActiveRecord.ar_model.should equal(Puppet::Rails::Host) end it "should call fact_merge when a node is found" do db_instance = stub 'db_instance' Puppet::Node::ActiveRecord.ar_model.expects(:find_by_name).returns db_instance node = Puppet::Node.new(nodename) db_instance.expects(:to_puppet).returns node Puppet[:statedir] = tmpdir('active_record_tmp') Puppet[:railslog] = '$statedir/rails.log' Puppet::Node::Facts.indirection.expects(:find).with(nodename, :environment => environment).returns(facts) node_indirection.find(request).parameters.should include(fact_values) end end diff --git a/spec/unit/indirector/node/exec_spec.rb b/spec/unit/indirector/node/exec_spec.rb index 876987af3..b19bef62d 100755 --- a/spec/unit/indirector/node/exec_spec.rb +++ b/spec/unit/indirector/node/exec_spec.rb @@ -1,76 +1,76 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/node/exec' require 'puppet/indirector/request' describe Puppet::Node::Exec do before do @indirection = mock 'indirection' Puppet.settings[:external_nodes] = File.expand_path("/echo") @searcher = Puppet::Node::Exec.new end describe "when constructing the command to run" do it "should use the external_node script as the command" do Puppet[:external_nodes] = "/bin/echo" @searcher.command.should == %w{/bin/echo} end it "should throw an exception if no external node command is set" do Puppet[:external_nodes] = "none" proc { @searcher.find(stub('request', :key => "foo")) }.should raise_error(ArgumentError) end end describe "when handling the results of the command" do before do @name = "yay" @node = Puppet::Node.new(@name) @node.stubs(:fact_merge) Puppet::Node.expects(:new).with(@name).returns(@node) @result = {} # Use a local variable so the reference is usable in the execute definition. result = @result @searcher.meta_def(:execute) do |command, arguments| return YAML.dump(result) end @request = Puppet::Indirector::Request.new(:node, :find, @name, nil) end it "should translate the YAML into a Node instance" do # Use an empty hash @searcher.find(@request).should equal(@node) end it "should set the resulting parameters as the node parameters" do @result[:parameters] = {"a" => "b", "c" => "d"} @searcher.find(@request) @node.parameters.should == {"a" => "b", "c" => "d"} end it "should set the resulting classes as the node classes" do @result[:classes] = %w{one two} @searcher.find(@request) @node.classes.should == [ 'one', 'two' ] end it "should merge the node's facts with its parameters" do @node.expects(:fact_merge) @searcher.find(@request) end it "should set the node's environment if one is provided" do @result[:environment] = "yay" @searcher.find(@request) @node.environment.to_s.should == 'yay' end it "should set the node's environment based on the request if not otherwise provided" do @request.environment = "boo" @searcher.find(@request) @node.environment.to_s.should == 'boo' end end end diff --git a/spec/unit/indirector/node/ldap_spec.rb b/spec/unit/indirector/node/ldap_spec.rb index a224488de..8d563b02d 100755 --- a/spec/unit/indirector/node/ldap_spec.rb +++ b/spec/unit/indirector/node/ldap_spec.rb @@ -1,432 +1,432 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/node/ldap' describe Puppet::Node::Ldap do let(:nodename) { "mynode.domain.com" } let(:node_indirection) { Puppet::Node::Ldap.new } let(:environment) { Puppet::Node::Environment.new("myenv") } let(:fact_values) { {:afact => "a value", "one" => "boo"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } before do Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => environment).returns(facts) end describe "when searching for a single node" do let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } it "should convert the hostname into a search filter" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{nodename}))").yields entry node_indirection.name2hash(nodename) end it "should convert any found entry into a hash" do entry = stub 'entry', :dn => 'cn=mynode.domain.com,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(cn=#{nodename}))").yields entry myhash = {"myhash" => true} node_indirection.expects(:entry2hash).with(entry).returns myhash node_indirection.name2hash(nodename).should == myhash end # This heavily tests our entry2hash method, so we don't have to stub out the stupid entry information any more. describe "when an ldap entry is found" do before do @entry = stub 'entry', :dn => 'cn=mynode,ou=hosts,dc=madstop,dc=com', :vals => %w{}, :to_hash => {} node_indirection.stubs(:ldapsearch).yields @entry end it "should convert the entry to a hash" do node_indirection.entry2hash(@entry).should be_instance_of(Hash) end it "should add the entry's common name to the hash if fqdn if false" do node_indirection.entry2hash(@entry,fqdn = false)[:name].should == "mynode" end it "should add the entry's fqdn name to the hash if fqdn if true" do node_indirection.entry2hash(@entry,fqdn = true)[:name].should == "mynode.madstop.com" end it "should add all of the entry's classes to the hash" do @entry.stubs(:vals).with("puppetclass").returns %w{one two} node_indirection.entry2hash(@entry)[:classes].should == %w{one two} end it "should deduplicate class values" do @entry.stubs(:to_hash).returns({}) node_indirection.stubs(:class_attributes).returns(%w{one two}) @entry.stubs(:vals).with("one").returns(%w{a b}) @entry.stubs(:vals).with("two").returns(%w{b c}) node_indirection.entry2hash(@entry)[:classes].should == %w{a b c} end it "should add the entry's environment to the hash" do @entry.stubs(:to_hash).returns("environment" => %w{production}) node_indirection.entry2hash(@entry)[:environment].should == "production" end it "should add all stacked parameters as parameters in the hash" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) result = node_indirection.entry2hash(@entry) result[:parameters]["one"].should == "two" result[:parameters]["three"].should == "four" end it "should not add the stacked parameter as a normal parameter" do @entry.stubs(:vals).with("puppetvar").returns(%w{one=two three=four}) @entry.stubs(:to_hash).returns("puppetvar" => %w{one=two three=four}) node_indirection.entry2hash(@entry)[:parameters]["puppetvar"].should be_nil end it "should add all other attributes as parameters in the hash" do @entry.stubs(:to_hash).returns("foo" => %w{one two}) node_indirection.entry2hash(@entry)[:parameters]["foo"].should == %w{one two} end it "should return single-value parameters as strings, not arrays" do @entry.stubs(:to_hash).returns("foo" => %w{one}) node_indirection.entry2hash(@entry)[:parameters]["foo"].should == "one" end it "should convert 'true' values to the boolean 'true'" do @entry.stubs(:to_hash).returns({"one" => ["true"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == true end it "should convert 'false' values to the boolean 'false'" do @entry.stubs(:to_hash).returns({"one" => ["false"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == false end it "should convert 'true' values to the boolean 'true' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["true", "other"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == [true, "other"] end it "should convert 'false' values to the boolean 'false' inside an array" do @entry.stubs(:to_hash).returns({"one" => ["false", "other"]}) node_indirection.entry2hash(@entry)[:parameters]["one"].should == [false, "other"] end it "should add the parent's name if present" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) node_indirection.entry2hash(@entry)[:parent].should == "foo" end it "should fail if more than one parent is specified" do @entry.stubs(:vals).with("parentnode").returns(%w{foo}) node_indirection.entry2hash(@entry)[:parent].should == "foo" end end it "should search first for the provided key" do node_indirection.expects(:name2hash).with("mynode.domain.com").returns({}) node_indirection.find(request) end it "should search for the short version of the provided key if the key looks like a hostname and no results are found for the key itself" do node_indirection.expects(:name2hash).with("mynode.domain.com").returns(nil) node_indirection.expects(:name2hash).with("mynode").returns({}) node_indirection.find(request) end it "should search for default information if no information can be found for the key" do node_indirection.expects(:name2hash).with("mynode.domain.com").returns(nil) node_indirection.expects(:name2hash).with("mynode").returns(nil) node_indirection.expects(:name2hash).with("default").returns({}) node_indirection.find(request) end it "should return nil if no results are found in ldap" do node_indirection.stubs(:name2hash).returns nil node_indirection.find(request).should be_nil end it "should return a node object if results are found in ldap" do node_indirection.stubs(:name2hash).returns({}) node_indirection.find(request).should be end describe "and node information is found in LDAP" do before do @result = {} node_indirection.stubs(:name2hash).returns @result end it "should create the node with the correct name, even if it was found by a different name" do node_indirection.expects(:name2hash).with(nodename).returns nil node_indirection.expects(:name2hash).with("mynode").returns @result node_indirection.find(request).name.should == nodename end it "should add any classes from ldap" do classes = %w{a b c d} @result[:classes] = classes node_indirection.find(request).classes.should == classes end it "should add all entry attributes as node parameters" do params = {"one" => "two", "three" => "four"} @result[:parameters] = params node_indirection.find(request).parameters.should include(params) end it "should set the node's environment to the environment of the results" do result_env = Puppet::Node::Environment.new("local_test") Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => result_env).returns(facts) @result[:environment] = "local_test" node_indirection.find(request).environment.should == result_env end it "should retain false parameter values" do @result[:parameters] = {} @result[:parameters]["one"] = false node_indirection.find(request).parameters.should include({"one" => false}) end it "should merge the node's facts after the parameters from ldap are assigned" do # Make sure we've got data to start with, so the parameters are actually set. params = {"one" => "yay", "two" => "hooray"} @result[:parameters] = params # Node implements its own merge so that an existing param takes # precedence over facts. We get the same result here by merging params # into facts node_indirection.find(request).parameters.should == facts.values.merge(params) end describe "and a parent node is specified" do before do @entry = {:classes => [], :parameters => {}} @parent = {:classes => [], :parameters => {}} @parent_parent = {:classes => [], :parameters => {}} node_indirection.stubs(:name2hash).with(nodename).returns(@entry) node_indirection.stubs(:name2hash).with('parent').returns(@parent) node_indirection.stubs(:name2hash).with('parent_parent').returns(@parent_parent) node_indirection.stubs(:parent_attribute).returns(:parent) end it "should search for the parent node" do @entry[:parent] = "parent" node_indirection.expects(:name2hash).with(nodename).returns @entry node_indirection.expects(:name2hash).with('parent').returns @parent node_indirection.find(request) end it "should fail if the parent cannot be found" do @entry[:parent] = "parent" node_indirection.expects(:name2hash).with('parent').returns nil proc { node_indirection.find(request) }.should raise_error(Puppet::Error, /Could not find parent node/) end it "should add any parent classes to the node's classes" do @entry[:parent] = "parent" @entry[:classes] = %w{a b} @parent[:classes] = %w{c d} node_indirection.find(request).classes.should == %w{a b c d} end it "should add any parent parameters to the node's parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["three"] = "four" node_indirection.find(request).parameters.should include({"one" => "two", "three" => "four"}) end it "should prefer node parameters over parent parameters" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parameters]["one"] = "three" node_indirection.find(request).parameters.should include({"one" => "two"}) end it "should use the parent's environment if the node has none" do env = Puppet::Node::Environment.new("parent") @entry[:parent] = "parent" @parent[:environment] = "parent" Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => env).returns(facts) node_indirection.find(request).environment.should == env end it "should prefer the node's environment to the parent's" do child_env = Puppet::Node::Environment.new("child") @entry[:parent] = "parent" @entry[:environment] = "child" @parent[:environment] = "parent" Puppet::Node::Facts.indirection.stubs(:find).with(nodename, :environment => child_env).returns(facts) node_indirection.find(request).environment.should == child_env end it "should recursively look up parent information" do @entry[:parent] = "parent" @entry[:parameters]["one"] = "two" @parent[:parent] = "parent_parent" @parent[:parameters]["three"] = "four" @parent_parent[:parameters]["five"] = "six" node_indirection.find(request).parameters.should include("one" => "two", "three" => "four", "five" => "six") end it "should not allow loops in parent declarations" do @entry[:parent] = "parent" @parent[:parent] = nodename proc { node_indirection.find(request) }.should raise_error(ArgumentError) end end end end describe "when searching for multiple nodes" do let(:options) { {:environment => environment} } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, options) } before :each do Puppet::Node::Facts.indirection.stubs(:terminus_class).returns :yaml end it "should find all nodes if no arguments are provided" do node_indirection.expects(:ldapsearch).with("(objectclass=puppetClient)") # LAK:NOTE The search method requires an essentially bogus key. It's # an API problem that I don't really know how to fix. node_indirection.search request end describe "and a class is specified" do it "should find all nodes that are members of that class" do node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one))") options[:class] = "one" node_indirection.search request end end describe "multiple classes are specified" do it "should find all nodes that are members of all classes" do node_indirection.expects(:ldapsearch).with("(&(objectclass=puppetClient)(puppetclass=one)(puppetclass=two))") options[:class] = %w{one two} node_indirection.search request end end it "should process each found entry" do # .yields can't be used to yield multiple values :/ node_indirection.expects(:ldapsearch).yields("one") node_indirection.expects(:entry2hash).with("one",nil).returns(:name => nodename) node_indirection.search request end it "should return a node for each processed entry with the name from the entry" do node_indirection.expects(:ldapsearch).yields("whatever") node_indirection.expects(:entry2hash).with("whatever",nil).returns(:name => nodename) result = node_indirection.search(request) result[0].should be_instance_of(Puppet::Node) result[0].name.should == nodename end it "should merge each node's facts" do node_indirection.stubs(:ldapsearch).yields("one") node_indirection.stubs(:entry2hash).with("one",nil).returns(:name => nodename) node_indirection.search(request)[0].parameters.should include(fact_values) end it "should pass the request's fqdn option to entry2hash" do options[:fqdn] = :hello node_indirection.stubs(:ldapsearch).yields("one") node_indirection.expects(:entry2hash).with("one",:hello).returns(:name => nodename) node_indirection.search(request) end end describe Puppet::Node::Ldap, " when developing the search query" do it "should return the value of the :ldapclassattrs split on commas as the class attributes" do Puppet.stubs(:[]).with(:ldapclassattrs).returns("one,two") node_indirection.class_attributes.should == %w{one two} end it "should return nil as the parent attribute if the :ldapparentattr is set to an empty string" do Puppet.stubs(:[]).with(:ldapparentattr).returns("") node_indirection.parent_attribute.should be_nil end it "should return the value of the :ldapparentattr as the parent attribute" do Puppet.stubs(:[]).with(:ldapparentattr).returns("pere") node_indirection.parent_attribute.should == "pere" end it "should use the value of the :ldapstring as the search filter" do Puppet.stubs(:[]).with(:ldapstring).returns("mystring") node_indirection.search_filter("testing").should == "mystring" end it "should replace '%s' with the node name in the search filter if it is present" do Puppet.stubs(:[]).with(:ldapstring).returns("my%sstring") node_indirection.search_filter("testing").should == "mytestingstring" end it "should not modify the global :ldapstring when replacing '%s' in the search filter" do filter = mock 'filter' filter.expects(:include?).with("%s").returns(true) filter.expects(:gsub).with("%s", "testing").returns("mynewstring") Puppet.stubs(:[]).with(:ldapstring).returns(filter) node_indirection.search_filter("testing").should == "mynewstring" end end describe Puppet::Node::Ldap, " when deciding attributes to search for" do it "should use 'nil' if the :ldapattrs setting is 'all'" do Puppet.stubs(:[]).with(:ldapattrs).returns("all") node_indirection.search_attributes.should be_nil end it "should split the value of :ldapattrs on commas and use the result as the attribute list" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") node_indirection.stubs(:class_attributes).returns([]) node_indirection.stubs(:parent_attribute).returns(nil) node_indirection.search_attributes.should == %w{one two} end it "should add the class attributes to the search attributes if not returning all attributes" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") node_indirection.stubs(:class_attributes).returns(%w{three four}) node_indirection.stubs(:parent_attribute).returns(nil) # Sort them so i don't have to care about return order node_indirection.search_attributes.sort.should == %w{one two three four}.sort end it "should add the parent attribute to the search attributes if not returning all attributes" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") node_indirection.stubs(:class_attributes).returns([]) node_indirection.stubs(:parent_attribute).returns("parent") node_indirection.search_attributes.sort.should == %w{one two parent}.sort end it "should not add nil parent attributes to the search attributes" do Puppet.stubs(:[]).with(:ldapattrs).returns("one,two") node_indirection.stubs(:class_attributes).returns([]) node_indirection.stubs(:parent_attribute).returns(nil) node_indirection.search_attributes.should == %w{one two} end end end diff --git a/spec/unit/indirector/node/memory_spec.rb b/spec/unit/indirector/node/memory_spec.rb index 8b26a47c1..3d5ae4408 100755 --- a/spec/unit/indirector/node/memory_spec.rb +++ b/spec/unit/indirector/node/memory_spec.rb @@ -1,18 +1,18 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/node/memory' require 'shared_behaviours/memory_terminus' describe Puppet::Node::Memory do before do @name = "me" @searcher = Puppet::Node::Memory.new @instance = stub 'instance', :name => @name @request = stub 'request', :key => @name, :instance => @instance end it_should_behave_like "A Memory Terminus" end diff --git a/spec/unit/indirector/node/plain_spec.rb b/spec/unit/indirector/node/plain_spec.rb index 9bfb0d9cd..93174346f 100755 --- a/spec/unit/indirector/node/plain_spec.rb +++ b/spec/unit/indirector/node/plain_spec.rb @@ -1,26 +1,26 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/node/plain' describe Puppet::Node::Plain do let(:nodename) { "mynode" } let(:fact_values) { {:afact => "a value"} } let(:facts) { Puppet::Node::Facts.new(nodename, fact_values) } let(:environment) { Puppet::Node::Environment.new("myenv") } let(:request) { Puppet::Indirector::Request.new(:node, :find, nodename, nil, :environment => environment) } let(:node_indirection) { Puppet::Node::Plain.new } before do Puppet::Node::Facts.indirection.expects(:find).with(nodename, :environment => environment).returns(facts) end it "merges facts into the node" do node_indirection.find(request).parameters.should include(fact_values) end it "should set the node environment from the request" do node_indirection.find(request).environment.should == environment end end diff --git a/spec/unit/indirector/node/rest_spec.rb b/spec/unit/indirector/node/rest_spec.rb index fa5d1baf7..c6b8e1740 100755 --- a/spec/unit/indirector/node/rest_spec.rb +++ b/spec/unit/indirector/node/rest_spec.rb @@ -1,12 +1,12 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/node/rest' describe Puppet::Node::Rest do before do @searcher = Puppet::Node::Rest.new end end diff --git a/spec/unit/indirector/node/store_configs_spec.rb b/spec/unit/indirector/node/store_configs_spec.rb index 6d5f88631..6bd86efac 100755 --- a/spec/unit/indirector/node/store_configs_spec.rb +++ b/spec/unit/indirector/node/store_configs_spec.rb @@ -1,17 +1,17 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node' require 'puppet/indirector/memory' require 'puppet/indirector/node/store_configs' class Puppet::Node::StoreConfigsTesting < Puppet::Indirector::Memory end describe Puppet::Node::StoreConfigs do after :each do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil end it_should_behave_like "a StoreConfigs terminus" end diff --git a/spec/unit/indirector/node/yaml_spec.rb b/spec/unit/indirector/node/yaml_spec.rb index db9ffe435..9dc0cd844 100755 --- a/spec/unit/indirector/node/yaml_spec.rb +++ b/spec/unit/indirector/node/yaml_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node' require 'puppet/indirector/node/yaml' describe Puppet::Node::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Node::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Node::Yaml.doc.should_not be_nil end it "should be registered with the configuration store indirection" do indirection = Puppet::Indirector::Indirection.instance(:node) Puppet::Node::Yaml.indirection.should equal(indirection) end it "should have its name set to :node" do Puppet::Node::Yaml.name.should == :yaml end end diff --git a/spec/unit/indirector/plain_spec.rb b/spec/unit/indirector/plain_spec.rb index 0894f70f0..f3a30aa7d 100755 --- a/spec/unit/indirector/plain_spec.rb +++ b/spec/unit/indirector/plain_spec.rb @@ -1,27 +1,27 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/plain' describe Puppet::Indirector::Plain do before do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = mock 'model' @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) module Testing; end @plain_class = class Testing::MyPlain < Puppet::Indirector::Plain self end @searcher = @plain_class.new @request = stub 'request', :key => "yay" end it "should return return an instance of the indirected model" do object = mock 'object' @model.expects(:new).with(@request.key).returns object @searcher.find(@request).should equal(object) end end diff --git a/spec/unit/indirector/queue_spec.rb b/spec/unit/indirector/queue_spec.rb index eba136bbc..a022fc4ee 100755 --- a/spec/unit/indirector/queue_spec.rb +++ b/spec/unit/indirector/queue_spec.rb @@ -1,121 +1,121 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/queue' class Puppet::Indirector::Queue::TestClient end class FooExampleData attr_accessor :name def self.pson_create(pson) new(pson['data'].to_sym) end def initialize(name = nil) @name = name if name end def render(format = :pson) to_pson end def to_pson(*args) {:type => self.class.to_s, :data => name}.to_pson(*args) end end describe Puppet::Indirector::Queue, :if => Puppet.features.pson? do before :each do @model = mock 'model' @indirection = stub 'indirection', :name => :my_queue, :register_terminus_type => nil, :model => @model Puppet::Indirector::Indirection.stubs(:instance).with(:my_queue).returns(@indirection) module MyQueue; end @store_class = class MyQueue::MyType < Puppet::Indirector::Queue self end @store = @store_class.new @subject_class = FooExampleData @subject = @subject_class.new @subject.name = :me Puppet[:queue_type] = :test_client Puppet::Util::Queue.stubs(:queue_type_to_class).with(:test_client).returns(Puppet::Indirector::Queue::TestClient) @request = stub 'request', :key => :me, :instance => @subject end it "should require PSON" do Puppet.features.expects(:pson?).returns false lambda { @store_class.new }.should raise_error(ArgumentError) end it 'should use the correct client type and queue' do @store.queue.should == :my_queue @store.client.should be_an_instance_of(Puppet::Indirector::Queue::TestClient) end describe "when saving" do it 'should render the instance using pson' do @subject.expects(:render).with(:pson) @store.client.stubs(:publish_message) @store.save(@request) end it "should publish the rendered message to the appropriate queue on the client" do @subject.expects(:render).returns "mypson" @store.client.expects(:publish_message).with(:my_queue, "mypson") @store.save(@request) end it "should catch any exceptions raised" do @store.client.expects(:publish_message).raises ArgumentError lambda { @store.save(@request) }.should raise_error(Puppet::Error) end end describe "when subscribing to the queue" do before do @store_class.stubs(:model).returns @model end it "should use the model's Format support to intern the message from pson" do @model.expects(:convert_from).with(:pson, "mymessage") @store_class.client.expects(:subscribe).yields("mymessage") @store_class.subscribe {|o| o } end it "should yield each interned received message" do @model.stubs(:convert_from).returns "something" @subject_two = @subject_class.new @subject_two.name = :too @store_class.client.expects(:subscribe).with(:my_queue).multiple_yields(@subject, @subject_two) received = [] @store_class.subscribe do |obj| received.push(obj) end received.should == %w{something something} end it "should log but not propagate errors" do @store_class.client.expects(:subscribe).yields("foo") @store_class.expects(:intern).raises(ArgumentError) expect { @store_class.subscribe {|o| o } }.should_not raise_error @logs.length.should == 1 @logs.first.message.should =~ /Error occured with subscription to queue my_queue for indirection my_queue: ArgumentError/ @logs.first.level.should == :err end end end diff --git a/spec/unit/indirector/report/processor_spec.rb b/spec/unit/indirector/report/processor_spec.rb index ebe5e1336..a10b96173 100755 --- a/spec/unit/indirector/report/processor_spec.rb +++ b/spec/unit/indirector/report/processor_spec.rb @@ -1,100 +1,100 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/report/processor' describe Puppet::Transaction::Report::Processor do before do Puppet.settings.stubs(:use).returns(true) end it "should provide a method for saving reports" do Puppet::Transaction::Report::Processor.new.should respond_to(:save) end it "should provide a method for cleaning reports" do Puppet::Transaction::Report::Processor.new.should respond_to(:destroy) end end describe Puppet::Transaction::Report::Processor, " when processing a report" do before do Puppet.settings.stubs(:use) @reporter = Puppet::Transaction::Report::Processor.new @request = stub 'request', :instance => stub("report", :host => 'hostname'), :key => 'node' end it "should not save the report if reports are set to 'none'" do Puppet::Reports.expects(:report).never Puppet[:reports] = 'none' request = Puppet::Indirector::Request.new(:indirection_name, :head, "key", nil) report = Puppet::Transaction::Report.new('apply') request.instance = report @reporter.save(request) end it "should save the report with each configured report type" do Puppet.settings.stubs(:value).with(:reports).returns("one,two") @reporter.send(:reports).should == %w{one two} Puppet::Reports.expects(:report).with('one') Puppet::Reports.expects(:report).with('two') @reporter.save(@request) end it "should destroy reports for each processor that responds to destroy" do Puppet.settings.stubs(:value).with(:reports).returns("http,store") http_report = mock() store_report = mock() store_report.expects(:destroy).with(@request.key) Puppet::Reports.expects(:report).with('http').returns(http_report) Puppet::Reports.expects(:report).with('store').returns(store_report) @reporter.destroy(@request) end end describe Puppet::Transaction::Report::Processor, " when processing a report" do before do Puppet[:reports] = "one" Puppet.settings.stubs(:use) @reporter = Puppet::Transaction::Report::Processor.new @report_type = mock 'one' @dup_report = mock 'dupe report' @dup_report.stubs(:process) @report = Puppet::Transaction::Report.new('apply') @report.expects(:dup).returns(@dup_report) @request = stub 'request', :instance => @report Puppet::Reports.expects(:report).with("one").returns(@report_type) @dup_report.expects(:extend).with(@report_type) end # LAK:NOTE This is stupid, because the code is so short it doesn't # make sense to split it out, which means I just do the same test # three times so the spec looks right. it "should process a duplicate of the report, not the original" do @reporter.save(@request) end it "should extend the report with the report type's module" do @reporter.save(@request) end it "should call the report type's :process method" do @dup_report.expects(:process) @reporter.save(@request) end it "should not raise exceptions" do Puppet[:trace] = false @dup_report.expects(:process).raises(ArgumentError) proc { @reporter.save(@request) }.should_not raise_error end end diff --git a/spec/unit/indirector/report/rest_spec.rb b/spec/unit/indirector/report/rest_spec.rb index 8e969ab4c..c9c56f277 100755 --- a/spec/unit/indirector/report/rest_spec.rb +++ b/spec/unit/indirector/report/rest_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/report/rest' describe Puppet::Transaction::Report::Rest do it "should be a subclass of Puppet::Indirector::REST" do Puppet::Transaction::Report::Rest.superclass.should equal(Puppet::Indirector::REST) end it "should use the :report_server setting in preference to :reportserver" do Puppet.settings[:reportserver] = "reportserver" Puppet.settings[:report_server] = "report_server" Puppet::Transaction::Report::Rest.server.should == "report_server" end it "should use the :report_server setting in preference to :server" do Puppet.settings[:server] = "server" Puppet.settings[:report_server] = "report_server" Puppet::Transaction::Report::Rest.server.should == "report_server" end it "should have a value for report_server and report_port" do Puppet::Transaction::Report::Rest.server.should_not be_nil Puppet::Transaction::Report::Rest.port.should_not be_nil end it "should use the :report SRV service" do Puppet::Transaction::Report::Rest.srv_service.should == :report end end diff --git a/spec/unit/indirector/report/yaml_spec.rb b/spec/unit/indirector/report/yaml_spec.rb index 72bff8261..d1e54f650 100755 --- a/spec/unit/indirector/report/yaml_spec.rb +++ b/spec/unit/indirector/report/yaml_spec.rb @@ -1,28 +1,28 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction/report' require 'puppet/indirector/report/yaml' describe Puppet::Transaction::Report::Yaml do it "should be a subclass of the Yaml terminus" do Puppet::Transaction::Report::Yaml.superclass.should equal(Puppet::Indirector::Yaml) end it "should have documentation" do Puppet::Transaction::Report::Yaml.doc.should_not be_nil end it "should be registered with the report indirection" do indirection = Puppet::Indirector::Indirection.instance(:report) Puppet::Transaction::Report::Yaml.indirection.should equal(indirection) end it "should have its name set to :yaml" do Puppet::Transaction::Report::Yaml.name.should == :yaml end it "should unconditionally save/load from the --lastrunreport setting" do subject.path(:me).should == Puppet[:lastrunreport] end end diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb index a326e367c..ab0bdc4cd 100755 --- a/spec/unit/indirector/request_spec.rb +++ b/spec/unit/indirector/request_spec.rb @@ -1,513 +1,513 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'matchers/json' require 'puppet/indirector/request' require 'puppet/util/pson' describe Puppet::Indirector::Request do describe "when registering the document type" do it "should register its document type with JSON" do PSON.registered_document_types["IndirectorRequest"].should equal(Puppet::Indirector::Request) end end describe "when initializing" do it "should always convert the indirection name to a symbol" do Puppet::Indirector::Request.new("ind", :method, "mykey", nil).indirection_name.should == :ind end it "should use provided value as the key if it is a string" do Puppet::Indirector::Request.new(:ind, :method, "mykey", nil).key.should == "mykey" end it "should use provided value as the key if it is a symbol" do Puppet::Indirector::Request.new(:ind, :method, :mykey, nil).key.should == :mykey end it "should use the name of the provided instance as its key if an instance is provided as the key instead of a string" do instance = mock 'instance', :name => "mykey" request = Puppet::Indirector::Request.new(:ind, :method, nil, instance) request.key.should == "mykey" request.instance.should equal(instance) end it "should support options specified as a hash" do lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil, :one => :two) }.should_not raise_error(ArgumentError) end it "should support nil options" do lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil, nil) }.should_not raise_error(ArgumentError) end it "should support unspecified options" do lambda { Puppet::Indirector::Request.new(:ind, :method, :key, nil) }.should_not raise_error(ArgumentError) end it "should use an empty options hash if nil was provided" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, nil).options.should == {} end it "should default to a nil node" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).node.should be_nil end it "should set its node attribute if provided in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :node => "foo.com").node.should == "foo.com" end it "should default to a nil ip" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).ip.should be_nil end it "should set its ip attribute if provided in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ip => "192.168.0.1").ip.should == "192.168.0.1" end it "should default to being unauthenticated" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_authenticated end it "should set be marked authenticated if configured in the options" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :authenticated => "eh").should be_authenticated end it "should keep its options as a hash even if a node is specified" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :node => "eh").options.should be_instance_of(Hash) end it "should keep its options as a hash even if another option is specified" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :foo => "bar").options.should be_instance_of(Hash) end it "should treat options other than :ip, :node, and :authenticated as options rather than attributes" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :server => "bar").options[:server].should == "bar" end it "should normalize options to use symbols as keys" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, "foo" => "bar").options[:foo].should == "bar" end describe "and the request key is a URI" do let(:file) { File.expand_path("/my/file with spaces") } describe "and the URI is a 'file' URI" do before do @request = Puppet::Indirector::Request.new(:ind, :method, "#{URI.unescape(Puppet::Util.path_to_uri(file).to_s)}", nil) end it "should set the request key to the unescaped full file path" do @request.key.should == file end it "should not set the protocol" do @request.protocol.should be_nil end it "should not set the port" do @request.port.should be_nil end it "should not set the server" do @request.server.should be_nil end end it "should set the protocol to the URI scheme" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff", nil).protocol.should == "http" end it "should set the server if a server is provided" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff", nil).server.should == "host" end it "should set the server and port if both are provided" do Puppet::Indirector::Request.new(:ind, :method, "http://host:543/stuff", nil).port.should == 543 end it "should default to the masterport if the URI scheme is 'puppet'" do Puppet.settings.expects(:value).with(:masterport).returns "321" Puppet::Indirector::Request.new(:ind, :method, "puppet://host/stuff", nil).port.should == 321 end it "should use the provided port if the URI scheme is not 'puppet'" do Puppet::Indirector::Request.new(:ind, :method, "http://host/stuff", nil).port.should == 80 end it "should set the request key to the unescaped key part path from the URI" do Puppet::Indirector::Request.new(:ind, :method, "http://host/environment/terminus/stuff with spaces", nil).key.should == "stuff with spaces" end it "should set the :uri attribute to the full URI" do Puppet::Indirector::Request.new(:ind, :method, "http:///stu ff", nil).uri.should == 'http:///stu ff' end it "should not parse relative URI" do Puppet::Indirector::Request.new(:ind, :method, "foo/bar", nil).uri.should be_nil end it "should not parse opaque URI" do Puppet::Indirector::Request.new(:ind, :method, "mailto:joe", nil).uri.should be_nil end end it "should allow indication that it should not read a cached instance" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ignore_cache => true).should be_ignore_cache end it "should default to not ignoring the cache" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_ignore_cache end it "should allow indication that it should not not read an instance from the terminus" do Puppet::Indirector::Request.new(:ind, :method, :key, nil, :ignore_terminus => true).should be_ignore_terminus end it "should default to not ignoring the terminus" do Puppet::Indirector::Request.new(:ind, :method, :key, nil).should_not be_ignore_terminus end end it "should look use the Indirection class to return the appropriate indirection" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind request = Puppet::Indirector::Request.new(:myind, :method, :key, nil) request.indirection.should equal(ind) end it "should use its indirection to look up the appropriate model" do ind = mock 'indirection' Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns ind request = Puppet::Indirector::Request.new(:myind, :method, :key, nil) ind.expects(:model).returns "mymodel" request.model.should == "mymodel" end it "should fail intelligently when asked to find a model but the indirection cannot be found" do Puppet::Indirector::Indirection.expects(:instance).with(:myind).returns nil request = Puppet::Indirector::Request.new(:myind, :method, :key, nil) lambda { request.model }.should raise_error(ArgumentError) end it "should have a method for determining if the request is plural or singular" do Puppet::Indirector::Request.new(:myind, :method, :key, nil).should respond_to(:plural?) end it "should be considered plural if the method is 'search'" do Puppet::Indirector::Request.new(:myind, :search, :key, nil).should be_plural end it "should not be considered plural if the method is not 'search'" do Puppet::Indirector::Request.new(:myind, :find, :key, nil).should_not be_plural end it "should use its uri, if it has one, as its string representation" do Puppet::Indirector::Request.new(:myind, :find, "foo://bar/baz", nil).to_s.should == "foo://bar/baz" end it "should use its indirection name and key, if it has no uri, as its string representation" do Puppet::Indirector::Request.new(:myind, :find, "key", nil) == "/myind/key" end it "should be able to return the URI-escaped key" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil).escaped_key.should == URI.escape("my key") end it "should have an environment accessor" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => "foo").should respond_to(:environment) end it "should set its environment to an environment instance when a string is specified as its environment" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => "foo").environment.should == Puppet::Node::Environment.new("foo") end it "should use any passed in environment instances as its environment" do env = Puppet::Node::Environment.new("foo") Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => env).environment.should equal(env) end it "should use the default environment when none is provided" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil ).environment.should equal(Puppet::Node::Environment.new) end it "should support converting its options to a hash" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil ).should respond_to(:to_hash) end it "should include all of its attributes when its options are converted to a hash" do Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :node => 'foo').to_hash[:node].should == 'foo' end describe "when building a query string from its options" do before do @request = Puppet::Indirector::Request.new(:myind, :find, "my key", nil) end it "should return an empty query string if there are no options" do @request.stubs(:options).returns nil @request.query_string.should == "" end it "should return an empty query string if the options are empty" do @request.stubs(:options).returns({}) @request.query_string.should == "" end it "should prefix the query string with '?'" do @request.stubs(:options).returns(:one => "two") @request.query_string.should =~ /^\?/ end it "should include all options in the query string, separated by '&'" do @request.stubs(:options).returns(:one => "two", :three => "four") @request.query_string.sub(/^\?/, '').split("&").sort.should == %w{one=two three=four}.sort end it "should ignore nil options" do @request.stubs(:options).returns(:one => "two", :three => nil) @request.query_string.should_not be_include("three") end it "should convert 'true' option values into strings" do @request.stubs(:options).returns(:one => true) @request.query_string.should == "?one=true" end it "should convert 'false' option values into strings" do @request.stubs(:options).returns(:one => false) @request.query_string.should == "?one=false" end it "should convert to a string all option values that are integers" do @request.stubs(:options).returns(:one => 50) @request.query_string.should == "?one=50" end it "should convert to a string all option values that are floating point numbers" do @request.stubs(:options).returns(:one => 1.2) @request.query_string.should == "?one=1.2" end it "should CGI-escape all option values that are strings" do escaping = CGI.escape("one two") @request.stubs(:options).returns(:one => "one two") @request.query_string.should == "?one=#{escaping}" end it "should YAML-dump and CGI-escape arrays" do escaping = CGI.escape(YAML.dump(%w{one two})) @request.stubs(:options).returns(:one => %w{one two}) @request.query_string.should == "?one=#{escaping}" end it "should convert to a string and CGI-escape all option values that are symbols" do escaping = CGI.escape("sym bol") @request.stubs(:options).returns(:one => :"sym bol") @request.query_string.should == "?one=#{escaping}" end it "should fail if options other than booleans or strings are provided" do @request.stubs(:options).returns(:one => {:one => :two}) lambda { @request.query_string }.should raise_error(ArgumentError) end end describe "when converting to json" do before do @request = Puppet::Indirector::Request.new(:facts, :find, "foo", nil) end it "should produce a hash with the document_type set to 'request'" do @request.should set_json_document_type_to("IndirectorRequest") end it "should set the 'key'" do @request.should set_json_attribute("key").to("foo") end it "should include an attribute for its indirection name" do @request.should set_json_attribute("type").to("facts") end it "should include a 'method' attribute set to its method" do @request.should set_json_attribute("method").to("find") end it "should add all attributes under the 'attributes' attribute" do @request.ip = "127.0.0.1" @request.should set_json_attribute("attributes", "ip").to("127.0.0.1") end it "should add all options under the 'attributes' attribute" do @request.options["opt"] = "value" PSON.parse(@request.to_pson)["data"]['attributes']['opt'].should == "value" end it "should include the instance if provided" do facts = Puppet::Node::Facts.new("foo") @request.instance = facts PSON.parse(@request.to_pson)["data"]['instance'].should be_instance_of(Hash) end end describe "when converting from json" do before do @request = Puppet::Indirector::Request.new(:facts, :find, "foo", nil) @klass = Puppet::Indirector::Request @format = Puppet::Network::FormatHandler.format('pson') end def from_json(json) @format.intern(Puppet::Indirector::Request, json) end it "should set the 'key'" do from_json(@request.to_pson).key.should == "foo" end it "should fail if no key is provided" do json = PSON.parse(@request.to_pson) json['data'].delete("key") lambda { from_json(json.to_pson) }.should raise_error(ArgumentError) end it "should set its indirector name" do from_json(@request.to_pson).indirection_name.should == :facts end it "should fail if no type is provided" do json = PSON.parse(@request.to_pson) json['data'].delete("type") lambda { from_json(json.to_pson) }.should raise_error(ArgumentError) end it "should set its method" do from_json(@request.to_pson).method.should == "find" end it "should fail if no method is provided" do json = PSON.parse(@request.to_pson) json['data'].delete("method") lambda { from_json(json.to_pson) }.should raise_error(ArgumentError) end it "should initialize with all attributes and options" do @request.ip = "127.0.0.1" @request.options["opt"] = "value" result = from_json(@request.to_pson) result.options[:opt].should == "value" result.ip.should == "127.0.0.1" end it "should set its instance as an instance if one is provided" do facts = Puppet::Node::Facts.new("foo") @request.instance = facts result = from_json(@request.to_pson) result.instance.should be_instance_of(Puppet::Node::Facts) end end context '#do_request' do before :each do @request = Puppet::Indirector::Request.new(:myind, :find, "my key", nil) end context 'when not using SRV records' do before :each do Puppet.settings[:use_srv_records] = false end it "yields the request with the default server and port when no server or port were specified on the original request" do count = 0 rval = @request.do_request(:puppet, 'puppet.example.com', '90210') do |got| count += 1 got.server.should == 'puppet.example.com' got.port.should == '90210' 'Block return value' end count.should == 1 rval.should == 'Block return value' end end context 'when using SRV records' do before :each do Puppet.settings[:use_srv_records] = true Puppet.settings[:srv_domain] = 'example.com' end it "yields the request with the original server and port unmodified" do @request.server = 'puppet.example.com' @request.port = '90210' count = 0 rval = @request.do_request do |got| count += 1 got.server.should == 'puppet.example.com' got.port.should == '90210' 'Block return value' end count.should == 1 rval.should == 'Block return value' end context "when SRV returns servers" do before :each do @dns_mock = mock('dns') Resolv::DNS.expects(:new).returns(@dns_mock) @port = 7205 @host = '_x-puppet._tcp.example.com' @srv_records = [Resolv::DNS::Resource::IN::SRV.new(0, 0, @port, @host)] @dns_mock.expects(:getresources). with("_x-puppet._tcp.#{Puppet.settings[:srv_domain]}", Resolv::DNS::Resource::IN::SRV). returns(@srv_records) end it "yields a request using the server and port from the SRV record" do count = 0 rval = @request.do_request do |got| count += 1 got.server.should == '_x-puppet._tcp.example.com' got.port.should == 7205 @block_return end count.should == 1 rval.should == @block_return end it "should fall back to the default server when the block raises a SystemCallError" do count = 0 second_pass = nil rval = @request.do_request(:puppet, 'puppet', 8140) do |got| count += 1 if got.server == '_x-puppet._tcp.example.com' then raise SystemCallError, "example failure" else second_pass = got end @block_return end second_pass.server.should == 'puppet' second_pass.port.should == 8140 count.should == 2 rval.should == @block_return end end end end end diff --git a/spec/unit/indirector/resource/active_record_spec.rb b/spec/unit/indirector/resource/active_record_spec.rb index 4aca0a443..302304af5 100755 --- a/spec/unit/indirector/resource/active_record_spec.rb +++ b/spec/unit/indirector/resource/active_record_spec.rb @@ -1,178 +1,178 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' require 'puppet/node/facts' describe "Puppet::Resource::ActiveRecord", :if => can_use_scratch_database? do include PuppetSpec::Files before :each do setup_scratch_database Puppet[:storeconfigs] = true end subject { require 'puppet/indirector/resource/active_record' Puppet::Resource.indirection.terminus(:active_record) } it "should automatically initialize Rails" do # Other tests in the suite may have established the connection, which will # linger; the assertion is just to enforce our assumption about the call, # not because I *really* want to test ActiveRecord works. Better to have # an early failure than wonder why the test overall doesn't DTRT. ActiveRecord::Base.remove_connection ActiveRecord::Base.should_not be_connected subject.should be ActiveRecord::Base.should be_connected end describe "#search" do before :each do Puppet::Rails.init end def search(type, host = 'default.local', filter = nil) args = { :host => host, :filter => filter } subject.search(Puppet::Resource.indirection.request(:search, type, nil, args)) end it "should return an empty array if no resources match" do search("Exec").should == [] end # Assert that this is a case-insensitive rule, too. %w{and or AND OR And Or anD oR}.each do |op| it "should fail if asked to search with #{op.inspect}" do filter = [%w{tag == foo}, op, %w{title == bar}] expect { search("Notify", 'localhost', filter) }. to raise_error Puppet::Error, /not supported/ end end context "with a matching resource" do before :each do host = Puppet::Rails::Host.create!(:name => 'one.local') Puppet::Rails::Resource. create!(:host => host, :restype => 'Exec', :title => 'whammo', :exported => true) end it "should return something responding to `to_resource` if a resource matches" do found = search("Exec") found.length.should == 1 found.map do |item| item.should respond_to :to_resource item.restype.should == "Exec" end end it "should not filter resources that have been found before" do search("Exec").should == search("Exec") end end end describe "#build_active_record_query" do before :each do Puppet::Rails.init end let :type do 'Notify' end def query(type, host, filter = nil) subject.send :build_active_record_query, type, host, filter end it "should exclude all database resources from the host" do host = Puppet::Rails::Host.create! :name => 'one.local' got = query(type, host.name) got.keys.should =~ [:conditions] got[:conditions][0] =~ /\(host_id != \?\)/ got[:conditions].last.should == host.id end it "should join appropriately when filtering on parameters" do filter = %w{propname == propval} got = query(type, 'whatever', filter) got.keys.should =~ [:conditions, :joins] got[:joins].should == { :param_values => :param_name } got[:conditions][0].should =~ /param_names\.name = \?/ got[:conditions][0].should =~ /param_values\.value = \?/ got[:conditions].should be_include filter.first got[:conditions].should be_include filter.last end it "should join appropriately when filtering on tags" do filter = %w{tag == test} got = query(type, 'whatever', filter) got.keys.should =~ [:conditions, :joins] got[:joins].should == {:resource_tags => :puppet_tag} got[:conditions].first.should =~ /puppet_tags/ got[:conditions].should_not be_include filter.first got[:conditions].should be_include filter.last end it "should only search for exported resources with the matching type" do got = query(type, 'whatever') got.keys.should =~ [:conditions] got[:conditions][0].should be_include "(exported=? AND restype=?)" got[:conditions][1].should == true got[:conditions][2].should == type.to_s.capitalize end it "should capitalize the type, since PGSQL is case sensitive" do got = query(type, 'whatever') got[:conditions][2].should == 'Notify' end end describe "#filter_to_active_record" do def filter_to_active_record(input) subject.send :filter_to_active_record, input end [nil, '', 'whatever', 12].each do |input| it "should fail if filter is not an array (with #{input.inspect})" do expect { filter_to_active_record(input) }. to raise_error ArgumentError, /must be arrays/ end end # Not exhaustive, just indicative. ['=', '<>', '=~', '+', '-', '!'].each do |input| it "should fail with unexpected comparison operators (with #{input.inspect})" do expect { filter_to_active_record(["one", input, "two"]) }. to raise_error ArgumentError, /unknown operator/ end end { ["title", "==", "whatever"] => ["title = ?", ["whatever"]], ["title", "!=", "whatever"] => ["title != ?", ["whatever"]], # Technically, these are not supported by Puppet yet, but as we pay # approximately zero cost other than a few minutes writing the tests, # and it would be *harder* to fail on them, nested queries. [["title", "==", "foo"], "or", ["title", "==", "bar"]] => ["(title = ?) OR (title = ?)", ["foo", "bar"]], [["title", "==", "foo"], "or", ["tag", "==", "bar"]] => ["(title = ?) OR (puppet_tags.name = ?)", ["foo", "bar"]], [["title", "==", "foo"], "or", ["param", "==", "bar"]] => ["(title = ?) OR (param_names.name = ? AND param_values.value = ?)", ["foo", "param", "bar"]], [[["title","==","foo"],"or",["tag", "==", "bar"]],"and",["param","!=","baz"]] => ["((title = ?) OR (puppet_tags.name = ?)) AND "+ "(param_names.name = ? AND param_values.value != ?)", ["foo", "bar", "param", "baz"]] }.each do |input, expect| it "should map #{input.inspect} to #{expect.inspect}" do filter_to_active_record(input).should == expect end end end end diff --git a/spec/unit/indirector/resource/ral_spec.rb b/spec/unit/indirector/resource/ral_spec.rb index df68c9b24..c56d6982a 100755 --- a/spec/unit/indirector/resource/ral_spec.rb +++ b/spec/unit/indirector/resource/ral_spec.rb @@ -1,135 +1,135 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "Puppet::Resource::Ral" do describe "find" do before do @request = stub 'request', :key => "user/root" end it "should find an existing instance" do my_resource = stub "my user resource" wrong_instance = stub "wrong user", :name => "bob" my_instance = stub "my user", :name => "root", :to_resource => my_resource require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ wrong_instance, my_instance, wrong_instance ]) Puppet::Resource::Ral.new.find(@request).should == my_resource end it "if there is no instance, it should create one" do wrong_instance = stub "wrong user", :name => "bob" root = mock "Root User" root_resource = mock "Root Resource" require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ wrong_instance, wrong_instance ]) Puppet::Type::User.expects(:new).with(has_entry(:name => "root")).returns(root) root.expects(:to_resource).returns(root_resource) result = Puppet::Resource::Ral.new.find(@request) result.should == root_resource end end describe "search" do before do @request = stub 'request', :key => "user/", :options => {} end it "should convert ral resources into regular resources" do my_resource = stub "my user resource" my_instance = stub "my user", :name => "root", :to_resource => my_resource require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should filter results by name if there's a name in the key" do my_resource = stub "my user resource" my_resource.stubs(:to_resource).returns(my_resource) my_resource.stubs(:[]).with(:name).returns("root") wrong_resource = stub "wrong resource" wrong_resource.stubs(:to_resource).returns(wrong_resource) wrong_resource.stubs(:[]).with(:name).returns("bad") my_instance = stub "my user", :to_resource => my_resource wrong_instance = stub "wrong user", :to_resource => wrong_resource @request = stub 'request', :key => "user/root", :options => {} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should filter results by query parameters" do wrong_resource = stub "my user resource" wrong_resource.stubs(:to_resource).returns(wrong_resource) wrong_resource.stubs(:[]).with(:name).returns("root") my_resource = stub "wrong resource" my_resource.stubs(:to_resource).returns(my_resource) my_resource.stubs(:[]).with(:name).returns("bob") my_instance = stub "my user", :to_resource => my_resource wrong_instance = stub "wrong user", :to_resource => wrong_resource @request = stub 'request', :key => "user/", :options => {:name => "bob"} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should return sorted results" do a_resource = stub "alice resource" a_resource.stubs(:to_resource).returns(a_resource) a_resource.stubs(:title).returns("alice") b_resource = stub "bob resource" b_resource.stubs(:to_resource).returns(b_resource) b_resource.stubs(:title).returns("bob") a_instance = stub "alice user", :to_resource => a_resource b_instance = stub "bob user", :to_resource => b_resource @request = stub 'request', :key => "user/", :options => {} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ b_instance, a_instance ]) Puppet::Resource::Ral.new.search(@request).should == [a_resource, b_resource] end end describe "save" do before do @rebuilt_res = stub 'rebuilt instance' @ral_res = stub 'ral resource', :to_resource => @rebuilt_res @instance = stub 'instance', :to_ral => @ral_res @request = stub 'request', :key => "user/", :instance => @instance @catalog = stub 'catalog' @report = stub 'report' @transaction = stub 'transaction', :report => @report Puppet::Resource::Catalog.stubs(:new).returns(@catalog) @catalog.stubs(:apply).returns(@transaction) @catalog.stubs(:add_resource) end it "should apply a new catalog with a ral object in it" do Puppet::Resource::Catalog.expects(:new).returns(@catalog) @catalog.expects(:add_resource).with(@ral_res) @catalog.expects(:apply).returns(@transaction) Puppet::Resource::Ral.new.save(@request).should end it "should return a regular resource that used to be the ral resource" do Puppet::Resource::Ral.new.save(@request).should == [@rebuilt_res, @report] end end end diff --git a/spec/unit/indirector/resource/rest_spec.rb b/spec/unit/indirector/resource/rest_spec.rb index 70c9a7f11..416e265ea 100755 --- a/spec/unit/indirector/resource/rest_spec.rb +++ b/spec/unit/indirector/resource/rest_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/resource/rest' describe Puppet::Resource::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Resource::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/resource/store_configs_spec.rb b/spec/unit/indirector/resource/store_configs_spec.rb index c6afcac30..af37101d2 100755 --- a/spec/unit/indirector/resource/store_configs_spec.rb +++ b/spec/unit/indirector/resource/store_configs_spec.rb @@ -1,12 +1,12 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource' require 'puppet/indirector/memory' require 'puppet/indirector/resource/store_configs' class Puppet::Resource::StoreConfigsTesting < Puppet::Indirector::Memory end describe Puppet::Resource::StoreConfigs do it_should_behave_like "a StoreConfigs terminus" end diff --git a/spec/unit/indirector/resource_type/parser_spec.rb b/spec/unit/indirector/resource_type/parser_spec.rb index 2c0db235a..626a95b5b 100755 --- a/spec/unit/indirector/resource_type/parser_spec.rb +++ b/spec/unit/indirector/resource_type/parser_spec.rb @@ -1,249 +1,249 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/resource_type/parser' require 'puppet_spec/files' describe Puppet::Indirector::ResourceType::Parser do include PuppetSpec::Files before do @terminus = Puppet::Indirector::ResourceType::Parser.new @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo", nil) @krt = @request.environment.known_resource_types end it "should be registered with the resource_type indirection" do Puppet::Indirector::Terminus.terminus_class(:resource_type, :parser).should equal(Puppet::Indirector::ResourceType::Parser) end describe "when finding" do it "should return any found type from the request's environment" do type = Puppet::Resource::Type.new(:hostclass, "foo") @request.environment.known_resource_types.add(type) @terminus.find(@request).should == type end it "should attempt to load the type if none is found in memory" do dir = tmpdir("find_a_type") FileUtils.mkdir_p(dir) Puppet[:modulepath] = dir # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo::bar", nil) manifest_path = File.join(dir, "foo", "manifests") FileUtils.mkdir_p(manifest_path) File.open(File.join(manifest_path, "bar.pp"), "w") { |f| f.puts "class foo::bar {}" } result = @terminus.find(@request) result.should be_instance_of(Puppet::Resource::Type) result.name.should == "foo::bar" end it "should return nil if no type can be found" do @terminus.find(@request).should be_nil end it "should prefer definitions to nodes" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "foo")) @terminus.find(@request).should == type end end describe "when searching" do describe "when the search key is a wildcard" do before do @request.key = "*" end it "should use the request's environment's list of known resource types" do @request.environment.known_resource_types.expects(:hostclasses).returns({}) @terminus.search(@request) end it "should return all results if '*' is provided as the search string" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) result = @terminus.search(@request) result.should be_include(type) result.should be_include(node) result.should be_include(define) end it "should return all known types" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) result = @terminus.search(@request) result.should be_include(type) result.should be_include(node) result.should be_include(define) end it "should not return the 'main' class" do main = @krt.add(Puppet::Resource::Type.new(:hostclass, "")) # So there is a return value foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) @terminus.search(@request).should_not be_include(main) end it "should return nil if no types can be found" do @terminus.search(@request).should be_nil end it "should load all resource types from all search paths" do dir = tmpdir("searching_in_all") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :search, "*", nil) onepath = File.join(first, "one", "manifests") FileUtils.mkdir_p(onepath) twopath = File.join(first, "two", "manifests") FileUtils.mkdir_p(twopath) File.open(File.join(onepath, "oneklass.pp"), "w") { |f| f.puts "class one::oneklass {}" } File.open(File.join(twopath, "twoklass.pp"), "w") { |f| f.puts "class two::twoklass {}" } result = @terminus.search(@request) result.find { |t| t.name == "one::oneklass" }.should be_instance_of(Puppet::Resource::Type) result.find { |t| t.name == "two::twoklass" }.should be_instance_of(Puppet::Resource::Type) end context "when specifying a 'kind' parameter" do before :each do @klass = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) @node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) @define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) end it "should raise an error if you pass an invalid kind filter" do @request.options[:kind] = "i bet you don't have a kind called this" expect { @terminus.search(@request) }.to raise_error(ArgumentError, /Unrecognized kind filter/) end it "should support filtering for only hostclass results" do @request.options[:kind] = "class" result = @terminus.search(@request) result.should be_include(@klass) result.should_not be_include(@node) result.should_not be_include(@define) end it "should support filtering for only node results" do @request.options[:kind] = "node" result = @terminus.search(@request) result.should_not be_include(@klass) result.should be_include(@node) result.should_not be_include(@define) end it "should support filtering for only definition results" do @request.options[:kind] = "defined_type" result = @terminus.search(@request) result.should_not be_include(@klass) result.should_not be_include(@node) result.should be_include(@define) end end end context "when the search string is not a wildcard" do it "should treat any search string as a regex" do @request.key = "a" foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) bar = @krt.add(Puppet::Resource::Type.new(:hostclass, "bar")) baz = @krt.add(Puppet::Resource::Type.new(:hostclass, "baz")) result = @terminus.search(@request) result.should be_include(bar) result.should be_include(baz) result.should_not be_include(foo) end it "should support kind filtering with a regex" do @request.key = "foo" @request.options[:kind] = "class" foobar = @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar")) foobaz = @krt.add(Puppet::Resource::Type.new(:hostclass, "foobaz")) foobam = @krt.add(Puppet::Resource::Type.new(:definition, "foobam")) fooball = @krt.add(Puppet::Resource::Type.new(:node, "fooball")) result = @terminus.search(@request) result.should be_include(foobar) result.should be_include(foobaz) result.should_not be_include(foobam) result.should_not be_include(fooball) end it "should fail if a provided search string is not a valid regex" do @request.key = "*foo*" # Add one instance so we don't just get an empty array" @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) lambda { @terminus.search(@request) }.should raise_error(ArgumentError) end end it "should not return the 'main' class" do main = @krt.add(Puppet::Resource::Type.new(:hostclass, "")) # So there is a return value foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) @terminus.search(@request).should_not be_include(main) end it "should return nil if no types can be found" do @terminus.search(@request).should be_nil end it "should load all resource types from all search paths" do dir = tmpdir("searching_in_all") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :search, "*", nil) onepath = File.join(first, "one", "manifests") FileUtils.mkdir_p(onepath) twopath = File.join(first, "two", "manifests") FileUtils.mkdir_p(twopath) File.open(File.join(onepath, "oneklass.pp"), "w") { |f| f.puts "class one::oneklass {}" } File.open(File.join(twopath, "twoklass.pp"), "w") { |f| f.puts "class two::twoklass {}" } result = @terminus.search(@request) result.find { |t| t.name == "one::oneklass" }.should be_instance_of(Puppet::Resource::Type) result.find { |t| t.name == "two::twoklass" }.should be_instance_of(Puppet::Resource::Type) end end end diff --git a/spec/unit/indirector/resource_type/rest_spec.rb b/spec/unit/indirector/resource_type/rest_spec.rb index 3d8ddf44c..b2a78b975 100755 --- a/spec/unit/indirector/resource_type/rest_spec.rb +++ b/spec/unit/indirector/resource_type/rest_spec.rb @@ -1,14 +1,14 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/resource_type/rest' describe Puppet::Indirector::ResourceType::Rest do it "should be registered with the resource_type indirection" do Puppet::Indirector::Terminus.terminus_class(:resource_type, :rest).should equal(Puppet::Indirector::ResourceType::Rest) end it "should be a subclass of Puppet::Indirector::Rest" do Puppet::Indirector::ResourceType::Rest.superclass.should == Puppet::Indirector::REST end end diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb index f1f30fa2d..7a89256ad 100755 --- a/spec/unit/indirector/rest_spec.rb +++ b/spec/unit/indirector/rest_spec.rb @@ -1,592 +1,592 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/rest' shared_examples_for "a REST http call" do it "should accept a path" do lambda { @search.send(@method, *@arguments) }.should_not raise_error(ArgumentError) end it "should require a path" do lambda { @searcher.send(@method) }.should raise_error(ArgumentError) end it "should return the results of deserializing the response to the request" do conn = mock 'connection' conn.stubs(:put).returns @response conn.stubs(:delete).returns @response conn.stubs(:get).returns @response Puppet::Network::HttpPool.stubs(:http_instance).returns conn @searcher.expects(:deserialize).with(@response).returns "myobject" @searcher.send(@method, *@arguments).should == 'myobject' end end describe Puppet::Indirector::REST do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) @model = stub('model', :supported_formats => %w{}, :convert_from => nil) @instance = stub('model instance', :name= => nil) @indirection = stub('indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model) Puppet::Indirector::Indirection.expects(:instance).returns(@indirection) module This module Is module A module Test end end end end @rest_class = class This::Is::A::Test::Class < Puppet::Indirector::REST self end end before :each do @response = stub('mock response', :body => 'result', :code => "200") @response.stubs(:[]).with('content-type').returns "text/plain" @response.stubs(:[]).with('content-encoding').returns nil @searcher = @rest_class.new @searcher.stubs(:model).returns @model end it "should include the v1 REST API module" do Puppet::Indirector::REST.ancestors.should be_include(Puppet::Network::HTTP::API::V1) end it "should have a method for specifying what setting a subclass should use to retrieve its server" do @rest_class.should respond_to(:use_server_setting) end it "should use any specified setting to pick the server" do @rest_class.expects(:server_setting).returns :servset Puppet.settings.expects(:value).with(:servset).returns "myserver" @rest_class.server.should == "myserver" end it "should default to :server for the server setting" do @rest_class.expects(:server_setting).returns nil Puppet.settings.expects(:value).with(:server).returns "myserver" @rest_class.server.should == "myserver" end it "should have a method for specifying what setting a subclass should use to retrieve its port" do @rest_class.should respond_to(:use_port_setting) end it "should use any specified setting to pick the port" do @rest_class.expects(:port_setting).returns :servset Puppet.settings.expects(:value).with(:servset).returns "321" @rest_class.port.should == 321 end it "should default to :port for the port setting" do @rest_class.expects(:port_setting).returns nil Puppet.settings.expects(:value).with(:masterport).returns "543" @rest_class.port.should == 543 end describe "when making http requests" do include PuppetSpec::Files it "should provide a suggestive error message when certificate verify failed" do connection = Net::HTTP.new('my_server', 8140) @searcher.stubs(:network).returns(connection) connection.stubs(:get).raises(OpenSSL::SSL::SSLError.new('certificate verify failed')) expect do @searcher.http_request(:get, stub('request')) end.to raise_error(/This is often because the time is out of sync on the server or client/) end it "should provide a helpful error message when hostname was not match with server certificate", :unless => Puppet.features.microsoft_windows? do Puppet[:confdir] = tmpdir('conf') cert = Puppet::SSL::CertificateAuthority.new.generate('not_my_server', :dns_alt_names => 'foo,bar,baz').content connection = Net::HTTP.new('my_server', 8140) @searcher.stubs(:network).returns(connection) ssl_context = OpenSSL::SSL::SSLContext.new ssl_context.stubs(:current_cert).returns(cert) connection.stubs(:get).with do connection.verify_callback.call(true, ssl_context) end.raises(OpenSSL::SSL::SSLError.new('hostname was not match with server certificate')) msg = /Server hostname 'my_server' did not match server certificate; expected one of (.+)/ expect { @searcher.http_request(:get, stub('request')) }.to( raise_error(Puppet::Error, msg) do |error| error.message =~ msg $1.split(', ').should =~ %w[DNS:foo DNS:bar DNS:baz DNS:not_my_server not_my_server] end ) end it "should pass along the error message otherwise" do connection = Net::HTTP.new('my_server', 8140) @searcher.stubs(:network).returns(connection) connection.stubs(:get).raises(OpenSSL::SSL::SSLError.new('some other message')) expect do @searcher.http_request(:get, stub('request')) end.to raise_error(/some other message/) end end it 'should default to :puppet for the srv_service' do Puppet::Indirector::REST.srv_service.should == :puppet end describe "when deserializing responses" do it "should return nil if the response code is 404" do response = mock 'response' response.expects(:code).returns "404" @searcher.deserialize(response).should be_nil end [300,400,403,405,500,501,502,503,504].each { |rc| describe "when the response code is #{rc}" do before :each do @model.expects(:convert_from).never @response = mock 'response' @response.stubs(:code).returns rc.to_s @response.stubs(:[]).with('content-encoding').returns nil @response.stubs(:message).returns "There was a problem (header)" end it "should fail" do @response.stubs(:body).returns nil lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError) end it "should take the error message from the body, if present" do @response.stubs(:body).returns "There was a problem (body)" lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (body)") end it "should take the error message from the response header if the body is empty" do @response.stubs(:body).returns "" lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") end it "should take the error message from the response header if the body is absent" do @response.stubs(:body).returns nil lambda { @searcher.deserialize(@response) }.should raise_error(Net::HTTPError,"Error #{rc} on SERVER: There was a problem (header)") end describe "and with http compression" do it "should uncompress the body" do @response.stubs(:body).returns("compressed body") @searcher.expects(:uncompress_body).with(@response).returns("uncompressed") lambda { @searcher.deserialize(@response) }.should raise_error { |e| e.message =~ /uncompressed/ } end end end } it "should return the results of converting from the format specified by the content-type header if the response code is in the 200s" do @model.expects(:convert_from).with("myformat", "mydata").returns "myobject" response = mock 'response' response.stubs(:[]).with("content-type").returns "myformat" response.stubs(:[]).with("content-encoding").returns nil response.stubs(:body).returns "mydata" response.stubs(:code).returns "200" @searcher.deserialize(response).should == "myobject" end it "should convert and return multiple instances if the return code is in the 200s and 'multiple' is specified" do @model.expects(:convert_from_multiple).with("myformat", "mydata").returns "myobjects" response = mock 'response' response.stubs(:[]).with("content-type").returns "myformat" response.stubs(:[]).with("content-encoding").returns nil response.stubs(:body).returns "mydata" response.stubs(:code).returns "200" @searcher.deserialize(response, true).should == "myobjects" end it "should strip the content-type header to keep only the mime-type" do @model.expects(:convert_from).with("text/plain", "mydata").returns "myobject" response = mock 'response' response.stubs(:[]).with("content-type").returns "text/plain; charset=utf-8" response.stubs(:[]).with("content-encoding").returns nil response.stubs(:body).returns "mydata" response.stubs(:code).returns "200" @searcher.deserialize(response) end it "should uncompress the body" do @model.expects(:convert_from).with("myformat", "uncompressed mydata").returns "myobject" response = mock 'response' response.stubs(:[]).with("content-type").returns "myformat" response.stubs(:body).returns "compressed mydata" response.stubs(:code).returns "200" @searcher.expects(:uncompress_body).with(response).returns("uncompressed mydata") @searcher.deserialize(response).should == "myobject" end end describe "when creating an HTTP client" do before do Puppet.settings.stubs(:value).returns("rest_testing") end it "should use the class's server and port if the indirection request provides neither" do @request = stub 'request', :key => "foo", :server => nil, :port => nil @searcher.class.expects(:port).returns 321 @searcher.class.expects(:server).returns "myserver" Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" @searcher.network(@request).should == "myconn" end it "should use the server from the indirection request if one is present" do @request = stub 'request', :key => "foo", :server => "myserver", :port => nil @searcher.class.stubs(:port).returns 321 Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" @searcher.network(@request).should == "myconn" end it "should use the port from the indirection request if one is present" do @request = stub 'request', :key => "foo", :server => nil, :port => 321 @searcher.class.stubs(:server).returns "myserver" Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn" @searcher.network(@request).should == "myconn" end end describe "when doing a find" do before :each do @connection = stub('mock http connection', :get => @response, :verify_callback= => nil) @searcher.stubs(:network).returns(@connection) # neuter the network connection # Use a key with spaces, so we can test escaping @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar", nil, :environment => "myenv") end describe "with a large body" do it "should use the POST http method" do params = {} 'aa'.upto('zz') do |s| params[s] = 'foo' end # The request special-cases this parameter, and it # won't be passed on to the server, so we remove it here # to avoid a failure. params.delete('ip') @request = Puppet::Indirector::Request.new(:foo, :find, "foo bar", nil, params.merge(:environment => "myenv")) @connection.expects(:post).with do |uri, body| uri == "/myenv/foo/foo%20bar" and body.split("&").sort == params.map {|key,value| "#{key}=#{value}"}.sort end.returns(@response) @searcher.find(@request) end end describe "with a small body" do it "should use the GET http method" do @searcher.expects(:network).returns @connection @connection.expects(:get).returns @response @searcher.find(@request) end end it "should deserialize and return the http response, setting name" do @connection.expects(:get).returns @response instance = stub 'object' instance.expects(:name=) @searcher.expects(:deserialize).with(@response).returns instance @searcher.find(@request).should == instance end it "should deserialize and return the http response, and not require name=" do @connection.expects(:get).returns @response instance = stub 'object' @searcher.expects(:deserialize).with(@response).returns instance @searcher.find(@request).should == instance end it "should use the URI generated by the Handler module" do @connection.expects(:get).with { |path, args| path == "/myenv/foo/foo%20bar?" }.returns(@response) @searcher.find(@request) end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.find(@request) end it "should add Accept-Encoding header" do @searcher.expects(:add_accept_encoding).returns({"accept-encoding" => "gzip"}) @connection.expects(:get).with { |path, args| args["accept-encoding"] == "gzip" }.returns(@response) @searcher.find(@request) end it "should deserialize and return the network response" do @searcher.expects(:deserialize).with(@response).returns @instance @searcher.find(@request).should equal(@instance) end it "should set the name of the resulting instance to the asked-for name" do @searcher.expects(:deserialize).with(@response).returns @instance @instance.expects(:name=).with "foo bar" @searcher.find(@request) end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.find(@request) }.should raise_error(ArgumentError) end end describe "when doing a head" do before :each do @connection = stub('mock http connection', :head => @response, :verify_callback= => nil) @searcher.stubs(:network).returns(@connection) # Use a key with spaces, so we can test escaping @request = Puppet::Indirector::Request.new(:foo, :head, "foo bar", nil) end it "should call the HEAD http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:head).returns @response @searcher.head(@request) end it "should return true if there was a successful http response" do @connection.expects(:head).returns @response @response.stubs(:code).returns "200" @searcher.head(@request).should == true end it "should return false if there was a successful http response" do @connection.expects(:head).returns @response @response.stubs(:code).returns "404" @searcher.head(@request).should == false end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:head).with { |path, args| path == "/my/uri" }.returns(@response) @searcher.head(@request) end end describe "when doing a search" do before :each do @connection = stub('mock http connection', :get => @response, :verify_callback= => nil) @searcher.stubs(:network).returns(@connection) # neuter the network connection @model.stubs(:convert_from_multiple) @request = Puppet::Indirector::Request.new(:foo, :search, "foo bar", nil) end it "should call the GET http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:get).returns @response @searcher.search(@request) end it "should deserialize as multiple instances and return the http response" do @connection.expects(:get).returns @response @searcher.expects(:deserialize).with(@response, true).returns "myobject" @searcher.search(@request).should == 'myobject' end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/mys/uri" @connection.expects(:get).with { |path, args| path == "/mys/uri" }.returns(@response) @searcher.search(@request) end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:get).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.search(@request) end it "should return an empty array if serialization returns nil" do @model.stubs(:convert_from_multiple).returns nil @searcher.search(@request).should == [] end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.search(@request) }.should raise_error(ArgumentError) end end describe "when doing a destroy" do before :each do @connection = stub('mock http connection', :delete => @response, :verify_callback= => nil) @searcher.stubs(:network).returns(@connection) # neuter the network connection @request = Puppet::Indirector::Request.new(:foo, :destroy, "foo bar", nil) end it "should call the DELETE http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:delete).returns @response @searcher.destroy(@request) end it "should fail if any options are provided, since DELETE apparently does not support query options" do @request.stubs(:options).returns(:one => "two", :three => "four") lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) end it "should deserialize and return the http response" do @connection.expects(:delete).returns @response @searcher.expects(:deserialize).with(@response).returns "myobject" @searcher.destroy(@request).should == 'myobject' end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:delete).with { |path, args| path == "/my/uri" }.returns(@response) @searcher.destroy(@request) end it "should not include the query string" do @connection.stubs(:delete).returns @response @searcher.destroy(@request) end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:delete).with { |path, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.destroy(@request) end it "should deserialize and return the network response" do @searcher.expects(:deserialize).with(@response).returns @instance @searcher.destroy(@request).should equal(@instance) end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.destroy(@request) }.should raise_error(ArgumentError) end end describe "when doing a save" do before :each do @connection = stub('mock http connection', :put => @response, :verify_callback= => nil) @searcher.stubs(:network).returns(@connection) # neuter the network connection @instance = stub 'instance', :render => "mydata", :mime => "mime" @request = Puppet::Indirector::Request.new(:foo, :save, "foo bar", nil) @request.instance = @instance end it "should call the PUT http method on a network connection" do @searcher.expects(:network).returns @connection @connection.expects(:put).returns @response @searcher.save(@request) end it "should fail if any options are provided, since DELETE apparently does not support query options" do @request.stubs(:options).returns(:one => "two", :three => "four") lambda { @searcher.save(@request) }.should raise_error(ArgumentError) end it "should use the URI generated by the Handler module" do @searcher.expects(:indirection2uri).with(@request).returns "/my/uri" @connection.expects(:put).with { |path, args| path == "/my/uri" }.returns(@response) @searcher.save(@request) end it "should serialize the instance using the default format and pass the result as the body of the request" do @instance.expects(:render).returns "serial_instance" @connection.expects(:put).with { |path, data, args| data == "serial_instance" }.returns @response @searcher.save(@request) end it "should deserialize and return the http response" do @connection.expects(:put).returns @response @searcher.expects(:deserialize).with(@response).returns "myobject" @searcher.save(@request).should == 'myobject' end it "should provide an Accept header containing the list of supported formats joined with commas" do @connection.expects(:put).with { |path, data, args| args["Accept"] == "supported, formats" }.returns(@response) @searcher.model.expects(:supported_formats).returns %w{supported formats} @searcher.save(@request) end it "should provide a Content-Type header containing the mime-type of the sent object" do @connection.expects(:put).with { |path, data, args| args['Content-Type'] == "mime" }.returns(@response) @instance.expects(:mime).returns "mime" @searcher.save(@request) end it "should deserialize and return the network response" do @searcher.expects(:deserialize).with(@response).returns @instance @searcher.save(@request).should equal(@instance) end it "should generate an error when result data deserializes fails" do @searcher.expects(:deserialize).raises(ArgumentError) lambda { @searcher.save(@request) }.should raise_error(ArgumentError) end end context 'dealing with SRV settings' do [ :destroy, :find, :head, :save, :search ].each do |method| it "##{method} passes the SRV service, and fall-back server & port to the request's do_request method" do request = Puppet::Indirector::Request.new(:indirection, method, 'key', nil) stub_response = stub 'response' stub_response.stubs(:code).returns('200') @searcher.stubs(:deserialize) request.expects(:do_request).with(@searcher.class.srv_service, @searcher.class.server, @searcher.class.port).returns(stub_response) @searcher.send(method, request) end end end end diff --git a/spec/unit/indirector/run/local_spec.rb b/spec/unit/indirector/run/local_spec.rb index da4af76d1..0b822aa46 100755 --- a/spec/unit/indirector/run/local_spec.rb +++ b/spec/unit/indirector/run/local_spec.rb @@ -1,19 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/run/local' describe Puppet::Run::Local do it "should be a sublcass of Puppet::Indirector::Code" do Puppet::Run::Local.superclass.should equal(Puppet::Indirector::Code) end it "should call runner.run on save and return the runner" do runner = Puppet::Run.new runner.stubs(:run).returns(runner) request = Puppet::Indirector::Request.new(:indirection, :save, "anything", nil) request.instance = runner = Puppet::Run.new Puppet::Run::Local.new.save(request).should == runner end end diff --git a/spec/unit/indirector/run/rest_spec.rb b/spec/unit/indirector/run/rest_spec.rb index 4b80962d1..f20e2b478 100755 --- a/spec/unit/indirector/run/rest_spec.rb +++ b/spec/unit/indirector/run/rest_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/run/rest' describe Puppet::Run::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Run::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb index f3467bb35..c99d928fe 100755 --- a/spec/unit/indirector/ssl_file_spec.rb +++ b/spec/unit/indirector/ssl_file_spec.rb @@ -1,301 +1,301 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/ssl_file' describe Puppet::Indirector::SslFile do include PuppetSpec::Files before :all do @indirection = stub 'indirection', :name => :testing, :model => @model Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) module Testing; end @file_class = class Testing::MyType < Puppet::Indirector::SslFile self end end before :each do @model = mock 'model' @setting = :certdir @file_class.store_in @setting @path = make_absolute("/thisdoesntexist/my_directory") Puppet[:noop] = false Puppet[@setting] = @path Puppet[:trace] = false end it "should use :main and :ssl upon initialization" do Puppet.settings.expects(:use).with(:main, :ssl) @file_class.new end it "should return a nil collection directory if no directory setting has been provided" do @file_class.store_in nil @file_class.collection_directory.should be_nil end it "should return a nil file location if no location has been provided" do @file_class.store_at nil @file_class.file_location.should be_nil end it "should fail if no store directory or file location has been set" do @file_class.store_in nil @file_class.store_at nil FileTest.expects(:exists?).with(File.dirname(@path)).at_least(0).returns(true) Dir.stubs(:mkdir).with(@path) lambda { @file_class.new }.should raise_error(Puppet::DevError, /No file or directory setting provided/) end describe "when managing ssl files" do before do Puppet.settings.stubs(:use) @searcher = @file_class.new @cert = stub 'certificate', :name => "myname" @certpath = File.join(@path, "myname.pem") @request = stub 'request', :key => @cert.name, :instance => @cert end it "should consider the file a ca file if the name is equal to what the SSL::Host class says is the CA name" do Puppet::SSL::Host.expects(:ca_name).returns "amaca" @searcher.should be_ca("amaca") end describe "when choosing the location for certificates" do it "should set them at the ca setting's path if a ca setting is available and the name resolves to the CA name" do @file_class.store_in nil @file_class.store_at :mysetting @file_class.store_ca_at :casetting Puppet.settings.stubs(:value).with(:casetting).returns "/ca/file" @searcher.expects(:ca?).with(@cert.name).returns true @searcher.path(@cert.name).should == "/ca/file" end it "should set them at the file location if a file setting is available" do @file_class.store_in nil @file_class.store_at :mysetting Puppet.settings.stubs(:value).with(:mysetting).returns "/some/file" @searcher.path(@cert.name).should == "/some/file" end it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do @searcher.path(@cert.name).should == @certpath end ['../foo', '..\\foo', './../foo', '.\\..\\foo', '/foo', '//foo', '\\foo', '\\\\goo', "test\0/../bar", "test\0\\..\\bar", "..\\/bar", "/tmp/bar", "/tmp\\bar", "tmp\\bar", " / bar", " /../ bar", " \\..\\ bar", "c:\\foo", "c:/foo", "\\\\?\\UNC\\bar", "\\\\foo\\bar", "\\\\?\\c:\\foo", "//?/UNC/bar", "//foo/bar", "//?/c:/foo", ].each do |input| it "should resist directory traversal attacks (#{input.inspect})" do expect { @searcher.path(input) }.to raise_error end end # REVISIT: Should probably test MS-DOS reserved names here, too, since # they would represent a vulnerability on a Win32 system, should we ever # support that path. Don't forget that 'CON.foo' == 'CON' # --daniel 2011-09-24 end describe "when finding certificates on disk" do describe "and no certificate is present" do before do # Stub things so the case management bits work. FileTest.stubs(:exist?).with(File.dirname(@certpath)).returns false FileTest.expects(:exist?).with(@certpath).returns false end it "should return nil" do @searcher.find(@request).should be_nil end end describe "and a certificate is present" do before do FileTest.expects(:exist?).with(@certpath).returns true end it "should return an instance of the model, which it should use to read the certificate" do cert = mock 'cert' model = mock 'model' @file_class.stubs(:model).returns model model.expects(:new).with("myname").returns cert cert.expects(:read).with(@certpath) @searcher.find(@request).should equal(cert) end end describe "and a certificate is present but has uppercase letters" do before do @request = stub 'request', :key => "myhost" end # This is kind of more an integration test; it's for #1382, until # the support for upper-case certs can be removed around mid-2009. it "should rename the existing file to the lower-case path" do @path = @searcher.path("myhost") FileTest.expects(:exist?).with(@path).returns(false) dir, file = File.split(@path) FileTest.expects(:exist?).with(dir).returns true Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase] File.expects(:rename).with(File.join(dir, file.upcase), @path) cert = mock 'cert' model = mock 'model' @searcher.stubs(:model).returns model @searcher.model.expects(:new).with("myhost").returns cert cert.expects(:read).with(@path) @searcher.find(@request) end end end describe "when saving certificates to disk" do before do FileTest.stubs(:directory?).returns true FileTest.stubs(:writable?).returns true end it "should fail if the directory is absent" do FileTest.expects(:directory?).with(File.dirname(@certpath)).returns false lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should fail if the directory is not writeable" do FileTest.stubs(:directory?).returns true FileTest.expects(:writable?).with(File.dirname(@certpath)).returns false lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should save to the path the output of converting the certificate to a string" do fh = mock 'filehandle' fh.expects(:print).with("mycert") @searcher.stubs(:write).yields fh @cert.expects(:to_s).returns "mycert" @searcher.save(@request) end describe "and a directory setting is set" do it "should use the Settings class to write the file" do @searcher.class.store_in @setting fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:writesub).with(@setting, @certpath).yields fh @searcher.save(@request) end end describe "and a file location is set" do it "should use the filehandle provided by the Settings" do @searcher.class.store_at @setting fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:write).with(@setting).yields fh @searcher.save(@request) end end describe "and the name is the CA name and a ca setting is set" do it "should use the filehandle provided by the Settings" do @searcher.class.store_at @setting @searcher.class.store_ca_at :castuff Puppet.settings.stubs(:value).with(:castuff).returns "castuff stub" fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:write).with(:castuff).yields fh @searcher.stubs(:ca?).returns true @searcher.save(@request) end end end describe "when destroying certificates" do describe "that do not exist" do before do FileTest.expects(:exist?).with(@certpath).returns false end it "should return false" do @searcher.destroy(@request).should be_false end end describe "that exist" do before do FileTest.expects(:exist?).with(@certpath).returns true end it "should unlink the certificate file" do File.expects(:unlink).with(@certpath) @searcher.destroy(@request) end it "should log that is removing the file" do File.stubs(:exist?).returns true File.stubs(:unlink) Puppet.expects(:notice) @searcher.destroy(@request) end end end describe "when searching for certificates" do before do @model = mock 'model' @file_class.stubs(:model).returns @model end it "should return a certificate instance for all files that exist" do Dir.expects(:entries).with(@path).returns %w{one.pem two.pem} one = stub 'one', :read => nil two = stub 'two', :read => nil @model.expects(:new).with("one").returns one @model.expects(:new).with("two").returns two @searcher.search(@request).should == [one, two] end it "should read each certificate in using the model's :read method" do Dir.expects(:entries).with(@path).returns %w{one.pem} one = stub 'one' one.expects(:read).with(File.join(@path, "one.pem")) @model.expects(:new).with("one").returns one @searcher.search(@request) end it "should skip any files that do not match /\.pem$/" do Dir.expects(:entries).with(@path).returns %w{. .. one.pem} one = stub 'one', :read => nil @model.expects(:new).with("one").returns one @searcher.search(@request) end end end end diff --git a/spec/unit/indirector/status/rest_spec.rb b/spec/unit/indirector/status/rest_spec.rb index b203e6e20..7613d3b88 100755 --- a/spec/unit/indirector/status/rest_spec.rb +++ b/spec/unit/indirector/status/rest_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/status/rest' describe Puppet::Indirector::Status::Rest do it "should be a sublcass of Puppet::Indirector::REST" do Puppet::Indirector::Status::Rest.superclass.should equal(Puppet::Indirector::REST) end end diff --git a/spec/unit/indirector/store_configs_spec.rb b/spec/unit/indirector/store_configs_spec.rb index 4ff22a292..82cb39b96 100755 --- a/spec/unit/indirector/store_configs_spec.rb +++ b/spec/unit/indirector/store_configs_spec.rb @@ -1,8 +1,8 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/store_configs' describe Puppet::Indirector::StoreConfigs do pending "REVISIT: creating an instance requires ludicrous amounts of stubbing, and there is relatively little to actually test here. What to do? Shared behaviours allow us to push down a lot of the testing into the implementation class tests anyhow..." end diff --git a/spec/unit/indirector/terminus_spec.rb b/spec/unit/indirector/terminus_spec.rb index ffd89a20f..cd760b6e9 100755 --- a/spec/unit/indirector/terminus_spec.rb +++ b/spec/unit/indirector/terminus_spec.rb @@ -1,217 +1,217 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/defaults' require 'puppet/indirector' require 'puppet/indirector/memory' describe Puppet::Indirector::Terminus do before :all do class Puppet::AbstractConcept extend Puppet::Indirector indirects :abstract_concept end class Puppet::AbstractConcept::Freedom < Puppet::Indirector::Code end end after :all do # Remove the class, unlinking it from the rest of the system. Puppet.send(:remove_const, :AbstractConcept) end let :terminus_class do Puppet::AbstractConcept::Freedom end let :terminus do terminus_class.new end let :indirection do Puppet::AbstractConcept.indirection end it "should provide a method for setting terminus class documentation" do terminus_class.should respond_to(:desc) end it "should support a class-level name attribute" do terminus_class.should respond_to(:name) end it "should support a class-level indirection attribute" do terminus_class.should respond_to(:indirection) end it "should support a class-level terminus-type attribute" do terminus_class.should respond_to(:terminus_type) end it "should support a class-level model attribute" do terminus_class.should respond_to(:model) end it "should accept indirection instances as its indirection" do # The test is that this shouldn't raise, and should preserve the object # instance exactly, hence "equal", not just "==". terminus_class.indirection = indirection terminus_class.indirection.should equal indirection end it "should look up indirection instances when only a name has been provided" do terminus_class.indirection = :abstract_concept terminus_class.indirection.should equal indirection end it "should fail when provided a name that does not resolve to an indirection" do expect { terminus_class.indirection = :exploding_whales }. should raise_error(ArgumentError) # We should still have the default indirection. terminus_class.indirection.should equal indirection end describe "when a terminus instance" do it "should return the class's name as its name" do terminus.name.should == :freedom end it "should return the class's indirection as its indirection" do terminus.indirection.should equal indirection end it "should set the instances's type to the abstract terminus type's name" do terminus.terminus_type.should == :code end it "should set the instances's model to the indirection's model" do terminus.model.should equal indirection.model end end describe "when managing terminus classes" do it "should provide a method for registering terminus classes" do Puppet::Indirector::Terminus.should respond_to(:register_terminus_class) end it "should provide a method for returning terminus classes by name and type" do terminus = stub 'terminus_type', :name => :abstract, :indirection_name => :whatever Puppet::Indirector::Terminus.register_terminus_class(terminus) Puppet::Indirector::Terminus.terminus_class(:whatever, :abstract).should equal(terminus) end it "should set up autoloading for any terminus class types requested" do Puppet::Indirector::Terminus.expects(:instance_load).with(:test2, "puppet/indirector/test2") Puppet::Indirector::Terminus.terminus_class(:test2, :whatever) end it "should load terminus classes that are not found" do # Set up instance loading; it would normally happen automatically Puppet::Indirector::Terminus.instance_load :test1, "puppet/indirector/test1" Puppet::Indirector::Terminus.instance_loader(:test1).expects(:load).with(:yay) Puppet::Indirector::Terminus.terminus_class(:test1, :yay) end it "should fail when no indirection can be found" do Puppet::Indirector::Indirection.expects(:instance).with(:abstract_concept).returns(nil) expect { class Puppet::AbstractConcept::Physics < Puppet::Indirector::Code end }.should raise_error(ArgumentError) end it "should register the terminus class with the terminus base class" do Puppet::Indirector::Terminus.expects(:register_terminus_class).with do |type| type.indirection_name == :abstract_concept and type.name == :intellect end begin class Puppet::AbstractConcept::Intellect < Puppet::Indirector::Code end ensure Puppet::AbstractConcept.send(:remove_const, :Intellect) rescue nil end end end describe "when parsing class constants for indirection and terminus names" do before :each do Puppet::Indirector::Terminus.stubs(:register_terminus_class) end let :subclass do subclass = mock 'subclass' subclass.stubs(:to_s).returns("TestInd::OneTwo") subclass.stubs(:mark_as_abstract_terminus) subclass end it "should fail when anonymous classes are used" do expect { Puppet::Indirector::Terminus.inherited(Class.new) }. should raise_error(Puppet::DevError) end it "should use the last term in the constant for the terminus class name" do subclass.expects(:name=).with(:one_two) subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(subclass) end it "should convert the terminus name to a downcased symbol" do subclass.expects(:name=).with(:one_two) subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(subclass) end it "should use the second to last term in the constant for the indirection name" do subclass.expects(:indirection=).with(:test_ind) subclass.stubs(:name=) subclass.stubs(:terminus_type=) Puppet::Indirector::Memory.inherited(subclass) end it "should convert the indirection name to a downcased symbol" do subclass.expects(:indirection=).with(:test_ind) subclass.stubs(:name=) subclass.stubs(:terminus_type=) Puppet::Indirector::Memory.inherited(subclass) end it "should convert camel case to lower case with underscores as word separators" do subclass.expects(:name=).with(:one_two) subclass.stubs(:indirection=) Puppet::Indirector::Terminus.inherited(subclass) end end describe "when creating terminus class types" do before :all do Puppet::Indirector::Terminus.stubs(:register_terminus_class) class Puppet::Indirector::Terminus::TestTerminusType < Puppet::Indirector::Terminus end end after :all do Puppet::Indirector::Terminus.send(:remove_const, :TestTerminusType) end let :subclass do Puppet::Indirector::Terminus::TestTerminusType end it "should set the name of the abstract subclass to be its class constant" do subclass.name.should == :test_terminus_type end it "should mark abstract terminus types as such" do subclass.should be_abstract_terminus end it "should not allow instances of abstract subclasses to be created" do expect { subclass.new }.should raise_error(Puppet::DevError) end end describe "when listing terminus classes" do it "should list the terminus files available to load" do Puppet::Util::Autoload.any_instance.stubs(:files_to_load).returns ["/foo/bar/baz", "/max/runs/marathon"] Puppet::Indirector::Terminus.terminus_classes('my_stuff').should == [:baz, :marathon] end end end diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb index 7fdfa7e3b..5528cb8e9 100755 --- a/spec/unit/indirector/yaml_spec.rb +++ b/spec/unit/indirector/yaml_spec.rb @@ -1,183 +1,183 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/indirector/yaml' describe Puppet::Indirector::Yaml, " when choosing file location" do before :all do @indirection = stub 'indirection', :name => :my_yaml, :register_terminus_type => nil Puppet::Indirector::Indirection.expects(:instance).with(:my_yaml).returns(@indirection) module MyYaml; end @store_class = class MyYaml::MyType < Puppet::Indirector::Yaml self end end before :each do @store = @store_class.new @subject = Object.new @subject.singleton_class.send(:attr_accessor, :name) @subject.name = :me @dir = "/what/ever" Puppet.settings.stubs(:value).returns("fakesettingdata") Puppet.settings.stubs(:value).with(:clientyamldir).returns(@dir) Puppet.run_mode.stubs(:master?).returns false @request = stub 'request', :key => :me, :instance => @subject end describe Puppet::Indirector::Yaml, " when choosing file location" do it "should use the server_datadir if the run_mode is master" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" @store.path(:me).should =~ %r{^/server/yaml/dir} end it "should use the client yamldir if the run_mode is not master" do Puppet.run_mode.expects(:master?).returns false Puppet.settings.expects(:value).with(:clientyamldir).returns "/client/yaml/dir" @store.path(:me).should =~ %r{^/client/yaml/dir} end it "should use the extension if one is specified" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" @store.path(:me,'.farfignewton').should =~ %r{\.farfignewton$} end it "should assume an extension of .yaml if none is specified" do Puppet.run_mode.expects(:master?).returns true Puppet.settings.expects(:value).with(:yamldir).returns "/server/yaml/dir" @store.path(:me).should =~ %r{\.yaml$} end it "should store all files in a single file root set in the Puppet defaults" do @store.path(:me).should =~ %r{^#{@dir}} end it "should use the terminus name for choosing the subdirectory" do @store.path(:me).should =~ %r{^#{@dir}/my_yaml} end it "should use the object's name to determine the file name" do @store.path(:me).should =~ %r{me.yaml$} end ['../foo', '..\\foo', './../foo', '.\\..\\foo', '/foo', '//foo', '\\foo', '\\\\goo', "test\0/../bar", "test\0\\..\\bar", "..\\/bar", "/tmp/bar", "/tmp\\bar", "tmp\\bar", " / bar", " /../ bar", " \\..\\ bar", "c:\\foo", "c:/foo", "\\\\?\\UNC\\bar", "\\\\foo\\bar", "\\\\?\\c:\\foo", "//?/UNC/bar", "//foo/bar", "//?/c:/foo", ].each do |input| it "should resist directory traversal attacks (#{input.inspect})" do expect { @store.path(input) }.to raise_error end end end describe Puppet::Indirector::Yaml, " when storing objects as YAML" do it "should only store objects that respond to :name" do @request.stubs(:instance).returns Object.new proc { @store.save(@request) }.should raise_error(ArgumentError) end it "should convert Ruby objects to YAML and write them to disk using a write lock" do yaml = @subject.to_yaml file = mock 'file' path = @store.send(:path, @subject.name) FileTest.expects(:exist?).with(File.dirname(path)).returns(true) Puppet::Util.expects(:replace_file).with(path, 0660).yields(file) file.expects(:print).with(yaml) @store.save(@request) end it "should create the indirection subdirectory if it does not exist" do yaml = @subject.to_yaml file = mock 'file' path = @store.send(:path, @subject.name) dir = File.dirname(path) FileTest.expects(:exist?).with(dir).returns(false) Dir.expects(:mkdir).with(dir) Puppet::Util.expects(:replace_file).yields(file) file.expects(:print).with(yaml) @store.save(@request) end end describe "when retrieving YAML" do it "should read YAML in from disk and convert it to Ruby objects" do path = @store.send(:path, @subject.name) yaml = @subject.to_yaml FileTest.expects(:exist?).with(path).returns true File.expects(:read).with(path).returns yaml @store.find(@request).instance_variable_get("@name").should == :me end it "should fail coherently when the stored YAML is invalid" do path = @store.send(:path, @subject.name) FileTest.expects(:exist?).with(path).returns(true) # Something that will fail in yaml File.expects(:read).returns "--- foo:\n 1,2,3\nargh" expect { @store.find(@request) }.should raise_error(Puppet::Error) end end describe Puppet::Indirector::Yaml, " when searching" do it "should return an array of fact instances with one instance for each file when globbing *" do @request = stub 'request', :key => "*", :instance => @subject @one = mock 'one' @two = mock 'two' @store.expects(:path).with(@request.key,'').returns :glob Dir.expects(:glob).with(:glob).returns(%w{one.yaml two.yaml}) YAML.expects(:load_file).with("one.yaml").returns @one; YAML.expects(:load_file).with("two.yaml").returns @two; @store.search(@request).should == [@one, @two] end it "should return an array containing a single instance of fact when globbing 'one*'" do @request = stub 'request', :key => "one*", :instance => @subject @one = mock 'one' @store.expects(:path).with(@request.key,'').returns :glob Dir.expects(:glob).with(:glob).returns(%w{one.yaml}) YAML.expects(:load_file).with("one.yaml").returns @one; @store.search(@request).should == [@one] end it "should return an empty array when the glob doesn't match anything" do @request = stub 'request', :key => "f*ilglobcanfail*", :instance => @subject @store.expects(:path).with(@request.key,'').returns :glob Dir.expects(:glob).with(:glob).returns [] @store.search(@request).should == [] end describe Puppet::Indirector::Yaml, " when destroying" do it "should unlink the right yaml file if it exists" do path = File.join("/what/ever", @store.class.indirection_name.to_s, @request.key.to_s + ".yaml") File.expects(:exists?).with(path).returns true File.expects(:unlink).with(path) @store.destroy(@request) end it "should not unlink the yaml file if it does not exists" do path = File.join("/what/ever", @store.class.indirection_name.to_s, @request.key.to_s + ".yaml") File.expects(:exists?).with(path).returns false File.expects(:unlink).with(path).never @store.destroy(@request) end end end end diff --git a/spec/unit/indirector_spec.rb b/spec/unit/indirector_spec.rb index 0c09831db..5969e0351 100755 --- a/spec/unit/indirector_spec.rb +++ b/spec/unit/indirector_spec.rb @@ -1,144 +1,144 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/defaults' require 'puppet/indirector' describe Puppet::Indirector, "when configuring routes" do before :each do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil end after :each do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.cache_class = nil end it "should configure routes as requested" do routes = { "node" => { "terminus" => "exec", "cache" => "plain" } } Puppet::Indirector.configure_routes(routes) Puppet::Node.indirection.terminus_class.should == "exec" Puppet::Node.indirection.cache_class.should == "plain" end it "should fail when given an invalid indirection" do routes = { "fake_indirection" => { "terminus" => "exec", "cache" => "plain" } } expect { Puppet::Indirector.configure_routes(routes) }.should raise_error(/fake_indirection does not exist/) end it "should fail when given an invalid terminus" do routes = { "node" => { "terminus" => "fake_terminus", "cache" => "plain" } } expect { Puppet::Indirector.configure_routes(routes) }.should raise_error(/Could not find terminus fake_terminus/) end it "should fail when given an invalid cache" do routes = { "node" => { "terminus" => "exec", "cache" => "fake_cache" } } expect { Puppet::Indirector.configure_routes(routes) }.should raise_error(/Could not find terminus fake_cache/) end end describe Puppet::Indirector, " when available to a model" do before do @thingie = Class.new do extend Puppet::Indirector end end it "should provide a way for the model to register an indirection under a name" do @thingie.should respond_to(:indirects) end end describe Puppet::Indirector, "when registering an indirection" do before do @thingie = Class.new do extend Puppet::Indirector attr_reader :name def initialize(name) @name = name end end end it "should require a name when registering a model" do Proc.new {@thingie.send(:indirects) }.should raise_error(ArgumentError) end it "should create an indirection instance to manage each indirecting model" do @indirection = @thingie.indirects(:test) @indirection.should be_instance_of(Puppet::Indirector::Indirection) end it "should not allow a model to register under multiple names" do # Keep track of the indirection instance so we can delete it on cleanup @indirection = @thingie.indirects :first Proc.new { @thingie.indirects :second }.should raise_error(ArgumentError) end it "should make the indirection available via an accessor" do @indirection = @thingie.indirects :first @thingie.indirection.should equal(@indirection) end it "should pass any provided options to the indirection during initialization" do klass = mock 'terminus class' Puppet::Indirector::Indirection.expects(:new).with(@thingie, :first, {:some => :options}) @indirection = @thingie.indirects :first, :some => :options end it "should extend the class with the Format Handler" do @indirection = @thingie.indirects :first @thingie.singleton_class.ancestors.should be_include(Puppet::Network::FormatHandler) end after do @indirection.delete if @indirection end end describe Puppet::Indirector, "when redirecting a model" do before do @thingie = Class.new do extend Puppet::Indirector attr_reader :name def initialize(name) @name = name end end @indirection = @thingie.send(:indirects, :test) end it "should include the Envelope module in the model" do @thingie.ancestors.should be_include(Puppet::Indirector::Envelope) end after do @indirection.delete end end diff --git a/spec/unit/interface/action_builder_spec.rb b/spec/unit/interface/action_builder_spec.rb index f779a5461..bd9671f2b 100755 --- a/spec/unit/interface/action_builder_spec.rb +++ b/spec/unit/interface/action_builder_spec.rb @@ -1,216 +1,216 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/interface/action_builder' require 'puppet/network/format_handler' describe Puppet::Interface::ActionBuilder do let :face do Puppet::Interface.new(:puppet_interface_actionbuilder, '0.0.1') end it "should build an action" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end end action.should be_a(Puppet::Interface::Action) action.name.should == :foo end it "should define a method on the face which invokes the action" do face = Puppet::Interface.new(:action_builder_test_interface, '0.0.1') do action(:foo) { when_invoked { |options| "invoked the method" } } end face.foo.should == "invoked the method" end it "should require a block" do expect { Puppet::Interface::ActionBuilder.build(nil, :foo) }. should raise_error("Action :foo must specify a block") end it "should require an invocation block" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) {} }.to raise_error(/actions need to know what to do when_invoked; please add the block/) end describe "when handling options" do it "should have a #option DSL function" do method = nil Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end method = self.method(:option) end method.should be_an_instance_of Method end it "should define an option without a block" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end option "--bar" end action.should be_option :bar end it "should accept an empty block" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end option "--bar" do # This space left deliberately blank. end end action.should be_option :bar end end context "inline documentation" do it "should set the summary" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end summary "this is some text" end action.summary.should == "this is some text" end end context "action defaulting" do it "should set the default to true" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end default end action.default.should be_true end it "should not be default by, er, default. *cough*" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end end action.default.should be_false end end context "#when_rendering" do it "should fail if no rendering format is given" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering do true end end }.to raise_error ArgumentError, /must give a rendering format to when_rendering/ end it "should fail if no block is given" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json end }.to raise_error ArgumentError, /must give a block to when_rendering/ end it "should fail if the block takes no arguments" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do true end end }.to raise_error ArgumentError, /the puppet_interface_actionbuilder face foo action takes .* not/ end it "should fail if the when_rendering block takes a different number of arguments than when_invoked" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do |a, b, c| true end end }.to raise_error ArgumentError, /the puppet_interface_actionbuilder face foo action takes .* not 3/ end it "should fail if the block takes a variable number of arguments" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do |*args| true end end }.to raise_error ArgumentError, /the puppet_interface_actionbuilder face foo action takes .* not/ end it "should stash a rendering block" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do |a| true end end action.when_rendering(:json).should be_an_instance_of Method end it "should fail if you try to set the same rendering twice" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do |a| true end when_rendering :json do |a| true end end }.to raise_error ArgumentError, /You can't define a rendering method for json twice/ end it "should work if you set two different renderings" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do |a| true end when_rendering :yaml do |a| true end end action.when_rendering(:json).should be_an_instance_of Method action.when_rendering(:yaml).should be_an_instance_of Method end it "should be bound to the face when called" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end when_rendering :json do |a| self end end action.when_rendering(:json).call(true).should == face end end context "#render_as" do it "should default to nil (eg: based on context)" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end end action.render_as.should be_nil end it "should fail if not rendering format is given" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end render_as end }.to raise_error ArgumentError, /must give a rendering format to render_as/ end Puppet::Network::FormatHandler.formats.each do |name| it "should accept #{name.inspect} format" do action = Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end render_as name end action.render_as.should == name end end [:if_you_define_this_format_you_frighten_me, "json", 12].each do |input| it "should fail if given #{input.inspect}" do expect { Puppet::Interface::ActionBuilder.build(face, :foo) do when_invoked do |options| true end render_as input end }.to raise_error ArgumentError, /#{input.inspect} is not a valid rendering format/ end end end end diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb index 3a84e4f83..8a5b68145 100755 --- a/spec/unit/interface/action_manager_spec.rb +++ b/spec/unit/interface/action_manager_spec.rb @@ -1,282 +1,282 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' # This is entirely an internal class for Interface, so we have to load it instead of our class. require 'puppet/interface' require 'puppet/face' class ActionManagerTester include Puppet::Interface::ActionManager end describe Puppet::Interface::ActionManager do subject { ActionManagerTester.new } describe "when included in a class" do it "should be able to define an action" do subject.action(:foo) do when_invoked { |options| "something "} end end it "should be able to define a 'script' style action" do subject.script :bar do |options| "a bar is where beer is found" end end it "should be able to list defined actions" do subject.action(:foo) do when_invoked { |options| "something" } end subject.action(:bar) do when_invoked { |options| "something" } end subject.actions.should =~ [:foo, :bar] end it "should list 'script' actions" do subject.script :foo do |options| "foo" end subject.actions.should =~ [:foo] end it "should list both script and normal actions" do subject.action :foo do when_invoked do |options| "foo" end end subject.script :bar do |options| "a bar is where beer is found" end subject.actions.should =~ [:foo, :bar] end it "should be able to indicate when an action is defined" do subject.action(:foo) do when_invoked { |options| "something" } end subject.should be_action(:foo) end it "should indicate an action is defined for script actions" do subject.script :foo do |options| "foo" end subject.should be_action :foo end it "should correctly treat action names specified as strings" do subject.action(:foo) do when_invoked { |options| "something" } end subject.should be_action("foo") end end describe "when used to extend a class" do subject { Class.new.extend(Puppet::Interface::ActionManager) } it "should be able to define an action" do subject.action(:foo) do when_invoked { |options| "something "} end end it "should be able to list defined actions" do subject.action(:foo) do when_invoked { |options| "something" } end subject.action(:bar) do when_invoked { |options| "something" } end subject.actions.should include(:bar) subject.actions.should include(:foo) end it "should be able to indicate when an action is defined" do subject.action(:foo) { when_invoked do |options| true end } subject.should be_action(:foo) end end describe "when used both at the class and instance level" do before do @klass = Class.new do include Puppet::Interface::ActionManager extend Puppet::Interface::ActionManager def __invoke_decorations(*args) true end def options() [] end end @instance = @klass.new end it "should be able to define an action at the class level" do @klass.action(:foo) do when_invoked { |options| "something "} end end it "should create an instance method when an action is defined at the class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.foo.should == "something" end it "should be able to define an action at the instance level" do @instance.action(:foo) do when_invoked { |options| "something "} end end it "should create an instance method when an action is defined at the instance level" do @instance.action(:foo) do when_invoked { |options| "something" } end @instance.foo.should == "something" end it "should be able to list actions defined at the class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @klass.action(:bar) do when_invoked { |options| "something" } end @klass.actions.should include(:bar) @klass.actions.should include(:foo) end it "should be able to list actions defined at the instance level" do @instance.action(:foo) do when_invoked { |options| "something" } end @instance.action(:bar) do when_invoked { |options| "something" } end @instance.actions.should include(:bar) @instance.actions.should include(:foo) end it "should be able to list actions defined at both instance and class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.action(:bar) do when_invoked { |options| "something" } end @instance.actions.should include(:bar) @instance.actions.should include(:foo) end it "should be able to indicate when an action is defined at the class level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.should be_action(:foo) end it "should be able to indicate when an action is defined at the instance level" do @klass.action(:foo) do when_invoked { |options| "something" } end @instance.should be_action(:foo) end context "with actions defined in superclass" do before :each do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:parent) do when_invoked { |options| "a" } end @subclass.action(:sub) do when_invoked { |options| "a" } end @instance.action(:instance) do when_invoked { |options| "a" } end end it "should list actions defined in superclasses" do @instance.should be_action(:parent) @instance.should be_action(:sub) @instance.should be_action(:instance) end it "should list inherited actions" do @instance.actions.should =~ [:instance, :parent, :sub] end it "should not duplicate instance actions after fetching them (#7699)" do @instance.actions.should =~ [:instance, :parent, :sub] @instance.get_action(:instance) @instance.actions.should =~ [:instance, :parent, :sub] end it "should not duplicate subclass actions after fetching them (#7699)" do @instance.actions.should =~ [:instance, :parent, :sub] @instance.get_action(:sub) @instance.actions.should =~ [:instance, :parent, :sub] end it "should not duplicate superclass actions after fetching them (#7699)" do @instance.actions.should =~ [:instance, :parent, :sub] @instance.get_action(:parent) @instance.actions.should =~ [:instance, :parent, :sub] end end it "should create an instance method when an action is defined in a superclass" do @subclass = Class.new(@klass) @instance = @subclass.new @klass.action(:foo) do when_invoked { |options| "something" } end @instance.foo.should == "something" end end describe "#action" do it 'should add an action' do subject.action(:foo) { when_invoked do |options| true end } subject.get_action(:foo).should be_a Puppet::Interface::Action end it 'should support default actions' do subject.action(:foo) { when_invoked do |options| true end; default } subject.get_default_action.should == subject.get_action(:foo) end it 'should not support more than one default action' do subject.action(:foo) { when_invoked do |options| true end; default } expect { subject.action(:bar) { when_invoked do |options| true end default } }.should raise_error /cannot both be default/ end end describe "#get_action" do let :parent_class do parent_class = Class.new(Puppet::Interface) parent_class.action(:foo) { when_invoked do |options| true end } parent_class end it "should check that we can find inherited actions when we are a class" do Class.new(parent_class).get_action(:foo).name.should == :foo end it "should check that we can find inherited actions when we are an instance" do instance = parent_class.new(:foo, '0.0.0') instance.get_action(:foo).name.should == :foo end end end diff --git a/spec/unit/interface/action_spec.rb b/spec/unit/interface/action_spec.rb index 6b68eb149..a3809c33c 100755 --- a/spec/unit/interface/action_spec.rb +++ b/spec/unit/interface/action_spec.rb @@ -1,649 +1,649 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/interface/action' describe Puppet::Interface::Action do describe "when validating the action name" do [nil, '', 'foo bar', '-foobar'].each do |input| it "should treat #{input.inspect} as an invalid name" do expect { Puppet::Interface::Action.new(nil, input) }. should raise_error(/is an invalid action name/) end end end describe "#when_invoked=" do it "should fail if the block has arity 0" do pending "Ruby 1.8 (painfully) treats argument-free blocks as arity -1" if RUBY_VERSION =~ /^1\.8/ expect { Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked { } end end }.to raise_error ArgumentError, /foo/ end it "should work with arity 1 blocks" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|one| } end end # -1, because we use option defaulting. :( face.method(:foo).arity.should == -1 end it "should work with arity 2 blocks" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|one, two| } end end # -2, because we use option defaulting. :( face.method(:foo).arity.should == -2 end it "should work with arity 1 blocks that collect arguments" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|*one| } end end # -1, because we use only varargs face.method(:foo).arity.should == -1 end it "should work with arity 2 blocks that collect arguments" do face = Puppet::Interface.new(:action_when_invoked, '1.0.0') do action :foo do when_invoked {|one, *two| } end end # -2, because we take one mandatory argument, and one varargs face.method(:foo).arity.should == -2 end end describe "when invoking" do it "should be able to call other actions on the same object" do face = Puppet::Interface.new(:my_face, '0.0.1') do action(:foo) do when_invoked { |options| 25 } end action(:bar) do when_invoked { |options| "the value of foo is '#{foo}'" } end end face.foo.should == 25 face.bar.should == "the value of foo is '25'" end # bar is a class action calling a class action # quux is a class action calling an instance action # baz is an instance action calling a class action # qux is an instance action calling an instance action it "should be able to call other actions on the same object when defined on a class" do class Puppet::Interface::MyInterfaceBaseClass < Puppet::Interface action(:foo) do when_invoked { |options| 25 } end action(:bar) do when_invoked { |options| "the value of foo is '#{foo}'" } end action(:quux) do when_invoked { |options| "qux told me #{qux}" } end end face = Puppet::Interface::MyInterfaceBaseClass.new(:my_inherited_face, '0.0.1') do action(:baz) do when_invoked { |options| "the value of foo in baz is '#{foo}'" } end action(:qux) do when_invoked { |options| baz } end end face.foo.should == 25 face.bar.should == "the value of foo is '25'" face.quux.should == "qux told me the value of foo in baz is '25'" face.baz.should == "the value of foo in baz is '25'" face.qux.should == "the value of foo in baz is '25'" end context "when calling the Ruby API" do let :face do Puppet::Interface.new(:ruby_api, '1.0.0') do action :bar do option "--bar" when_invoked do |*args| args.last end end end end it "should work when no options are supplied" do options = face.bar options.should == {} end it "should work when options are supplied" do options = face.bar(:bar => "beer") options.should == { :bar => "beer" } end it "should call #validate_and_clean on the action when invoked" do face.get_action(:bar).expects(:validate_and_clean).with({}).returns({}) face.bar 1, :two, 'three' end end end describe "with action-level options" do it "should support options with an empty block" do face = Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| true end option "--bar" do # this line left deliberately blank end end end face.should_not be_option :bar face.get_action(:foo).should be_option :bar end it "should return only action level options when there are no face options" do face = Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| true end option "--bar" end end face.get_action(:foo).options.should =~ [:bar] end describe "option aliases" do let :option do action.get_option :bar end let :action do face.get_action :foo end let :face do Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| options end option "--bar", "--foo", "-b" end end end it "should only list options and not aliases" do action.options.should =~ [:bar] end it "should use the canonical option name when passed aliases" do name = option.name option.aliases.each do |input| face.foo(input => 1).should == { name => 1 } end end end describe "with both face and action options" do let :face do Puppet::Interface.new(:action_level_options, '0.0.1') do action :foo do when_invoked do |options| true end ; option "--bar" end action :baz do when_invoked do |options| true end ; option "--bim" end option "--quux" end end it "should return combined face and action options" do face.get_action(:foo).options.should =~ [:bar, :quux] end it "should fetch options that the face inherited" do parent = Class.new(Puppet::Interface) parent.option "--foo" child = parent.new(:inherited_options, '0.0.1') do option "--bar" action :action do when_invoked do |options| true end option "--baz" end end action = child.get_action(:action) action.should be [:baz, :bar, :foo].each do |name| action.get_option(name).should be_an_instance_of Puppet::Interface::Option end end it "should get an action option when asked" do face.get_action(:foo).get_option(:bar). should be_an_instance_of Puppet::Interface::Option end it "should get a face option when asked" do face.get_action(:foo).get_option(:quux). should be_an_instance_of Puppet::Interface::Option end it "should return options only for this action" do face.get_action(:baz).options.should =~ [:bim, :quux] end end it_should_behave_like "things that declare options" do def add_options_to(&block) face = Puppet::Interface.new(:with_options, '0.0.1') do action(:foo) do when_invoked do |options| true end self.instance_eval &block end end face.get_action(:foo) end end it "should fail when a face option duplicates an action option" do expect { Puppet::Interface.new(:action_level_options, '0.0.1') do option "--foo" action :bar do option "--foo" end end }.should raise_error ArgumentError, /Option foo conflicts with existing option foo/i end it "should fail when a required action option is not provided" do face = Puppet::Interface.new(:required_action_option, '0.0.1') do action(:bar) do option('--foo') { required } when_invoked {|options| } end end expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/ end it "should fail when a required face option is not provided" do face = Puppet::Interface.new(:required_face_option, '0.0.1') do option('--foo') { required } action(:bar) { when_invoked {|options| } } end expect { face.bar }.to raise_error ArgumentError, /The following options are required: foo/ end end context "with decorators" do context "declared locally" do let :face do Puppet::Interface.new(:action_decorators, '0.0.1') do action :bar do when_invoked do |options| true end end def reported; @reported; end def report(arg) (@reported ||= []) << arg end end end it "should execute before advice on action options in declaration order" do face.action(:boo) do option("--foo") { before_action { |_,_,_| report :foo } } option("--bar", '-b') { before_action { |_,_,_| report :bar } } option("-q", "--quux") { before_action { |_,_,_| report :quux } } option("-f") { before_action { |_,_,_| report :f } } option("--baz") { before_action { |_,_,_| report :baz } } when_invoked {|options| } end face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ] end it "should execute after advice on action options in declaration order" do face.action(:boo) do option("--foo") { after_action { |_,_,_| report :foo } } option("--bar", '-b') { after_action { |_,_,_| report :bar } } option("-q", "--quux") { after_action { |_,_,_| report :quux } } option("-f") { after_action { |_,_,_| report :f } } option("--baz") { after_action { |_,_,_| report :baz } } when_invoked {|options| } end face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ].reverse end it "should execute before advice on face options in declaration order" do face.instance_eval do option("--foo") { before_action { |_,_,_| report :foo } } option("--bar", '-b') { before_action { |_,_,_| report :bar } } option("-q", "--quux") { before_action { |_,_,_| report :quux } } option("-f") { before_action { |_,_,_| report :f } } option("--baz") { before_action { |_,_,_| report :baz } } end face.script(:boo) {|options| } face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ] end it "should execute after advice on face options in declaration order" do face.instance_eval do option("--foo") { after_action { |_,_,_| report :foo } } option("--bar", '-b') { after_action { |_,_,_| report :bar } } option("-q", "--quux") { after_action { |_,_,_| report :quux } } option("-f") { after_action { |_,_,_| report :f } } option("--baz") { after_action { |_,_,_| report :baz } } end face.script(:boo) {|options| } face.boo :foo => 1, :bar => 1, :quux => 1, :f => 1, :baz => 1 face.reported.should == [ :foo, :bar, :quux, :f, :baz ].reverse end it "should execute before advice on face options before action options" do face.instance_eval do option("--face-foo") { before_action { |_,_,_| report :face_foo } } option("--face-bar", '-r') { before_action { |_,_,_| report :face_bar } } action(:boo) do option("--action-foo") { before_action { |_,_,_| report :action_foo } } option("--action-bar", '-b') { before_action { |_,_,_| report :action_bar } } option("-q", "--action-quux") { before_action { |_,_,_| report :action_quux } } option("-a") { before_action { |_,_,_| report :a } } option("--action-baz") { before_action { |_,_,_| report :action_baz } } when_invoked {|options| } end option("-u", "--face-quux") { before_action { |_,_,_| report :face_quux } } option("-f") { before_action { |_,_,_| report :f } } option("--face-baz") { before_action { |_,_,_| report :face_baz } } end expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz, :action_foo, :action_bar, :action_quux, :a, :action_baz ] face.boo Hash[ *expected_calls.zip([]).flatten ] face.reported.should == expected_calls end it "should execute after advice on face options in declaration order" do face.instance_eval do option("--face-foo") { after_action { |_,_,_| report :face_foo } } option("--face-bar", '-r') { after_action { |_,_,_| report :face_bar } } action(:boo) do option("--action-foo") { after_action { |_,_,_| report :action_foo } } option("--action-bar", '-b') { after_action { |_,_,_| report :action_bar } } option("-q", "--action-quux") { after_action { |_,_,_| report :action_quux } } option("-a") { after_action { |_,_,_| report :a } } option("--action-baz") { after_action { |_,_,_| report :action_baz } } when_invoked {|options| } end option("-u", "--face-quux") { after_action { |_,_,_| report :face_quux } } option("-f") { after_action { |_,_,_| report :f } } option("--face-baz") { after_action { |_,_,_| report :face_baz } } end expected_calls = [ :face_foo, :face_bar, :face_quux, :f, :face_baz, :action_foo, :action_bar, :action_quux, :a, :action_baz ] face.boo Hash[ *expected_calls.zip([]).flatten ] face.reported.should == expected_calls.reverse end it "should not invoke a decorator if the options are empty" do face.option("--foo FOO") { before_action { |_,_,_| report :before_action } } face.expects(:report).never face.bar end context "passing a subset of the options" do before :each do face.option("--foo") { before_action { |_,_,_| report :foo } } face.option("--bar") { before_action { |_,_,_| report :bar } } end it "should invoke only foo's advice when passed only 'foo'" do face.bar(:foo => true) face.reported.should == [ :foo ] end it "should invoke only bar's advice when passed only 'bar'" do face.bar(:bar => true) face.reported.should == [ :bar ] end it "should invoke advice for all passed options" do face.bar(:foo => true, :bar => true) face.reported.should == [ :foo, :bar ] end end end context "and inheritance" do let :parent do Class.new(Puppet::Interface) do script(:on_parent) {|options| :on_parent } def reported; @reported; end def report(arg) (@reported ||= []) << arg end end end let :child do parent.new(:inherited_decorators, '0.0.1') do script(:on_child) {|options| :on_child } end end context "locally declared face options" do subject do child.option("--foo=") { before_action { |_,_,_| report :child_before } } child end it "should be invoked when calling a child action" do subject.on_child(:foo => true).should == :on_child subject.reported.should == [ :child_before ] end it "should be invoked when calling a parent action" do subject.on_parent(:foo => true).should == :on_parent subject.reported.should == [ :child_before ] end end context "inherited face option decorators" do subject do parent.option("--foo=") { before_action { |_,_,_| report :parent_before } } child end it "should be invoked when calling a child action" do subject.on_child(:foo => true).should == :on_child subject.reported.should == [ :parent_before ] end it "should be invoked when calling a parent action" do subject.on_parent(:foo => true).should == :on_parent subject.reported.should == [ :parent_before ] end end context "with both inherited and local face options" do # Decorations should be invoked in declaration order, according to # inheritance (e.g. parent class options should be handled before # subclass options). subject do child.option "-c" do before_action { |action, args, options| report :c_before } after_action { |action, args, options| report :c_after } end parent.option "-a" do before_action { |action, args, options| report :a_before } after_action { |action, args, options| report :a_after } end child.option "-d" do before_action { |action, args, options| report :d_before } after_action { |action, args, options| report :d_after } end parent.option "-b" do before_action { |action, args, options| report :b_before } after_action { |action, args, options| report :b_after } end child.script(:decorations) { |options| report :invoked } child end it "should invoke all decorations when calling a child action" do subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1) subject.reported.should == [ :a_before, :b_before, :c_before, :d_before, :invoked, :d_after, :c_after, :b_after, :a_after ] end it "should invoke all decorations when calling a parent action" do subject.decorations(:a => 1, :b => 1, :c => 1, :d => 1) subject.reported.should == [ :a_before, :b_before, :c_before, :d_before, :invoked, :d_after, :c_after, :b_after, :a_after ] end end end end it_should_behave_like "documentation on faces" do subject do face = Puppet::Interface.new(:action_documentation, '0.0.1') do action :documentation do when_invoked do |options| true end end end face.get_action(:documentation) end end context "#when_rendering" do it "should fail if no type is given when_rendering" it "should accept a when_rendering block" it "should accept multiple when_rendering blocks" it "should fail if when_rendering gets a non-symbol identifier" it "should fail if a second block is given for the same type" it "should return the block if asked" end context "#validate_and_clean" do subject do Puppet::Interface.new(:validate_args, '1.0.0') do script :test do |options| options end end end it "should fail if a required option is not passed" do subject.option "--foo" do required end expect { subject.test }.to raise_error ArgumentError, /options are required/ end it "should fail if two aliases to one option are passed" do subject.option "--foo", "-f" expect { subject.test :foo => true, :f => true }. to raise_error ArgumentError, /Multiple aliases for the same option/ end it "should fail if an unknown option is passed" do expect { subject.test :unknown => true }. to raise_error ArgumentError, /Unknown options passed: unknown/ end it "should report all the unknown options passed" do expect { subject.test :unknown => true, :unseen => false }. to raise_error ArgumentError, /Unknown options passed: unknown, unseen/ end it "should accept 'global' options from settings" do expect { subject.test(:certname => "true").should == { :certname => "true" } }.not_to raise_error end end context "default option values" do subject do Puppet::Interface.new(:default_option_values, '1.0.0') do action :foo do option "--foo" do end option "--bar" do end when_invoked do |options| options end end end end let :action do subject.get_action :foo end let :option do action.get_option :foo end it "should not add options without defaults" do subject.foo.should == {} end it "should not add options without defaults, if options are given" do subject.foo(:bar => 1).should == { :bar => 1 } end it "should add the option default value when set" do option.default = proc { 12 } subject.foo.should == { :foo => 12 } end it "should add the option default value when set, if other options are given" do option.default = proc { 12 } subject.foo(:bar => 1).should == { :foo => 12, :bar => 1 } end it "should invoke the same default proc every time called" do option.default = proc { @foo ||= {} } subject.foo[:foo].object_id.should == subject.foo[:foo].object_id end [nil, 0, 1, true, false, {}, []].each do |input| it "should not override a passed option (#{input.inspect})" do option.default = proc { :fail } subject.foo(:foo => input).should == { :foo => input } end end end context "runtime manipulations" do subject do Puppet::Interface.new(:runtime_manipulations, '1.0.0') do action :foo do when_invoked do |options| options end end end end let :action do subject.get_action :foo end it "should be the face default action if default is set true" do subject.get_default_action.should be_nil action.default = true subject.get_default_action.should == action end end end diff --git a/spec/unit/interface/documentation_spec.rb b/spec/unit/interface/documentation_spec.rb index d9c55bdfd..6370b0742 100755 --- a/spec/unit/interface/documentation_spec.rb +++ b/spec/unit/interface/documentation_spec.rb @@ -1,35 +1,35 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/interface' require 'puppet/interface/option' require 'puppet/interface/documentation' class Puppet::Interface::TinyDocs::Test include Puppet::Interface::TinyDocs attr_accessor :name, :options, :display_global_options def initialize self.name = "tinydoc-test" self.options = [] self.display_global_options = [] end def get_option(name) Puppet::Interface::Option.new(nil, "--#{name}") end end describe Puppet::Interface::TinyDocs do subject { Puppet::Interface::TinyDocs::Test.new } context "#build_synopsis" do before :each do subject.options = [:foo, :bar] end it { should respond_to :build_synopsis } it "should put a space between options (#7828)" do subject.build_synopsis('baz').should =~ /#{Regexp.quote('[--foo] [--bar]')}/ end end end diff --git a/spec/unit/interface/face_collection_spec.rb b/spec/unit/interface/face_collection_spec.rb index b31d28019..b3825589e 100755 --- a/spec/unit/interface/face_collection_spec.rb +++ b/spec/unit/interface/face_collection_spec.rb @@ -1,200 +1,200 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'tmpdir' require 'puppet/interface/face_collection' describe Puppet::Interface::FaceCollection do # To avoid cross-pollution we have to save and restore both the hash # containing all the interface data, and the array used by require. Restoring # both means that we don't leak side-effects across the code. --daniel 2011-04-06 # # Worse luck, we *also* need to flush $" of anything defining a face, # because otherwise we can cross-pollute from other test files and end up # with no faces loaded, but the require value set true. --daniel 2011-04-10 before :each do @original_faces = subject.instance_variable_get("@faces").dup @original_required = $".dup $".delete_if do |path| path =~ %r{/face/.*\.rb$} end subject.instance_variable_get(:@faces).clear subject.instance_variable_set(:@loaded, false) @autoload_loaded = {} Puppet::Util::Autoload.stubs(:loaded).returns(@autoload_loaded) end after :each do # Just pushing the duplicate back into place doesn't work as reliably as # this method to restore the state. Honestly, I need to make this horror # go away entirely. --daniel 2012-04-28 faces = subject.instance_variable_get("@faces") faces.clear @original_faces.each {|k,v| faces[k] = v } @original_required.each {|f| $".push f unless $".include? f } end describe "::[]" do before :each do subject.instance_variable_get("@faces")[:foo][SemVer.new('0.0.1')] = 10 end it "should return the face with the given name" do subject["foo", '0.0.1'].should == 10 end it "should attempt to load the face if it isn't found" do subject.expects(:require).once.with('puppet/face/bar') subject.expects(:require).once.with('puppet/face/0.0.1/bar') subject["bar", '0.0.1'] end it "should attempt to load the default face for the specified version :current" do subject.expects(:require).with('puppet/face/fozzie') subject['fozzie', :current] end it "should return true if the face specified is registered" do subject.instance_variable_get("@faces")[:foo][SemVer.new('0.0.1')] = 10 subject["foo", '0.0.1'].should == 10 end it "should attempt to require the face if it is not registered" do subject.expects(:require).with do |file| subject.instance_variable_get("@faces")[:bar][SemVer.new('0.0.1')] = true file == 'puppet/face/bar' end subject["bar", '0.0.1'].should be_true end it "should return false if the face is not registered" do subject.stubs(:require).returns(true) subject["bar", '0.0.1'].should be_false end it "should return false if the face file itself is missing" do subject.stubs(:require). raises(LoadError, 'no such file to load -- puppet/face/bar').then. raises(LoadError, 'no such file to load -- puppet/face/0.0.1/bar') subject["bar", '0.0.1'].should be_false end it "should register the version loaded by `:current` as `:current`" do subject.expects(:require).with do |file| subject.instance_variable_get("@faces")[:huzzah]['2.0.1'] = :huzzah_face file == 'puppet/face/huzzah' end subject["huzzah", :current] subject.instance_variable_get("@faces")[:huzzah][:current].should == :huzzah_face end context "with something on disk" do it "should register the version loaded from `puppet/face/{name}` as `:current`" do subject["huzzah", '2.0.1'].should be subject["huzzah", :current].should be Puppet::Face[:huzzah, '2.0.1'].should == Puppet::Face[:huzzah, :current] end it "should index :current when the code was pre-required" do subject.instance_variable_get("@faces")[:huzzah].should_not be_key :current require 'puppet/face/huzzah' subject[:huzzah, :current].should be_true end end it "should not cause an invalid face to be enumerated later" do subject[:there_is_no_face, :current].should be_false subject.faces.should_not include :there_is_no_face end end describe "::get_action_for_face" do it "should return an action on the current face" do Puppet::Face::FaceCollection.get_action_for_face(:huzzah, :bar, :current). should be_an_instance_of Puppet::Interface::Action end it "should return an action on an older version of a face" do action = Puppet::Face::FaceCollection. get_action_for_face(:huzzah, :obsolete, :current) action.should be_an_instance_of Puppet::Interface::Action action.face.version.should == SemVer.new('1.0.0') end it "should load the full older version of a face" do action = Puppet::Face::FaceCollection. get_action_for_face(:huzzah, :obsolete, :current) action.face.version.should == SemVer.new('1.0.0') action.face.should be_action :obsolete_in_core end it "should not add obsolete actions to the current version" do action = Puppet::Face::FaceCollection. get_action_for_face(:huzzah, :obsolete, :current) action.face.version.should == SemVer.new('1.0.0') action.face.should be_action :obsolete_in_core current = Puppet::Face[:huzzah, :current] current.version.should == SemVer.new('2.0.1') current.should_not be_action :obsolete_in_core current.should_not be_action :obsolete end end describe "::register" do it "should store the face by name" do face = Puppet::Face.new(:my_face, '0.0.1') subject.register(face) subject.instance_variable_get("@faces").should == { :my_face => { face.version => face } } end end describe "::underscorize" do faulty = [1, "23foo", "#foo", "$bar", "sturm und drang", :"sturm und drang"] valid = { "Foo" => :foo, :Foo => :foo, "foo_bar" => :foo_bar, :foo_bar => :foo_bar, "foo-bar" => :foo_bar, :"foo-bar" => :foo_bar, "foo_bar23" => :foo_bar23, :foo_bar23 => :foo_bar23, } valid.each do |input, expect| it "should map #{input.inspect} to #{expect.inspect}" do result = subject.underscorize(input) result.should == expect end end faulty.each do |input| it "should fail when presented with #{input.inspect} (#{input.class})" do expect { subject.underscorize(input) }. should raise_error ArgumentError, /not a valid face name/ end end end context "faulty faces" do before :each do $:.unshift "#{PuppetSpec::FIXTURE_DIR}/faulty_face" end after :each do $:.delete_if {|x| x == "#{PuppetSpec::FIXTURE_DIR}/faulty_face"} end it "should not die if a face has a syntax error" do subject.faces.should be_include :help subject.faces.should_not be_include :syntax @logs.should_not be_empty @logs.first.message.should =~ /syntax error/ end end end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 57d7edbbd..3523f5045 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -1,857 +1,857 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet_spec/modules' require 'puppet/module_tool/checksums' describe Puppet::Module do include PuppetSpec::Files before do # This is necessary because of the extra checks we have for the deprecated # 'plugins' directory FileTest.stubs(:exist?).returns false end it "should have a class method that returns a named module from a given environment" do env = mock 'module' env.expects(:module).with("mymod").returns "yep" Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should == "yep" end it "should return nil if asked for a named module that doesn't exist" do env = mock 'module' env.expects(:module).with("mymod").returns nil Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should be_nil end it "should support a 'version' attribute" do mod = Puppet::Module.new("mymod") mod.version = 1.09 mod.version.should == 1.09 end it "should support a 'source' attribute" do mod = Puppet::Module.new("mymod") mod.source = "http://foo/bar" mod.source.should == "http://foo/bar" end it "should support a 'project_page' attribute" do mod = Puppet::Module.new("mymod") mod.project_page = "http://foo/bar" mod.project_page.should == "http://foo/bar" end it "should support an 'author' attribute" do mod = Puppet::Module.new("mymod") mod.author = "Luke Kanies " mod.author.should == "Luke Kanies " end it "should support a 'license' attribute" do mod = Puppet::Module.new("mymod") mod.license = "GPL2" mod.license.should == "GPL2" end it "should support a 'summary' attribute" do mod = Puppet::Module.new("mymod") mod.summary = "GPL2" mod.summary.should == "GPL2" end it "should support a 'description' attribute" do mod = Puppet::Module.new("mymod") mod.description = "GPL2" mod.description.should == "GPL2" end it "should support specifying a compatible puppet version" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" mod.puppetversion.should == "0.25" end it "should validate that the puppet version is compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.expects(:version).returns "0.25" mod.validate_puppet_version end it "should fail if the specified puppet version is not compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.stubs(:version).returns "0.24" lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) end describe "when finding unmet dependencies" do before do FileTest.unstub(:exist?) @modpath = tmpdir('modpath') Puppet.settings[:modulepath] = @modpath end it "should list modules that are missing" do mod = PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] } ) mod.unmet_dependencies.should == [{ :reason => :missing, :name => "baz/foobar", :version_constraint => ">= 2.2.0", :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' }, :mod_details => { :installed_version => nil } }] end it "should list modules that are missing and have invalid names" do mod = PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar=bar" }] } ) mod.unmet_dependencies.should == [{ :reason => :missing, :name => "baz/foobar=bar", :version_constraint => ">= 2.2.0", :parent => { :name => 'puppetlabs/needy', :version => 'v9.9.9' }, :mod_details => { :installed_version => nil } }] end it "should list modules with unmet version requirement" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "baz/foobar" }] } ) mod2 = PuppetSpec::Modules.create( 'foobaz', @modpath, :metadata => { :dependencies => [{ "version_requirement" => "1.0.0", "name" => "baz/foobar" }] } ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '2.0.0', :author => 'baz' } ) mod.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => ">= 2.2.0", :parent => { :version => "v9.9.9", :name => "puppetlabs/foobar" }, :mod_details => { :installed_version => "2.0.0" } }] mod2.unmet_dependencies.should == [{ :reason => :version_mismatch, :name => "baz/foobar", :version_constraint => "v1.0.0", :parent => { :version => "v9.9.9", :name => "puppetlabs/foobaz" }, :mod_details => { :installed_version => "2.0.0" } }] end it "should consider a dependency without a version requirement to be satisfied" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "name" => "baz/foobar" }] } ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '2.0.0', :author => 'baz' } ) mod.unmet_dependencies.should be_empty end it "should consider a dependency without a semantic version to be unmet" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [{ "name" => "baz/foobar" }] } ) PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :version => '5.1', :author => 'baz' } ) mod.unmet_dependencies.should == [{ :reason => :non_semantic_version, :parent => { :version => "v9.9.9", :name => "puppetlabs/foobar" }, :mod_details => { :installed_version => "5.1" }, :name => "baz/foobar", :version_constraint => ">= 0.0.0" }] end it "should have valid dependencies when no dependencies have been specified" do mod = PuppetSpec::Modules.create( 'foobar', @modpath, :metadata => { :dependencies => [] } ) mod.unmet_dependencies.should == [] end it "should only list unmet dependencies" do mod = PuppetSpec::Modules.create( 'mymod', @modpath, :metadata => { :dependencies => [ { "version_requirement" => ">= 2.2.0", "name" => "baz/satisfied" }, { "version_requirement" => ">= 2.2.0", "name" => "baz/notsatisfied" } ] } ) PuppetSpec::Modules.create( 'satisfied', @modpath, :metadata => { :version => '3.3.0', :author => 'baz' } ) mod.unmet_dependencies.should == [{ :reason => :missing, :mod_details => { :installed_version => nil }, :parent => { :version => "v9.9.9", :name => "puppetlabs/mymod" }, :name => "baz/notsatisfied", :version_constraint => ">= 2.2.0" }] end it "should be empty when all dependencies are met" do mod = PuppetSpec::Modules.create( 'mymod2', @modpath, :metadata => { :dependencies => [ { "version_requirement" => ">= 2.2.0", "name" => "baz/satisfied" }, { "version_requirement" => "< 2.2.0", "name" => "baz/alsosatisfied" } ] } ) PuppetSpec::Modules.create( 'satisfied', @modpath, :metadata => { :version => '3.3.0', :author => 'baz' } ) PuppetSpec::Modules.create( 'alsosatisfied', @modpath, :metadata => { :version => '2.1.0', :author => 'baz' } ) mod.unmet_dependencies.should be_empty end end describe "when managing supported platforms" do it "should support specifying a supported platform" do mod = Puppet::Module.new("mymod") mod.supports "solaris" end it "should support specifying a supported platform and version" do mod = Puppet::Module.new("mymod") mod.supports "solaris", 1.0 end it "should fail when not running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" mod.supports "hpux" lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::UnsupportedPlatform) end it "should fail when supported platforms are present but of the wrong version" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when no supported platforms have been specified" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") lambda { mod.validate_supported_platform }.should_not raise_error end it "should be considered supported when running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when running on any of multiple supported platforms" do pending "Not sure how to send client platform to the module" end it "should validate its platform support on initialization" do pending "Not sure how to send client platform to the module" end end it "should return nil if asked for a module whose name is 'nil'" do Puppet::Module.find(nil, "myenv").should be_nil end it "should provide support for logging" do Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) end it "should be able to be converted to a string" do Puppet::Module.new("foo").to_s.should == "Module foo" end it "should add the path to its string form if the module is found" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a" mod.to_s.should == "Module foo(/a)" end it "should fail if its name is not alphanumeric" do lambda { Puppet::Module.new(".something") }.should raise_error(Puppet::Module::InvalidName) end it "should require a name at initialization" do lambda { Puppet::Module.new }.should raise_error(ArgumentError) end it "should convert an environment name into an Environment instance" do Puppet::Module.new("foo", :environment => "prod").environment.should be_instance_of(Puppet::Node::Environment) end it "should accept an environment at initialization" do Puppet::Module.new("foo", :environment => :prod).environment.name.should == :prod end it "should use the default environment if none is provided" do env = Puppet::Node::Environment.new Puppet::Module.new("foo").environment.should equal(env) end it "should use any provided Environment instance" do env = Puppet::Node::Environment.new Puppet::Module.new("foo", :environment => env).environment.should equal(env) end describe ".path" do before do dir = tmpdir("deep_path") @first = File.join(dir, "first") @second = File.join(dir, "second") Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}" FileUtils.mkdir_p(@first) FileUtils.mkdir_p(@second) end it "should return the path to the first found instance in its environment's module paths as its path" do modpath = File.join(@first, "foo") FileUtils.mkdir_p(modpath) # Make a second one, which we shouldn't find FileUtils.mkdir_p(File.join(@second, "foo")) mod = Puppet::Module.new("foo") mod.path.should == modpath end it "should be able to find itself in a directory other than the first directory in the module path" do modpath = File.join(@second, "foo") FileUtils.mkdir_p(modpath) mod = Puppet::Module.new("foo") mod.should be_exist mod.path.should == modpath end it "should be able to find itself in a directory other than the first directory in the module path even when it exists in the first" do environment = Puppet::Node::Environment.new first_modpath = File.join(@first, "foo") FileUtils.mkdir_p(first_modpath) second_modpath = File.join(@second, "foo") FileUtils.mkdir_p(second_modpath) mod = Puppet::Module.new("foo", :environment => environment, :path => second_modpath) mod.path.should == File.join(@second, "foo") mod.environment.should == environment end end describe '#modulepath' do it "should return the directory the module is installed in, if a path exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.modulepath.should == '/a' end it "should return nil if no path exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.modulepath.should be_nil end end it "should be considered existent if it exists in at least one module path" do mod = Puppet::Module.new("foo") mod.expects(:path).returns "/a/foo" mod.should be_exist end it "should be considered nonexistent if it does not exist in any of the module paths" do mod = Puppet::Module.new("foo") mod.expects(:path).returns nil mod.should_not be_exist end [:plugins, :templates, :files, :manifests].each do |filetype| dirname = filetype == :plugins ? "lib" : filetype.to_s it "should be able to return individual #{filetype}" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == path end it "should consider #{filetype} to be present if their base directory exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s + "?").should be_true end it "should consider #{filetype} to be absent if their base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s + "?").should be_false end it "should consider #{filetype} to be absent if the module base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s + "?").should be_false end it "should return nil if asked to return individual #{filetype} that don't exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return nil when asked for individual #{filetype} if the module does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return the base directory if asked for a nil path" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" base = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(base).returns true mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base end end it "should return the path to the plugin directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.plugin_directory.should == "/a/foo/lib" end it "should throw a warning if plugins are in a 'plugins' directory rather than a 'lib' directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" FileTest.expects(:exist?).with("/a/foo/plugins").returns true Puppet.expects(:deprecation_warning).with("using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'") mod.plugin_directory.should == "/a/foo/plugins" end it "should default to 'lib' for the plugins directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.plugin_directory.should == "/a/foo/lib" end end describe Puppet::Module, "when finding matching manifests" do before do @mod = Puppet::Module.new("mymod") @mod.stubs(:path).returns "/a" @pq_glob_with_extension = "yay/*.xx" @fq_glob_with_extension = "/a/manifests/#{@pq_glob_with_extension}" end it "should return all manifests matching the glob pattern" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.stubs(:directory?).returns false @mod.match_manifests(@pq_glob_with_extension).should == %w{foo bar} end it "should not return directories" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.expects(:directory?).with("foo").returns false FileTest.expects(:directory?).with("bar").returns true @mod.match_manifests(@pq_glob_with_extension).should == %w{foo} end it "should default to the 'init' file if no glob pattern is specified" do Dir.expects(:glob).with("/a/manifests/init.{pp,rb}").returns(%w{/a/manifests/init.pp}) @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} end it "should return all manifests matching the glob pattern in all existing paths" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{a b}) @mod.match_manifests(@pq_glob_with_extension).should == %w{a b} end it "should match the glob pattern plus '.{pp,rb}' if no extention is specified" do Dir.expects(:glob).with("/a/manifests/yay/foo.{pp,rb}").returns(%w{yay}) @mod.match_manifests("yay/foo").should == %w{yay} end it "should return an empty array if no manifests matched" do Dir.expects(:glob).with(@fq_glob_with_extension).returns([]) @mod.match_manifests(@pq_glob_with_extension).should == [] end end describe Puppet::Module do include PuppetSpec::Files before do @modpath = tmpdir('modpath') @module = PuppetSpec::Modules.create('mymod', @modpath) end it "should use 'License' in its current path as its metadata file" do @module.license_file.should == "#{@modpath}/mymod/License" end it "should return nil as its license file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").license_file.should be_nil end it "should cache the license file" do @module.expects(:path).once.returns nil @module.license_file @module.license_file end it "should use 'metadata.json' in its current path as its metadata file" do @module.metadata_file.should == "#{@modpath}/mymod/metadata.json" end it "should return nil as its metadata file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").metadata_file.should be_nil end it "should cache the metadata file" do Puppet::Module.any_instance.expects(:path).once.returns nil mod = Puppet::Module.new("foo") mod.metadata_file.should == mod.metadata_file end it "should have metadata if it has a metadata file and its data is not empty" do FileTest.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should have metadata if it has a metadata file and its data is not empty" do FileTest.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}" @module.should be_has_metadata end it "should not have metadata if has a metadata file and its data is empty" do FileTest.expects(:exist?).with(@module.metadata_file).returns true File.stubs(:read).with(@module.metadata_file).returns "/* +-----------------------------------------------------------------------+ | | | ==> DO NOT EDIT THIS FILE! <== | | | | You should edit the `Modulefile` and run `puppet-module build` | | to generate the `metadata.json` file for your releases. | | | +-----------------------------------------------------------------------+ */ {}" @module.should_not be_has_metadata end it "should know if it is missing a metadata file" do FileTest.expects(:exist?).with(@module.metadata_file).returns false @module.should_not be_has_metadata end it "should be able to parse its metadata file" do @module.should respond_to(:load_metadata) end it "should parse its metadata file on initialization if it is present" do Puppet::Module.any_instance.expects(:has_metadata?).returns true Puppet::Module.any_instance.expects(:load_metadata) Puppet::Module.new("yay") end describe "when loading the metadata file", :if => Puppet.features.pson? do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25", :dependencies => [] } @text = @data.to_pson @module = Puppet::Module.new("foo") @module.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns @text end %w{source author version license}.each do |attr| it "should set #{attr} if present in the metadata file" do @module.load_metadata @module.send(attr).should == @data[attr.to_sym] end it "should fail if #{attr} is not present in the metadata file" do @data.delete(attr.to_sym) @text = @data.to_pson File.stubs(:read).with("/my/file").returns @text lambda { @module.load_metadata }.should raise_error( Puppet::Module::MissingMetadata, "No #{attr} module metadata provided for foo" ) end end it "should set puppetversion if present in the metadata file" do @module.load_metadata @module.puppetversion.should == @data[:puppetversion] end context "when versionRequirement is used for dependency version info" do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25", :dependencies => [ { "versionRequirement" => "0.0.1", "name" => "pmtacceptance/stdlib" }, { "versionRequirement" => "0.1.0", "name" => "pmtacceptance/apache" } ] } @text = @data.to_pson @module = Puppet::Module.new("foo") @module.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns @text end it "should set the dependency version_requirement key" do @module.load_metadata @module.dependencies[0]['version_requirement'].should == "0.0.1" end it "should set the version_requirement key for all dependencies" do @module.load_metadata @module.dependencies[0]['version_requirement'].should == "0.0.1" @module.dependencies[1]['version_requirement'].should == "0.1.0" end end it "should fail if the discovered name is different than the metadata name" end it "should be able to tell if there are local changes" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do modpath = tmpdir('modpath') foo_checksum = 'acbd18db4cc2f85cedef654fccc4a4d8' checksummed_module = PuppetSpec::Modules.create( 'changed', modpath, :metadata => { :checksums => { "foo" => foo_checksum, } } ) foo_path = Pathname.new(File.join(checksummed_module.path, 'foo')) IO.binwrite(foo_path, 'notfoo') Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path).should_not == foo_checksum checksummed_module.has_local_changes?.should be_true IO.binwrite(foo_path, 'foo') Puppet::ModuleTool::Checksums.new(foo_path).checksum(foo_path).should == foo_checksum checksummed_module.has_local_changes?.should be_false end end it "should know what other modules require it" do Puppet.settings[:modulepath] = @modpath dependable = PuppetSpec::Modules.create( 'dependable', @modpath, :metadata => {:author => 'puppetlabs'} ) PuppetSpec::Modules.create( 'needy', @modpath, :metadata => { :author => 'beggar', :dependencies => [{ "version_requirement" => ">= 2.2.0", "name" => "puppetlabs/dependable" }] } ) PuppetSpec::Modules.create( 'wantit', @modpath, :metadata => { :author => 'spoiled', :dependencies => [{ "version_requirement" => "< 5.0.0", "name" => "puppetlabs/dependable" }] } ) dependable.required_by.should =~ [ { "name" => "beggar/needy", "version" => "9.9.9", "version_requirement" => ">= 2.2.0" }, { "name" => "spoiled/wantit", "version" => "9.9.9", "version_requirement" => "< 5.0.0" } ] end end diff --git a/spec/unit/module_tool_spec.rb b/spec/unit/module_tool_spec.rb index 9da829d01..0106ee8b9 100755 --- a/spec/unit/module_tool_spec.rb +++ b/spec/unit/module_tool_spec.rb @@ -1,203 +1,203 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # encoding: UTF-8 require 'spec_helper' require 'puppet/module_tool' describe Puppet::ModuleTool do describe '.format_tree' do it 'should return an empty tree when given an empty list' do subject.format_tree([]).should == '' end it 'should return a shallow when given a list without dependencies' do list = [ { :text => 'first' }, { :text => 'second' }, { :text => 'third' } ] subject.format_tree(list).should == <<-TREE ├── first ├── second └── third TREE end it 'should return a deeply nested tree when given a list with deep dependencies' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] } ] }, ] subject.format_tree(list).should == <<-TREE └─┬ first └─┬ second └── third TREE end it 'should show connectors when deep dependencies are not on the last node of the top level' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] } ] }, { :text => 'fourth' } ] subject.format_tree(list).should == <<-TREE ├─┬ first │ └─┬ second │ └── third └── fourth TREE end it 'should show connectors when deep dependencies are not on the last node of any level' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] }, { :text => 'fourth' } ] } ] subject.format_tree(list).should == <<-TREE └─┬ first ├─┬ second │ └── third └── fourth TREE end it 'should show connectors in every case when deep dependencies are not on the last node' do list = [ { :text => 'first', :dependencies => [ { :text => 'second', :dependencies => [ { :text => 'third' } ] }, { :text => 'fourth' } ] }, { :text => 'fifth' } ] subject.format_tree(list).should == <<-TREE ├─┬ first │ ├─┬ second │ │ └── third │ └── fourth └── fifth TREE end end describe '.set_option_defaults' do include PuppetSpec::Files let (:setting) { {:environment => "foo", :modulepath => make_absolute("foo")} } [:environment, :modulepath].each do |value| describe "if #{value} is part of options" do let (:options) { {} } before(:each) do options[value] = setting[value] Puppet[value] = "bar" end it "should set Puppet[#{value}] to the options[#{value}]" do subject.set_option_defaults options Puppet[value].should == options[value] end it "should not override options[#{value}]" do subject.set_option_defaults options options[value].should == setting[value] end end describe "if #{value} is not part of options" do let (:options) { {} } before(:each) do Puppet[value] = setting[value] end it "should populate options[#{value}] with the value of Puppet[#{value}]" do subject.set_option_defaults options Puppet[value].should == options[value] end it "should not override Puppet[#{value}]" do subject.set_option_defaults options Puppet[value].should == setting[value] end end end describe ':target_dir' do let (:sep) { File::PATH_SEPARATOR } let (:my_fake_path) { ["/my/fake/dir", "/my/other/dir"].collect { |dir| make_absolute(dir) } .join(sep) } let (:options) { {:modulepath => my_fake_path}} describe "when not specified" do it "should set options[:target_dir]" do subject.set_option_defaults options options[:target_dir].should_not be_nil end it "should be the first path of options[:modulepath]" do subject.set_option_defaults options options[:target_dir].should == my_fake_path.split(sep).first end end describe "when specified" do let (:my_target_dir) { make_absolute("/foo/bar") } before(:each) do options[:target_dir] = my_target_dir end it "should not be overridden" do subject.set_option_defaults options options[:target_dir].should == my_target_dir end it "should be prepended to options[:modulepath]" do subject.set_option_defaults options options[:modulepath].split(sep).first.should == my_target_dir end it "should leave the remainder of options[:modulepath] untouched" do subject.set_option_defaults options options[:modulepath].split(sep).drop(1).join(sep).should == my_fake_path end end end end end diff --git a/spec/unit/network/authconfig_spec.rb b/spec/unit/network/authconfig_spec.rb index 9b33679a7..578f2a190 100755 --- a/spec/unit/network/authconfig_spec.rb +++ b/spec/unit/network/authconfig_spec.rb @@ -1,315 +1,315 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/authconfig' describe Puppet::Network::AuthConfig do before do @rights = stubs 'rights' Puppet::Network::Rights.stubs(:new).returns(@rights) @rights.stubs(:each).returns([]) FileTest.stubs(:exists?).returns(true) File.stubs(:stat).returns(stub('stat', :ctime => :now)) Time.stubs(:now).returns Time.now @authconfig = Puppet::Network::AuthConfig.new("dummy", false) end describe "when initializing" do before :each do Puppet::Network::AuthConfig.any_instance.stubs(:read) end it "should use the authconfig default pathname if none provided" do Puppet.expects(:[]).with(:authconfig).returns("dummy") Puppet::Network::AuthConfig.new end it "should raise an error if no file is defined finally" do Puppet.stubs(:[]).with(:authconfig).returns(nil) lambda { Puppet::Network::AuthConfig.new }.should raise_error(Puppet::DevError) end it "should read and parse the file if parsenow is true" do Puppet::Network::AuthConfig.any_instance.expects(:read) Puppet::Network::AuthConfig.new("dummy", true) end end describe "when checking authorization" do before :each do @authconfig.stubs(:read) @call = stub 'call', :intern => "name" @handler = stub 'handler', :intern => "handler" @method = stub_everything 'method' @request = stub 'request', :call => @call, :handler => @handler, :method => @method, :name => "me", :ip => "1.2.3.4" end it "should attempt to read the authconfig file" do @rights.stubs(:include?) @authconfig.expects(:read) @authconfig.allowed?(@request) end it "should use a name right if it exists" do right = stub 'right' @rights.stubs(:include?).with("name").returns(true) @rights.stubs(:[]).with("name").returns(right) right.expects(:allowed?).with("me", "1.2.3.4") @authconfig.allowed?(@request) end it "should use a namespace right otherwise" do right = stub 'right' @rights.stubs(:include?).with("name").returns(false) @rights.stubs(:include?).with("handler").returns(true) @rights.stubs(:[]).with("handler").returns(right) right.expects(:allowed?).with("me", "1.2.3.4") @authconfig.allowed?(@request) end it "should return whatever the found rights returns" do right = stub 'right' @rights.stubs(:include?).with("name").returns(true) @rights.stubs(:[]).with("name").returns(right) right.stubs(:allowed?).with("me", "1.2.3.4").returns(:returned) @authconfig.allowed?(@request).should == :returned end end describe "when parsing authconfig file" do before :each do @fd = stub 'fd' @fd.expects(:each).never File.stubs(:open).yields(@fd) @rights.stubs(:include?).returns(false) @rights.stubs(:[]) end it "should skip comments" do @fd.stubs(:each_line).yields(' # comment') @rights.expects(:newright).never @authconfig.read end it "should increment line number even on commented lines" do @fd.stubs(:each_line).multiple_yields(' # comment','[puppetca]') @rights.expects(:newright).with('[puppetca]', 2, 'dummy') @authconfig.read end it "should skip blank lines" do @fd.stubs(:each_line).yields(' ') @rights.expects(:newright).never @authconfig.read end it "should increment line number even on blank lines" do @fd.stubs(:each_line).multiple_yields(' ','[puppetca]') @rights.expects(:newright).with('[puppetca]', 2, 'dummy') @authconfig.read end it "should throw an error if the current namespace right already exist" do @fd.stubs(:each_line).yields('[puppetca]') @rights.stubs(:include?).with("puppetca").returns(true) lambda { @authconfig.read }.should raise_error end it "should not throw an error if the current path right already exist" do @fd.stubs(:each_line).yields('path /hello') @rights.stubs(:newright).with("/hello",1, 'dummy') @rights.stubs(:include?).with("/hello").returns(true) lambda { @authconfig.read }.should_not raise_error end it "should create a new right for found namespaces" do @fd.stubs(:each_line).yields('[puppetca]') @rights.expects(:newright).with("[puppetca]", 1, 'dummy') @authconfig.read end it "should create a new right for each found namespace line" do @fd.stubs(:each_line).multiple_yields('[puppetca]', '[fileserver]') @rights.expects(:newright).with("[puppetca]", 1, 'dummy') @rights.expects(:newright).with("[fileserver]", 2, 'dummy') @authconfig.read end it "should create a new right for each found path line" do @fd.stubs(:each_line).multiple_yields('path /certificates') @rights.expects(:newright).with("/certificates", 1, 'dummy') @authconfig.read end it "should create a new right for each found regex line" do @fd.stubs(:each_line).multiple_yields('path ~ .rb$') @rights.expects(:newright).with("~ .rb$", 1, 'dummy') @authconfig.read end it "should strip whitespace around ACE" do acl = stub 'acl', :info @fd.stubs(:each_line).multiple_yields('[puppetca]', ' allow 127.0.0.1 , 172.16.10.0 ') @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) acl.expects(:allow).with('127.0.0.1') acl.expects(:allow).with('172.16.10.0') @authconfig.read end it "should allow ACE inline comments" do acl = stub 'acl', :info @fd.stubs(:each_line).multiple_yields('[puppetca]', ' allow 127.0.0.1 # will it work?') @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) acl.expects(:allow).with('127.0.0.1') @authconfig.read end it "should create an allow ACE on each subsequent allow" do acl = stub 'acl', :info @fd.stubs(:each_line).multiple_yields('[puppetca]', 'allow 127.0.0.1') @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) acl.expects(:allow).with('127.0.0.1') @authconfig.read end it "should create a deny ACE on each subsequent deny" do acl = stub 'acl', :info @fd.stubs(:each_line).multiple_yields('[puppetca]', 'deny 127.0.0.1') @rights.stubs(:newright).with("[puppetca]", 1, 'dummy').returns(acl) acl.expects(:deny).with('127.0.0.1') @authconfig.read end it "should inform the current ACL if we get the 'method' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('path /certificates', 'method search,find') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_method).with('search') acl.expects(:restrict_method).with('find') @authconfig.read end it "should raise an error if the 'method' directive is used in a right different than a path/regex one" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('[puppetca]', 'method search,find') @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) lambda { @authconfig.read }.should raise_error end it "should inform the current ACL if we get the 'environment' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('path /certificates', 'environment production,development') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_environment).with('production') acl.expects(:restrict_environment).with('development') @authconfig.read end it "should raise an error if the 'environment' directive is used in a right different than a path/regex one" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('[puppetca]', 'environment env') @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) lambda { @authconfig.read }.should raise_error end it "should inform the current ACL if we get the 'auth' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('path /certificates', 'auth yes') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_authenticated).with('yes') @authconfig.read end it "should also allow the longest 'authenticated' directive" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('path /certificates', 'authenticated yes') @rights.stubs(:newright).with("/certificates", 1, 'dummy').returns(acl) acl.expects(:restrict_authenticated).with('yes') @authconfig.read end it "should raise an error if the 'auth' directive is used in a right different than a path/regex one" do acl = stub 'acl', :info acl.stubs(:acl_type).returns(:regex) @fd.stubs(:each_line).multiple_yields('[puppetca]', 'auth yes') @rights.stubs(:newright).with("puppetca", 1, 'dummy').returns(acl) lambda { @authconfig.read }.should raise_error end end end diff --git a/spec/unit/network/authstore_spec.rb b/spec/unit/network/authstore_spec.rb index 32ce1930f..226fe1ad4 100755 --- a/spec/unit/network/authstore_spec.rb +++ b/spec/unit/network/authstore_spec.rb @@ -1,401 +1,401 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/authconfig' describe Puppet::Network::AuthStore do before :each do @authstore = Puppet::Network::AuthStore.new @authstore.reset_interpolation end describe "when checking if the acl has some entries" do it "should be empty if no ACE have been entered" do @authstore.should be_empty end it "should not be empty if it is a global allow" do @authstore.allow('*') @authstore.should_not be_empty end it "should not be empty if at least one allow has been entered" do @authstore.allow('1.1.1.*') @authstore.should_not be_empty end it "should not be empty if at least one deny has been entered" do @authstore.deny('1.1.1.*') @authstore.should_not be_empty end end describe "when checking global allow" do it "should not be enabled by default" do @authstore.should_not be_globalallow @authstore.should_not be_allowed('foo.bar.com', '192.168.1.1') end it "should always allow when enabled" do @authstore.allow('*') @authstore.should be_globalallow @authstore.should be_allowed('foo.bar.com', '192.168.1.1') end end describe "when checking a regex type of allow" do before :each do @authstore.allow('/^(test-)?host[0-9]+\.other-domain\.(com|org|net)$|some-domain\.com/') @ip = '192.168.1.1' end ['host5.other-domain.com', 'test-host12.other-domain.net', 'foo.some-domain.com'].each { |name| it "should allow the host #{name}" do @authstore.should be_allowed(name, @ip) end } ['host0.some-other-domain.com',''].each { |name| it "should not allow the host #{name}" do @authstore.should_not be_allowed(name, @ip) end } end end describe Puppet::Network::AuthStore::Declaration do ['100.101.99.98','100.100.100.100','1.2.3.4','11.22.33.44'].each { |ip| describe "when the pattern is a simple numeric IP such as #{ip}" do before :each do @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) end it "should match the specified IP" do @declaration.should be_match('www.testsite.org',ip) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org','200.101.99.98') end end (1..3).each { |n| describe "when the pattern is a IP mask with #{n} numeric segments and a *" do before :each do @ip_pattern = ip.split('.')[0,n].join('.')+'.*' @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip_pattern) end it "should match an IP in the range" do @declaration.should be_match('www.testsite.org',ip) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org','200.101.99.98') end it "should not match IPs that differ in the last non-wildcard segment" do other = ip.split('.') other[n-1].succ! @declaration.should_not be_match('www.testsite.org',other.join('.')) end end } } describe "when the pattern is a numeric IP with a back reference" do before :each do @ip = '100.101.$1' @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@ip).interpolate('12.34'.match(/(.*)/)) end it "should match an IP with the appropriate interpolation" do @declaration.should be_match('www.testsite.org',@ip.sub(/\$1/,'12.34')) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org',@ip.sub(/\$1/,'66.34')) end end [ "02001:0000:1234:0000:0000:C1C0:ABCD:0876", "2001:0000:1234:0000:00001:C1C0:ABCD:0876", " 2001:0000:1234:0000:0000:C1C0:ABCD:0876 0", "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", "3ffe:0b00:0000:0001:0000:0000:000a", "FF02:0000:0000:0000:0000:0000:0000:0000:0001", "3ffe:b00::1::a", "1:2:3::4:5::7:8", "12345::6:7:8", "1::5:400.2.3.4", "1::5:260.2.3.4", "1::5:256.2.3.4", "1::5:1.256.3.4", "1::5:1.2.256.4", "1::5:1.2.3.256", "1::5:300.2.3.4", "1::5:1.300.3.4", "1::5:1.2.300.4", "1::5:1.2.3.300", "1::5:900.2.3.4", "1::5:1.900.3.4", "1::5:1.2.900.4", "1::5:1.2.3.900", "1::5:300.300.300.300", "1::5:3000.30.30.30", "1::400.2.3.4", "1::260.2.3.4", "1::256.2.3.4", "1::1.256.3.4", "1::1.2.256.4", "1::1.2.3.256", "1::300.2.3.4", "1::1.300.3.4", "1::1.2.300.4", "1::1.2.3.300", "1::900.2.3.4", "1::1.900.3.4", "1::1.2.900.4", "1::1.2.3.900", "1::300.300.300.300", "1::3000.30.30.30", "::400.2.3.4", "::260.2.3.4", "::256.2.3.4", "::1.256.3.4", "::1.2.256.4", "::1.2.3.256", "::300.2.3.4", "::1.300.3.4", "::1.2.300.4", "::1.2.3.300", "::900.2.3.4", "::1.900.3.4", "::1.2.900.4", "::1.2.3.900", "::300.300.300.300", "::3000.30.30.30", "2001:DB8:0:0:8:800:200C:417A:221", # unicast, full "FF01::101::2" # multicast, compressed ].each { |invalid_ip| describe "when the pattern is an invalid IPv6 address such as #{invalid_ip}" do it "should raise an exception" do lambda { Puppet::Network::AuthStore::Declaration.new(:allow,invalid_ip) }.should raise_error end end } [ "1.2.3.4", "2001:0000:1234:0000:0000:C1C0:ABCD:0876", "3ffe:0b00:0000:0000:0001:0000:0000:000a", "FF02:0000:0000:0000:0000:0000:0000:0001", "0000:0000:0000:0000:0000:0000:0000:0001", "0000:0000:0000:0000:0000:0000:0000:0000", "::ffff:192.168.1.26", "2::10", "ff02::1", "fe80::", "2002::", "2001:db8::", "2001:0db8:1234::", "::ffff:0:0", "::1", "::ffff:192.168.1.1", "1:2:3:4:5:6:7:8", "1:2:3:4:5:6::8", "1:2:3:4:5::8", "1:2:3:4::8", "1:2:3::8", "1:2::8", "1::8", "1::2:3:4:5:6:7", "1::2:3:4:5:6", "1::2:3:4:5", "1::2:3:4", "1::2:3", "1::8", "::2:3:4:5:6:7:8", "::2:3:4:5:6:7", "::2:3:4:5:6", "::2:3:4:5", "::2:3:4", "::2:3", "::8", "1:2:3:4:5:6::", "1:2:3:4:5::", "1:2:3:4::", "1:2:3::", "1:2::", "1::", "1:2:3:4:5::7:8", "1:2:3:4::7:8", "1:2:3::7:8", "1:2::7:8", "1::7:8", "1:2:3:4:5:6:1.2.3.4", "1:2:3:4:5::1.2.3.4", "1:2:3:4::1.2.3.4", "1:2:3::1.2.3.4", "1:2::1.2.3.4", "1::1.2.3.4", "1:2:3:4::5:1.2.3.4", "1:2:3::5:1.2.3.4", "1:2::5:1.2.3.4", "1::5:1.2.3.4", "1::5:11.22.33.44", "fe80::217:f2ff:254.7.237.98", "fe80::217:f2ff:fe07:ed62", "2001:DB8:0:0:8:800:200C:417A", # unicast, full "FF01:0:0:0:0:0:0:101", # multicast, full "0:0:0:0:0:0:0:1", # loopback, full "0:0:0:0:0:0:0:0", # unspecified, full "2001:DB8::8:800:200C:417A", # unicast, compressed "FF01::101", # multicast, compressed "::1", # loopback, compressed, non-routable "::", # unspecified, compressed, non-routable "0:0:0:0:0:0:13.1.68.3", # IPv4-compatible IPv6 address, full, deprecated "0:0:0:0:0:FFFF:129.144.52.38", # IPv4-mapped IPv6 address, full "::13.1.68.3", # IPv4-compatible IPv6 address, compressed, deprecated "::FFFF:129.144.52.38", # IPv4-mapped IPv6 address, compressed "2001:0DB8:0000:CD30:0000:0000:0000:0000/60", # full, with prefix "2001:0DB8::CD30:0:0:0:0/60", # compressed, with prefix "2001:0DB8:0:CD30::/60", # compressed, with prefix #2 "::/128", # compressed, unspecified address type, non-routable "::1/128", # compressed, loopback address type, non-routable "FF00::/8", # compressed, multicast address type "FE80::/10", # compressed, link-local unicast, non-routable "FEC0::/10", # compressed, site-local unicast, deprecated "127.0.0.1", # standard IPv4, loopback, non-routable "0.0.0.0", # standard IPv4, unspecified, non-routable "255.255.255.255", # standard IPv4 "fe80:0000:0000:0000:0204:61ff:fe9d:f156", "fe80:0:0:0:204:61ff:fe9d:f156", "fe80::204:61ff:fe9d:f156", "fe80:0000:0000:0000:0204:61ff:254.157.241.086", "fe80:0:0:0:204:61ff:254.157.241.86", "fe80::204:61ff:254.157.241.86", "::1", "fe80::", "fe80::1" ].each { |ip| describe "when the pattern is a valid IP such as #{ip}" do before :each do @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,ip) end it "should match the specified IP" do @declaration.should be_match('www.testsite.org',ip) end it "should not match other IPs" do @declaration.should_not be_match('www.testsite.org','200.101.99.98') end end unless ip =~ /:.*\./ # Hybrid IPs aren't supported by ruby's ipaddr } { 'spirit.mars.nasa.gov' => 'a PQDN', 'ratchet.2ndsiteinc.com' => 'a PQDN with digits', 'a.c.ru' => 'a PQDN with short segments', }.each {|pqdn,desc| describe "when the pattern is #{desc}" do before :each do @host = pqdn @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) end it "should match the specified PQDN" do @declaration.should be_match(@host,'200.101.99.98') end it "should not match a similar FQDN" do pending "FQDN consensus" @declaration.should_not be_match(@host+'.','200.101.99.98') end end } ['abc.12seps.edu.phisher.biz','www.google.com','slashdot.org'].each { |host| (1...(host.split('.').length)).each { |n| describe "when the pattern is #{"*."+host.split('.')[-n,n].join('.')}" do before :each do @pattern = "*."+host.split('.')[-n,n].join('.') @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@pattern) end it "should match #{host}" do @declaration.should be_match(host,'1.2.3.4') end it "should not match www.testsite.gov" do @declaration.should_not be_match('www.testsite.gov','200.101.99.98') end it "should not match hosts that differ in the first non-wildcard segment" do other = host.split('.') other[-n].succ! @declaration.should_not be_match(other.join('.'),'1.2.3.4') end end } } describe "when the pattern is a FQDN" do before :each do @host = 'spirit.mars.nasa.gov.' @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,@host) end it "should match the specified FQDN" do pending "FQDN consensus" @declaration.should be_match(@host,'200.101.99.98') end it "should not match a similar PQDN" do @declaration.should_not be_match(@host[0..-2],'200.101.99.98') end end describe "when the pattern is an opaque string with a back reference" do before :each do @host = 'c216f41a-f902-4bfb-a222-850dd957bebb' @item = "/catalog/#{@host}" @pattern = %{^/catalog/([^/]+)$} @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') end it "should match an IP with the appropriate interpolation" do @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') end end describe "when the pattern is an opaque string with a back reference and the matched data contains dots" do before :each do @host = 'admin.mgmt.nym1' @item = "/catalog/#{@host}" @pattern = %{^/catalog/([^/]+)$} @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') end it "should match a name with the appropriate interpolation" do @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') end end describe "when the pattern is an opaque string with a back reference and the matched data contains dots with an initial prefix that looks like an IP address" do before :each do @host = '01.admin.mgmt.nym1' @item = "/catalog/#{@host}" @pattern = %{^/catalog/([^/]+)$} @declaration = Puppet::Network::AuthStore::Declaration.new(:allow,'$1') end it "should match a name with the appropriate interpolation" do @declaration.interpolate(@item.match(@pattern)).should be_match(@host,'10.0.0.5') end end describe "when comparing patterns" do before :each do @ip = Puppet::Network::AuthStore::Declaration.new(:allow,'127.0.0.1') @host_name = Puppet::Network::AuthStore::Declaration.new(:allow,'www.hard_knocks.edu') @opaque = Puppet::Network::AuthStore::Declaration.new(:allow,'hey_dude') end it "should consider ip addresses before host names" do (@ip < @host_name).should be_true end it "should consider ip addresses before opaque strings" do (@ip < @opaque).should be_true end it "should consider host_names before opaque strings" do (@host_name < @opaque).should be_true end end end diff --git a/spec/unit/network/format_handler_spec.rb b/spec/unit/network/format_handler_spec.rb index 8b535c3ab..62200bd60 100755 --- a/spec/unit/network/format_handler_spec.rb +++ b/spec/unit/network/format_handler_spec.rb @@ -1,335 +1,335 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/format_handler' class FormatTester extend Puppet::Network::FormatHandler end describe Puppet::Network::FormatHandler do after do formats = Puppet::Network::FormatHandler.instance_variable_get("@formats") formats.each do |name, format| formats.delete(name) unless format.is_a?(Puppet::Network::Format) end end it "should be able to list supported formats" do FormatTester.should respond_to(:supported_formats) end it "should include all supported formats" do one = stub 'supported', :supported? => true, :name => :one, :weight => 1 two = stub 'supported', :supported? => false, :name => :two, :weight => 1 three = stub 'supported', :supported? => true, :name => :three, :weight => 1 four = stub 'supported', :supported? => false, :name => :four, :weight => 1 Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four result = FormatTester.supported_formats result.length.should == 2 result.should be_include(:one) result.should be_include(:three) end it "should return the supported formats in decreasing order of weight" do one = stub 'supported', :supported? => true, :name => :one, :weight => 1 two = stub 'supported', :supported? => true, :name => :two, :weight => 6 three = stub 'supported', :supported? => true, :name => :three, :weight => 2 four = stub 'supported', :supported? => true, :name => :four, :weight => 8 Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two, :three, :four] Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two Puppet::Network::FormatHandler.stubs(:format).with(:three).returns three Puppet::Network::FormatHandler.stubs(:format).with(:four).returns four FormatTester.supported_formats.should == [:four, :two, :three, :one] end describe "with a preferred serialization format setting" do before do one = stub 'supported', :supported? => true, :name => :one, :weight => 1 two = stub 'supported', :supported? => true, :name => :two, :weight => 6 Puppet::Network::FormatHandler.stubs(:formats).returns [:one, :two] Puppet::Network::FormatHandler.stubs(:format).with(:one).returns one Puppet::Network::FormatHandler.stubs(:format).with(:two).returns two end describe "that is supported" do before do Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :one end it "should return the preferred serialization format first" do FormatTester.supported_formats.should == [:one, :two] end end describe "that is not supported" do before do Puppet.settings.expects(:value).with(:preferred_serialization_format).returns :unsupported end it "should still return the default format first" do FormatTester.supported_formats.should == [:two, :one] end it "should log a debug message" do Puppet.expects(:debug).with("Value of 'preferred_serialization_format' (unsupported) is invalid for FormatTester, using default (two)") Puppet.expects(:debug).with("FormatTester supports formats: one two; using two") FormatTester.supported_formats end end end it "should return the first format as the default format" do FormatTester.expects(:supported_formats).returns [:one, :two] FormatTester.default_format.should == :one end it "should be able to use a protected format for better logging on errors" do Puppet::Network::FormatHandler.should respond_to(:protected_format) end it "should delegate all methods from the informative format to the specified format" do format = mock 'format' format.stubs(:name).returns(:myformat) Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format format.expects(:render).with("foo").returns "yay" Puppet::Network::FormatHandler.protected_format(:myformat).render("foo").should == "yay" end it "should provide better logging if a failure is encountered when delegating from the informative format to the real format" do format = mock 'format' format.stubs(:name).returns(:myformat) Puppet::Network::FormatHandler.expects(:format).twice.with(:myformat).returns format format.expects(:render).with("foo").raises "foo" lambda { Puppet::Network::FormatHandler.protected_format(:myformat).render("foo") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should raise an error if we couldn't find a format by name or mime-type" do Puppet::Network::FormatHandler.stubs(:format).with(:myformat).returns nil lambda { Puppet::Network::FormatHandler.protected_format(:myformat) }.should raise_error end describe "when using formats" do before do @format = mock 'format' @format.stubs(:supported?).returns true @format.stubs(:name).returns :my_format Puppet::Network::FormatHandler.stubs(:format).with(:my_format).returns @format Puppet::Network::FormatHandler.stubs(:mime).with("text/myformat").returns @format Puppet::Network::Format.stubs(:===).returns false Puppet::Network::Format.stubs(:===).with(@format).returns true end it "should be able to test whether a format is supported" do FormatTester.should respond_to(:support_format?) end it "should use the Format to determine whether a given format is supported" do @format.expects(:supported?).with(FormatTester) FormatTester.support_format?(:my_format) end it "should be able to convert from a given format" do FormatTester.should respond_to(:convert_from) end it "should call the format-specific converter when asked to convert from a given format" do @format.expects(:intern).with(FormatTester, "mydata") FormatTester.convert_from(:my_format, "mydata") end it "should call the format-specific converter when asked to convert from a given format by mime-type" do @format.expects(:intern).with(FormatTester, "mydata") FormatTester.convert_from("text/myformat", "mydata") end it "should call the format-specific converter when asked to convert from a given format by format instance" do @format.expects(:intern).with(FormatTester, "mydata") FormatTester.convert_from(@format, "mydata") end it "should raise a FormatError when an exception is encountered when converting from a format" do @format.expects(:intern).with(FormatTester, "mydata").raises "foo" lambda { FormatTester.convert_from(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should be able to use a specific hook for converting into multiple instances" do @format.expects(:intern_multiple).with(FormatTester, "mydata") FormatTester.convert_from_multiple(:my_format, "mydata") end it "should raise a FormatError when an exception is encountered when converting multiple items from a format" do @format.expects(:intern_multiple).with(FormatTester, "mydata").raises "foo" lambda { FormatTester.convert_from_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should be able to use a specific hook for rendering multiple instances" do @format.expects(:render_multiple).with("mydata") FormatTester.render_multiple(:my_format, "mydata") end it "should raise a FormatError when an exception is encountered when rendering multiple items into a format" do @format.expects(:render_multiple).with("mydata").raises "foo" lambda { FormatTester.render_multiple(:my_format, "mydata") }.should raise_error(Puppet::Network::FormatHandler::FormatError) end end describe "when managing formats" do it "should have a method for defining a new format" do Puppet::Network::FormatHandler.should respond_to(:create) end it "should create a format instance when asked" do format = stub 'format', :name => :foo Puppet::Network::Format.expects(:new).with(:foo).returns format Puppet::Network::FormatHandler.create(:foo) end it "should instance_eval any block provided when creating a format" do format = stub 'format', :name => :instance_eval format.expects(:yayness) Puppet::Network::Format.expects(:new).returns format Puppet::Network::FormatHandler.create(:instance_eval) do yayness end end it "should be able to retrieve a format by name" do format = Puppet::Network::FormatHandler.create(:by_name) Puppet::Network::FormatHandler.format(:by_name).should equal(format) end it "should be able to retrieve a format by extension" do format = Puppet::Network::FormatHandler.create(:by_extension, :extension => "foo") Puppet::Network::FormatHandler.format_by_extension("foo").should equal(format) end it "should return nil if asked to return a format by an unknown extension" do Puppet::Network::FormatHandler.format_by_extension("yayness").should be_nil end it "should be able to retrieve formats by name irrespective of case and class" do format = Puppet::Network::FormatHandler.create(:by_name) Puppet::Network::FormatHandler.format(:By_Name).should equal(format) end it "should be able to retrieve a format by mime type" do format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") Puppet::Network::FormatHandler.mime("foo/bar").should equal(format) end it "should be able to retrieve a format by mime type irrespective of case" do format = Puppet::Network::FormatHandler.create(:by_name, :mime => "foo/bar") Puppet::Network::FormatHandler.mime("Foo/Bar").should equal(format) end it "should be able to return all formats" do one = stub 'one', :name => :one two = stub 'two', :name => :two Puppet::Network::Format.expects(:new).with(:one).returns(one) Puppet::Network::Format.expects(:new).with(:two).returns(two) Puppet::Network::FormatHandler.create(:one) Puppet::Network::FormatHandler.create(:two) list = Puppet::Network::FormatHandler.formats list.should be_include(:one) list.should be_include(:two) end end describe "when an instance" do it "should be able to test whether a format is supported" do FormatTester.new.should respond_to(:support_format?) end it "should be able to convert to a given format" do FormatTester.new.should respond_to(:render) end it "should be able to get a format mime-type" do FormatTester.new.should respond_to(:mime) end it "should raise a FormatError when a rendering error is encountered" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).raises "eh" lambda { tester.render(:foo) }.should raise_error(Puppet::Network::FormatHandler::FormatError) end it "should call the format-specific converter when asked to convert to a given format" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).returns "foo" tester.render(:foo).should == "foo" end it "should call the format-specific converter when asked to convert to a given format by mime-type" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:mime).with("text/foo").returns format Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).returns "foo" tester.render("text/foo").should == "foo" end it "should call the format converter when asked to convert to a given format instance" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::Format.stubs(:===).with(format).returns(true) Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:render).with(tester).returns "foo" tester.render(format).should == "foo" end it "should render to the default format if no format is provided when rendering" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format FormatTester.expects(:default_format).returns :foo tester = FormatTester.new format.expects(:render).with(tester) tester.render end it "should call the format-specific converter when asked for the mime-type of a given format" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format tester = FormatTester.new format.expects(:mime).returns "text/foo" tester.mime(:foo).should == "text/foo" end it "should return the default format mime-type if no format is provided" do format = stub 'rendering format', :supported? => true, :name => :foo Puppet::Network::FormatHandler.stubs(:format).with(:foo).returns format FormatTester.expects(:default_format).returns :foo tester = FormatTester.new format.expects(:mime).returns "text/foo" tester.mime.should == "text/foo" end end end diff --git a/spec/unit/network/format_spec.rb b/spec/unit/network/format_spec.rb index f59593479..47257c922 100755 --- a/spec/unit/network/format_spec.rb +++ b/spec/unit/network/format_spec.rb @@ -1,197 +1,197 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/format' # A class with all of the necessary # hooks. class FormatRenderer def self.to_multiple_my_format(list) end def self.from_multiple_my_format(text) end def self.from_my_format(text) end def to_my_format end end describe Puppet::Network::Format do describe "when initializing" do it "should require a name" do lambda { Puppet::Network::Format.new }.should raise_error(ArgumentError) end it "should be able to provide its name" do Puppet::Network::Format.new(:my_format).name.should == :my_format end it "should always convert its name to a downcased symbol" do Puppet::Network::Format.new(:My_Format).name.should == :my_format end it "should be able to set its downcased mime type at initialization" do format = Puppet::Network::Format.new(:my_format, :mime => "Foo/Bar") format.mime.should == "foo/bar" end it "should default to text plus the name of the format as the mime type" do Puppet::Network::Format.new(:my_format).mime.should == "text/my_format" end it "should fail if unsupported options are provided" do lambda { Puppet::Network::Format.new(:my_format, :foo => "bar") }.should raise_error(ArgumentError) end end describe "instances" do before do @format = Puppet::Network::Format.new(:my_format) end it "should support being confined" do @format.should respond_to(:confine) end it "should not be considered suitable if confinement conditions are not met" do @format.confine :true => false @format.should_not be_suitable end it "should be able to determine if a class is supported" do @format.should respond_to(:supported?) end it "should consider a class to be supported if it has the individual and multiple methods for rendering and interning" do @format.should be_supported(FormatRenderer) end it "should default to its required methods being the individual and multiple methods for rendering and interning" do Puppet::Network::Format.new(:foo).required_methods.sort { |a,b| a.to_s <=> b.to_s }.should == [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].sort { |a,b| a.to_s <=> b.to_s } end it "should consider a class supported if the provided class has all required methods present" do format = Puppet::Network::Format.new(:foo) [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| format.expects(:required_method_present?).with { |name, klass, type| name == method and klass == String }.returns true end format.should be_required_methods_present(String) end it "should consider a class not supported if any required methods are missing from the provided class" do format = Puppet::Network::Format.new(:foo) format.stubs(:required_method_present?).returns true format.expects(:required_method_present?).with { |name, *args| name == :intern_method }.returns false format.should_not be_required_methods_present(String) end it "should be able to specify the methods required for support" do Puppet::Network::Format.new(:foo, :required_methods => [:render_method, :intern_method]).required_methods.should == [:render_method, :intern_method] end it "should only test for required methods if specific methods are specified as required" do format = Puppet::Network::Format.new(:foo, :required_methods => [:intern_method]) format.expects(:required_method_present?).with { |name, klass, type| name == :intern_method } format.required_methods_present?(String) end it "should not consider a class supported unless the format is suitable" do @format.expects(:suitable?).returns false @format.should_not be_supported(FormatRenderer) end it "should always downcase mimetypes" do @format.mime = "Foo/Bar" @format.mime.should == "foo/bar" end it "should support having a weight" do @format.should respond_to(:weight) end it "should default to a weight of of 5" do @format.weight.should == 5 end it "should be able to override its weight at initialization" do Puppet::Network::Format.new(:foo, :weight => 1).weight.should == 1 end it "should default to its extension being equal to its name" do Puppet::Network::Format.new(:foo).extension.should == "foo" end it "should support overriding the extension" do Puppet::Network::Format.new(:foo, :extension => "bar").extension.should == "bar" end [:intern_method, :intern_multiple_method, :render_multiple_method, :render_method].each do |method| it "should allow assignment of the #{method}" do Puppet::Network::Format.new(:foo, method => :foo).send(method).should == :foo end end end describe "when converting between instances and formatted text" do before do @format = Puppet::Network::Format.new(:my_format) @instance = FormatRenderer.new end it "should have a method for rendering a single instance" do @format.should respond_to(:render) end it "should have a method for rendering multiple instances" do @format.should respond_to(:render_multiple) end it "should have a method for interning text" do @format.should respond_to(:intern) end it "should have a method for interning text into multiple instances" do @format.should respond_to(:intern_multiple) end it "should return the results of calling the instance-specific render method if the method is present" do @instance.expects(:to_my_format).returns "foo" @format.render(@instance).should == "foo" end it "should return the results of calling the class-specific render_multiple method if the method is present" do @instance.class.expects(:to_multiple_my_format).returns ["foo"] @format.render_multiple([@instance]).should == ["foo"] end it "should return the results of calling the class-specific intern method if the method is present" do FormatRenderer.expects(:from_my_format).with("foo").returns @instance @format.intern(FormatRenderer, "foo").should equal(@instance) end it "should return the results of calling the class-specific intern_multiple method if the method is present" do FormatRenderer.expects(:from_multiple_my_format).with("foo").returns [@instance] @format.intern_multiple(FormatRenderer, "foo").should == [@instance] end it "should fail if asked to render and the instance does not respond to 'to_'" do lambda { @format.render("foo") }.should raise_error(NotImplementedError) end it "should fail if asked to intern and the class does not respond to 'from_'" do lambda { @format.intern(String, "foo") }.should raise_error(NotImplementedError) end it "should fail if asked to intern multiple and the class does not respond to 'from_multiple_'" do lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) end it "should fail if asked to render multiple and the instance does not respond to 'to_multiple_'" do lambda { @format.render_multiple(["foo", "bar"]) }.should raise_error(NotImplementedError) end end end diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb index 635e6953e..433eb5d09 100755 --- a/spec/unit/network/formats_spec.rb +++ b/spec/unit/network/formats_spec.rb @@ -1,352 +1,352 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/formats' class PsonTest attr_accessor :string def ==(other) string == other.string end def self.from_pson(data) new(data) end def initialize(string) @string = string end def to_pson(*args) { 'type' => self.class.name, 'data' => @string }.to_pson(*args) end end describe "Puppet Network Format" do it "should include a yaml format" do Puppet::Network::FormatHandler.format(:yaml).should_not be_nil end describe "yaml" do before do @yaml = Puppet::Network::FormatHandler.format(:yaml) end it "should have its mime type set to text/yaml" do @yaml.mime.should == "text/yaml" end it "should be supported on Strings" do @yaml.should be_supported(String) end it "should render by calling 'to_yaml' on the instance" do instance = mock 'instance' instance.expects(:to_yaml).returns "foo" @yaml.render(instance).should == "foo" end it "should render multiple instances by calling 'to_yaml' on the array" do instances = [mock('instance')] instances.expects(:to_yaml).returns "foo" @yaml.render_multiple(instances).should == "foo" end it "should intern by calling 'YAML.load'" do text = "foo" YAML.expects(:load).with("foo").returns "bar" @yaml.intern(String, text).should == "bar" end it "should intern multiples by calling 'YAML.load'" do text = "foo" YAML.expects(:load).with("foo").returns "bar" @yaml.intern_multiple(String, text).should == "bar" end end describe "base64 compressed yaml", :if => Puppet.features.zlib? do yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) before do @yaml = Puppet::Network::FormatHandler.format(:b64_zlib_yaml) end it "should have its mime type set to text/b64_zlib_yaml" do @yaml.mime.should == "text/b64_zlib_yaml" end it "should render by calling 'to_yaml' on the instance" do instance = mock 'instance' instance.expects(:to_yaml).returns "foo" @yaml.render(instance) end it "should encode generated yaml on render" do instance = mock 'instance', :to_yaml => "foo" @yaml.expects(:encode).with("foo").returns "bar" @yaml.render(instance).should == "bar" end it "should render multiple instances by calling 'to_yaml' on the array" do instances = [mock('instance')] instances.expects(:to_yaml).returns "foo" @yaml.render_multiple(instances) end it "should encode generated yaml on render" do instances = [mock('instance')] instances.stubs(:to_yaml).returns "foo" @yaml.expects(:encode).with("foo").returns "bar" @yaml.render(instances).should == "bar" end it "should intern by calling decode" do text = "foo" @yaml.expects(:decode).with("foo").returns "bar" @yaml.intern(String, text).should == "bar" end it "should intern multiples by calling 'decode'" do text = "foo" @yaml.expects(:decode).with("foo").returns "bar" @yaml.intern_multiple(String, text).should == "bar" end it "should decode by base64 decoding, uncompressing and Yaml loading" do Base64.expects(:decode64).with("zorg").returns "foo" Zlib::Inflate.expects(:inflate).with("foo").returns "baz" YAML.expects(:load).with("baz").returns "bar" @yaml.decode("zorg").should == "bar" end it "should encode by compressing and base64 encoding" do Zlib::Deflate.expects(:deflate).with("foo", Zlib::BEST_COMPRESSION).returns "bar" Base64.expects(:encode64).with("bar").returns "baz" @yaml.encode("foo").should == "baz" end describe "when zlib is disabled" do before do Puppet[:zlib] = false end it "use_zlib? should return false" do @yaml.use_zlib?.should == false end it "should refuse to encode" do lambda{ @yaml.encode("foo") }.should raise_error end it "should refuse to decode" do lambda{ @yaml.decode("foo") }.should raise_error end end describe "when zlib is not installed" do it "use_zlib? should return false" do Puppet[:zlib] = true Puppet.features.expects(:zlib?).returns(false) @yaml.use_zlib?.should == false end end end describe "plaintext" do before do @text = Puppet::Network::FormatHandler.format(:s) end it "should have its mimetype set to text/plain" do @text.mime.should == "text/plain" end it "should use 'txt' as its extension" do @text.extension.should == "txt" end end describe "dot" do before do @dot = Puppet::Network::FormatHandler.format(:dot) end it "should have its mimetype set to text/dot" do @dot.mime.should == "text/dot" end end describe Puppet::Network::FormatHandler.format(:raw) do before do @format = Puppet::Network::FormatHandler.format(:raw) end it "should exist" do @format.should_not be_nil end it "should have its mimetype set to application/x-raw" do @format.mime.should == "application/x-raw" end it "should always be supported" do @format.should be_supported(String) end it "should fail if its multiple_render method is used" do lambda { @format.render_multiple("foo") }.should raise_error(NotImplementedError) end it "should fail if its multiple_intern method is used" do lambda { @format.intern_multiple(String, "foo") }.should raise_error(NotImplementedError) end it "should have a weight of 1" do @format.weight.should == 1 end end it "should include a pson format" do Puppet::Network::FormatHandler.format(:pson).should_not be_nil end describe "pson", :if => Puppet.features.pson? do before do @pson = Puppet::Network::FormatHandler.format(:pson) end it "should have its mime type set to text/pson" do Puppet::Network::FormatHandler.format(:pson).mime.should == "text/pson" end it "should require the :render_method" do Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:render_method) end it "should require the :intern_method" do Puppet::Network::FormatHandler.format(:pson).required_methods.should be_include(:intern_method) end it "should have a weight of 10" do @pson.weight.should == 10 end describe "when supported" do it "should render by calling 'to_pson' on the instance" do instance = PsonTest.new("foo") instance.expects(:to_pson).returns "foo" @pson.render(instance).should == "foo" end it "should render multiple instances by calling 'to_pson' on the array" do instances = [mock('instance')] instances.expects(:to_pson).returns "foo" @pson.render_multiple(instances).should == "foo" end it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the data into an instance" do text = "foo" PSON.expects(:parse).with("foo").returns("type" => "PsonTest", "data" => "foo") PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" @pson.intern(PsonTest, text).should == "parsed_pson" end it "should not render twice if 'PSON.parse' creates the appropriate instance" do text = "foo" instance = PsonTest.new("foo") PSON.expects(:parse).with("foo").returns(instance) PsonTest.expects(:from_pson).never @pson.intern(PsonTest, text).should equal(instance) end it "should intern by calling 'PSON.parse' on the text and then using from_pson to convert the actual into an instance if the pson has no class/data separation" do text = "foo" PSON.expects(:parse).with("foo").returns("foo") PsonTest.expects(:from_pson).with("foo").returns "parsed_pson" @pson.intern(PsonTest, text).should == "parsed_pson" end it "should intern multiples by parsing the text and using 'class.intern' on each resulting data structure" do text = "foo" PSON.expects(:parse).with("foo").returns ["bar", "baz"] PsonTest.expects(:from_pson).with("bar").returns "BAR" PsonTest.expects(:from_pson).with("baz").returns "BAZ" @pson.intern_multiple(PsonTest, text).should == %w{BAR BAZ} end end end describe ":console format" do subject { Puppet::Network::FormatHandler.format(:console) } it { should be_an_instance_of Puppet::Network::Format } let :json do Puppet::Network::FormatHandler.format(:pson) end [:intern, :intern_multiple].each do |method| it "should not implement #{method}" do expect { subject.send(method, String, 'blah') }.to raise_error NotImplementedError end end ["hello", 1, 1.0].each do |input| it "should just return a #{input.inspect}" do subject.render(input).should == input end end [[1, 2], ["one"], [{ 1 => 1 }]].each do |input| it "should render #{input.inspect} as JSON" do subject.render(input).should == json.render(input).chomp end end it "should render a non-trivially-keyed Hash as JSON" do hash = { [1,2] => 3, [2,3] => 5, [3,4] => 7 } subject.render(hash).should == json.render(hash).chomp end it "should render a {String,Numeric}-keyed Hash into a table" do object = Object.new hash = { "one" => 1, "two" => [], "three" => {}, "four" => object, 5 => 5, 6.0 => 6 } # Gotta love ASCII-betical sort order. Hope your objects are better # structured for display than my test one is. --daniel 2011-04-18 subject.render(hash).should == < { "1" => '1' * 40, "2" => '2' * 40, '3' => '3' * 40 }, "text" => { "a" => 'a' * 40, 'b' => 'b' * 40, 'c' => 'c' * 40 } } subject.render(hash).should == <"1111111111111111111111111111111111111111", "2"=>"2222222222222222222222222222222222222222", "3"=>"3333333333333333333333333333333333333333"} text {"a"=>"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "b"=>"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "c"=>"cccccccccccccccccccccccccccccccccccccccc"} EOT end end end diff --git a/spec/unit/network/http/api/v1_spec.rb b/spec/unit/network/http/api/v1_spec.rb index d8c978eb3..3d9c60c91 100755 --- a/spec/unit/network/http/api/v1_spec.rb +++ b/spec/unit/network/http/api/v1_spec.rb @@ -1,216 +1,216 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http/api/v1' class V1RestApiTester include Puppet::Network::HTTP::API::V1 end describe Puppet::Network::HTTP::API::V1 do before do @tester = V1RestApiTester.new end it "should be able to convert a URI into a request" do @tester.should respond_to(:uri2indirection) end it "should be able to convert a request into a URI" do @tester.should respond_to(:indirection2uri) end describe "when converting a URI into a request" do before do @tester.stubs(:handler).returns "foo" end it "should require the http method, the URI, and the query parameters" do # Not a terribly useful test, but an important statement for the spec lambda { @tester.uri2indirection("/foo") }.should raise_error(ArgumentError) end it "should use the first field of the URI as the environment" do @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].to_s.should == "env" end it "should fail if the environment is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env ness/foo/bar", {}) }.should raise_error(ArgumentError) end it "should use the environment from the URI even if one is specified in the parameters" do @tester.uri2indirection("GET", "/env/foo/bar", {:environment => "otherenv"})[3][:environment].to_s.should == "env" end it "should not pass a buck_path parameter through (See Bugs #13553, #13518, #13511)" do @tester.uri2indirection("GET", "/env/foo/bar", { :bucket_path => "/malicious/path" })[3].should_not include({ :bucket_path => "/malicious/path" }) end it "should pass allowed parameters through" do @tester.uri2indirection("GET", "/env/foo/bar", { :allowed_param => "value" })[3].should include({ :allowed_param => "value" }) end it "should return the environment as a Puppet::Node::Environment" do @tester.uri2indirection("GET", "/env/foo/bar", {})[3][:environment].should be_a Puppet::Node::Environment end it "should not pass a buck_path parameter through (See Bugs #13553, #13518, #13511)" do @tester.uri2indirection("GET", "/env/foo/bar", { :bucket_path => "/malicious/path" })[3].should_not include({ :bucket_path => "/malicious/path" }) end it "should pass allowed parameters through" do @tester.uri2indirection("GET", "/env/foo/bar", { :allowed_param => "value" })[3].should include({ :allowed_param => "value" }) end it "should use the second field of the URI as the indirection name" do @tester.uri2indirection("GET", "/env/foo/bar", {})[0].should == "foo" end it "should fail if the indirection name is not alphanumeric" do lambda { @tester.uri2indirection("GET", "/env/foo ness/bar", {}) }.should raise_error(ArgumentError) end it "should use the remainder of the URI as the indirection key" do @tester.uri2indirection("GET", "/env/foo/bar", {})[2].should == "bar" end it "should support the indirection key being a /-separated file path" do @tester.uri2indirection("GET", "/env/foo/bee/baz/bomb", {})[2].should == "bee/baz/bomb" end it "should fail if no indirection key is specified" do lambda { @tester.uri2indirection("GET", "/env/foo/", {}) }.should raise_error(ArgumentError) lambda { @tester.uri2indirection("GET", "/env/foo", {}) }.should raise_error(ArgumentError) end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is singular" do @tester.uri2indirection("GET", "/env/foo/bar", {})[1].should == :find end it "should choose 'find' as the indirection method if the http method is a POST and the indirection name is singular" do @tester.uri2indirection("POST", "/env/foo/bar", {})[1].should == :find end it "should choose 'head' as the indirection method if the http method is a HEAD and the indirection name is singular" do @tester.uri2indirection("HEAD", "/env/foo/bar", {})[1].should == :head end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is plural" do @tester.uri2indirection("GET", "/env/foos/bar", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'find' as the indirection method if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {})[1].should == :find end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is facts" do @tester.uri2indirection("PUT", "/env/facts/bar", {})[1].should == :save end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is inventory" do @tester.uri2indirection("GET", "/env/inventory/search", {})[1].should == :search end it "should choose 'search' as the indirection method if the http method is a GET and the indirection name is facts_search" do @tester.uri2indirection("GET", "/env/facts_search/bar", {})[1].should == :search end it "should change indirection name to 'facts' if the http method is a GET and the indirection name is facts_search" do @tester.uri2indirection("GET", "/env/facts_search/bar", {})[0].should == 'facts' end it "should not change indirection name from 'facts' if the http method is a GET and the indirection name is facts" do @tester.uri2indirection("GET", "/env/facts/bar", {})[0].should == 'facts' end it "should change indirection name to 'status' if the http method is a GET and the indirection name is statuses" do @tester.uri2indirection("GET", "/env/statuses/bar", {})[0].should == 'status' end it "should change indirection name to 'probe' if the http method is a GET and the indirection name is probes" do @tester.uri2indirection("GET", "/env/probes/bar", {})[0].should == 'probe' end it "should choose 'delete' as the indirection method if the http method is a DELETE and the indirection name is singular" do @tester.uri2indirection("DELETE", "/env/foo/bar", {})[1].should == :destroy end it "should choose 'save' as the indirection method if the http method is a PUT and the indirection name is singular" do @tester.uri2indirection("PUT", "/env/foo/bar", {})[1].should == :save end it "should fail if an indirection method cannot be picked" do lambda { @tester.uri2indirection("UPDATE", "/env/foo/bar", {}) }.should raise_error(ArgumentError) end it "should URI unescape the indirection key" do escaped = URI.escape("foo bar") indirection_name, method, key, params = @tester.uri2indirection("GET", "/env/foo/#{escaped}", {}) key.should == "foo bar" end end describe "when converting a request into a URI" do before do @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", nil, :foo => :bar, :environment => "myenv") end it "should use the environment as the first field of the URI" do @tester.indirection2uri(@request).split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do @tester.indirection2uri(@request).split("/")[2].should == "foo" end it "should pluralize the indirection name if the method is 'search'" do @request.stubs(:method).returns :search @tester.indirection2uri(@request).split("/")[2].should == "foos" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") @tester.indirection2uri(@request).split("/")[3].sub(/\?.+/, '').should == escaped end it "should add the query string to the URI" do @request.expects(:query_string).returns "?query" @tester.indirection2uri(@request).should =~ /\?query$/ end end describe "when converting a request into a URI with body" do before :each do @request = Puppet::Indirector::Request.new(:foo, :find, "with spaces", nil, :foo => :bar, :environment => "myenv") end it "should use the environment as the first field of the URI" do @tester.request_to_uri_and_body(@request).first.split("/")[1].should == "myenv" end it "should use the indirection as the second field of the URI" do @tester.request_to_uri_and_body(@request).first.split("/")[2].should == "foo" end it "should use the escaped key as the remainder of the URI" do escaped = URI.escape("with spaces") @tester.request_to_uri_and_body(@request).first.split("/")[3].sub(/\?.+/, '').should == escaped end it "should return the URI and body separately" do @tester.request_to_uri_and_body(@request).should == ["/myenv/foo/with%20spaces", "foo=bar"] end end end diff --git a/spec/unit/network/http/compression_spec.rb b/spec/unit/network/http/compression_spec.rb index 5c919c6c5..04c12e3b1 100755 --- a/spec/unit/network/http/compression_spec.rb +++ b/spec/unit/network/http/compression_spec.rb @@ -1,196 +1,196 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "http compression" do describe "when zlib is not available" do before(:each) do Puppet.features.stubs(:zlib?).returns false require 'puppet/network/http/compression' class HttpUncompressor include Puppet::Network::HTTP::Compression::None end @uncompressor = HttpUncompressor.new end it "should have a module function that returns the None underlying module" do Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::None end it "should not add any Accept-Encoding header" do @uncompressor.add_accept_encoding({}).should == {} end it "should not tamper the body" do response = stub 'response', :body => "data" @uncompressor.uncompress_body(response).should == "data" end it "should yield an identity uncompressor" do response = stub 'response' @uncompressor.uncompress(response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) } end end describe "when zlib is available", :if => Puppet.features.zlib? do before(:each) do Puppet.features.stubs(:zlib?).returns true require 'puppet/network/http/compression' class HttpUncompressor include Puppet::Network::HTTP::Compression::Active end @uncompressor = HttpUncompressor.new end it "should have a module function that returns the Active underlying module" do Puppet::Network::HTTP::Compression.module.should == Puppet::Network::HTTP::Compression::Active end it "should add an Accept-Encoding header when http compression is available" do Puppet.settings.expects(:[]).with(:http_compression).returns(true) headers = @uncompressor.add_accept_encoding({}) headers.should have_key('accept-encoding') headers['accept-encoding'].should =~ /gzip/ headers['accept-encoding'].should =~ /deflate/ headers['accept-encoding'].should =~ /identity/ end it "should not add Accept-Encoding header if http compression is not available" do Puppet.settings.stubs(:[]).with(:http_compression).returns(false) @uncompressor.add_accept_encoding({}).should == {} end describe "when uncompressing response body" do before do @response = stub 'response' @response.stubs(:[]).with('content-encoding') @response.stubs(:body).returns("mydata") end it "should return untransformed response body with no content-encoding" do @uncompressor.uncompress_body(@response).should == "mydata" end it "should return untransformed response body with 'identity' content-encoding" do @response.stubs(:[]).with('content-encoding').returns('identity') @uncompressor.uncompress_body(@response).should == "mydata" end it "should use a Zlib inflater with 'deflate' content-encoding" do @response.stubs(:[]).with('content-encoding').returns('deflate') inflater = stub 'inflater' Zlib::Inflate.expects(:new).returns(inflater) inflater.expects(:inflate).with("mydata").returns "uncompresseddata" @uncompressor.uncompress_body(@response).should == "uncompresseddata" end it "should use a GzipReader with 'gzip' content-encoding" do @response.stubs(:[]).with('content-encoding').returns('gzip') io = stub 'io' StringIO.expects(:new).with("mydata").returns io reader = stub 'gzip reader' Zlib::GzipReader.expects(:new).with(io).returns(reader) reader.expects(:read).returns "uncompresseddata" @uncompressor.uncompress_body(@response).should == "uncompresseddata" end end describe "when uncompressing by chunk" do before do @response = stub 'response' @response.stubs(:[]).with('content-encoding') @inflater = stub_everything 'inflater' Zlib::Inflate.stubs(:new).returns(@inflater) end it "should yield an identity uncompressor with no content-encoding" do @uncompressor.uncompress(@response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) } end it "should yield an identity uncompressor with 'identity' content-encoding" do @response.stubs(:[]).with('content-encoding').returns 'identity' @uncompressor.uncompress(@response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::IdentityAdapter) } end %w{gzip deflate}.each do |c| it "should yield a Zlib uncompressor with '#{c}' content-encoding" do @response.stubs(:[]).with('content-encoding').returns c @uncompressor.uncompress(@response) { |u| u.should be_instance_of(Puppet::Network::HTTP::Compression::Active::ZlibAdapter) } end end it "should close the underlying adapter" do adapter = stub_everything 'adapter' Puppet::Network::HTTP::Compression::IdentityAdapter.expects(:new).returns(adapter) adapter.expects(:close) @uncompressor.uncompress(@response) { |u| } end end describe "zlib adapter" do before do @inflater = stub_everything 'inflater' Zlib::Inflate.stubs(:new).returns(@inflater) @adapter = Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new end it "should initialize the underlying inflater with gzip/zlib header parsing" do Zlib::Inflate.expects(:new).with(15+32) Puppet::Network::HTTP::Compression::Active::ZlibAdapter.new end it "should inflate the given chunk" do @inflater.expects(:inflate).with("chunk") @adapter.uncompress("chunk") end it "should return the inflated chunk" do @inflater.stubs(:inflate).with("chunk").returns("uncompressed") @adapter.uncompress("chunk").should == "uncompressed" end it "should try a 'regular' inflater on Zlib::DataError" do @inflater.expects(:inflate).raises(Zlib::DataError.new("not a zlib stream")) inflater = stub_everything 'inflater2' inflater.expects(:inflate).with("chunk").returns("uncompressed") Zlib::Inflate.expects(:new).with.returns(inflater) @adapter.uncompress("chunk") end it "should raise the error the second time" do @inflater.stubs(:inflate).raises(Zlib::DataError.new("not a zlib stream")) Zlib::Inflate.expects(:new).with.returns(@inflater) lambda { @adapter.uncompress("chunk") }.should raise_error end it "should finish the stream on close" do @inflater.expects(:finish) @adapter.close end it "should close the stream on close" do @inflater.expects(:close) @adapter.close end end end end diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb index c709d82fe..3c36a12c0 100755 --- a/spec/unit/network/http/handler_spec.rb +++ b/spec/unit/network/http/handler_spec.rb @@ -1,467 +1,467 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http/handler' require 'puppet/network/rest_authorization' class HttpHandled include Puppet::Network::HTTP::Handler end describe Puppet::Network::HTTP::Handler do before do @handler = HttpHandled.new end it "should include the v1 REST API" do Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::HTTP::API::V1) end it "should include the Rest Authorization system" do Puppet::Network::HTTP::Handler.ancestors.should be_include(Puppet::Network::RestAuthorization) end it "should have a method for initializing" do @handler.should respond_to(:initialize_for_puppet) end describe "when initializing" do it "should fail when no server type has been provided" do lambda { @handler.initialize_for_puppet }.should raise_error(ArgumentError) end it "should set server type" do @handler.initialize_for_puppet("foo") @handler.server.should == "foo" end end it "should be able to process requests" do @handler.should respond_to(:process) end describe "when processing a request" do before do @request = stub('http request') @request.stubs(:[]).returns "foo" @response = stub('http response') @model_class = stub('indirected model class') @indirection = stub('indirection') @model_class.stubs(:indirection).returns(@indirection) @result = stub 'result', :render => "mytext" @handler.stubs(:check_authorization) stub_server_interface end # Stub out the interface we require our including classes to # implement. def stub_server_interface @handler.stubs(:accept_header ).returns "format_one,format_two" @handler.stubs(:content_type_header).returns "text/yaml" @handler.stubs(:set_content_type ).returns "my_result" @handler.stubs(:set_response ).returns "my_result" @handler.stubs(:path ).returns "/my_handler/my_result" @handler.stubs(:http_method ).returns("GET") @handler.stubs(:params ).returns({}) @handler.stubs(:content_type ).returns("text/plain") end it "should create an indirection request from the path, parameters, and http method" do @handler.expects(:path).with(@request).returns "mypath" @handler.expects(:http_method).with(@request).returns "mymethod" @handler.expects(:params).with(@request).returns "myparams" @handler.expects(:uri2indirection).with("mymethod", "mypath", "myparams").returns stub("request", :method => :find) @handler.stubs(:do_find) @handler.process(@request, @response) end it "should call the 'do' method and delegate authorization to the RestAuthorization layer" do @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) @handler.expects(:do_mymethod).with("facts", "key", {:node => "name"}, @request, @response) @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}) @handler.process(@request, @response) end it "should return 403 if the request is not authorized" do @handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}]) @handler.expects(:do_mymethod).never @handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden")) @handler.expects(:set_response).with { |response, body, status| status == 403 } @handler.process(@request, @response) end it "should serialize a controller exception when an exception is thrown while finding the model instance" do @handler.expects(:uri2indirection).returns(["facts", :find, "key", {:node => "name"}]) @handler.expects(:do_find).raises(ArgumentError, "The exception") @handler.expects(:set_response).with { |response, body, status| body == "The exception" and status == 400 } @handler.process(@request, @response) end it "should set the format to text/plain when serializing an exception" do @handler.expects(:set_content_type).with(@response, "text/plain") @handler.do_exception(@response, "A test", 404) end it "should raise an error if the request is formatted in an unknown format" do @handler.stubs(:content_type_header).returns "unknown format" lambda { @handler.request_format(@request) }.should raise_error end it "should still find the correct format if content type contains charset information" do @handler.stubs(:content_type_header).returns "text/plain; charset=UTF-8" @handler.request_format(@request).should == "s" end describe "when finding a model instance" do before do @indirection.stubs(:find).returns @result Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat end it "should use the indirection request to find the model class" do @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key" do @indirection.expects(:find).with do |key, args| key == "my_result" end.returns @result @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do @indirection.expects(:find).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result @handler.do_find("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should set the content type to the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should fail if no accept header is provided" do @handler.expects(:accept_header).with(@request).returns nil lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should fail if the accept header does not contain a valid format" do @handler.expects(:accept_header).with(@request).returns "" lambda { @handler.do_find("my_handler", "my_result", {}, @request, @response) }.should raise_error(RuntimeError) end it "should not use an unsuitable format" do @handler.expects(:accept_header).with(@request).returns "foo,bar" foo = mock 'foo', :suitable? => false bar = mock 'bar', :suitable? => true Puppet::Network::FormatHandler.expects(:format).with("foo").returns foo Puppet::Network::FormatHandler.expects(:format).with("bar").returns bar @handler.expects(:set_content_type).with(@response, bar) # the suitable one @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should render the result using the first format specified in the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @result.expects(:render).with(@oneformat) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should pass the result through without rendering it if the result is a string" do @indirection.stubs(:find).returns "foo" @handler.expects(:set_response).with(@response, "foo") @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should use the default status when a model find call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a serialized object when a model find call succeeds" do @model_instance = stub('model instance') @model_instance.expects(:render).returns "my_rendered_object" @handler.expects(:set_response).with { |response, body, status| body == "my_rendered_object" } @indirection.stubs(:find).returns(@model_instance) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when no model instance can be found" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @indirection.stubs(:find).returns(nil) @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should write a log message when no model instance can be found" do @model_class.stubs(:name).returns "my name" @indirection.stubs(:find).returns(nil) Puppet.expects(:info).with("Could not find my_handler for 'my_result'") @handler.do_find("my_handler", "my_result", {}, @request, @response) end it "should serialize the result in with the appropriate format" do @model_instance = stub('model instance') @handler.expects(:format_to_use).returns(@oneformat) @model_instance.expects(:render).with(@oneformat).returns "my_rendered_object" @indirection.stubs(:find).returns(@model_instance) @handler.do_find("my_handler", "my_result", {}, @request, @response) end end describe "when performing head operation" do before do @handler.stubs(:model).with("my_handler").returns(stub 'model', :indirection => @model_class) @handler.stubs(:http_method).with(@request).returns("HEAD") @handler.stubs(:path).with(@request).returns("/production/my_handler/my_result") @handler.stubs(:params).with(@request).returns({}) @model_class.stubs(:head).returns true end it "should use the escaped request key" do @model_class.expects(:head).with do |key, args| key == "my_result" end.returns true @handler.process(@request, @response) end it "should not generate a response when a model head call succeeds" do @handler.expects(:set_response).never @handler.process(@request, @response) end it "should return a 404 when the model head call returns false" do @handler.expects(:set_response).with { |response, body, status| status == 404 } @model_class.stubs(:head).returns(false) @handler.process(@request, @response) end end describe "when searching for model instances" do before do Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result1 = mock 'result1' @result2 = mock 'results' @result = [@result1, @result2] @model_class.stubs(:render_multiple).returns "my rendered instances" @indirection.stubs(:search).returns(@result) @format = stub 'format', :suitable? => true, :mime => "text/format", :name => "format" Puppet::Network::FormatHandler.stubs(:format).returns @format @oneformat = stub 'one', :suitable? => true, :mime => "text/one", :name => "one" Puppet::Network::FormatHandler.stubs(:format).with("one").returns @oneformat end it "should use the indirection request to find the model" do @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should use a common method for determining the request parameters" do @indirection.expects(:search).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end.returns @result @handler.do_search("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status when a model search call succeeds" do @indirection.stubs(:search).returns(@result) @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should set the content type to the first format returned by the accept header" do @handler.expects(:accept_header).with(@request).returns "one,two" @handler.expects(:set_content_type).with(@response, @oneformat) @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a list of serialized objects when a model search call succeeds" do @handler.expects(:accept_header).with(@request).returns "one,two" @indirection.stubs(:search).returns(@result) @model_class.expects(:render_multiple).with(@oneformat, @result).returns "my rendered instances" @handler.expects(:set_response).with { |response, data| data == "my rendered instances" } @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return [] when searching returns an empty array" do @handler.expects(:accept_header).with(@request).returns "one,two" @indirection.stubs(:search).returns([]) @model_class.expects(:render_multiple).with(@oneformat, []).returns "[]" @handler.expects(:set_response).with { |response, data| data == "[]" } @handler.do_search("my_handler", "my_result", {}, @request, @response) end it "should return a 404 when searching returns nil" do @model_class.stubs(:name).returns "my name" @handler.expects(:set_response).with { |response, body, status| status == 404 } @indirection.stubs(:search).returns(nil) @handler.do_search("my_handler", "my_result", {}, @request, @response) end end describe "when destroying a model instance" do before do Puppet::Indirector::Indirection.expects(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @result = stub 'result', :render => "the result" @indirection.stubs(:destroy).returns @result end it "should use the indirection request to find the model" do @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should use the escaped request key to destroy the instance in the model" do @indirection.expects(:destroy).with do |key, args| key == "foo bar" end @handler.do_destroy("my_handler", "foo bar", {}, @request, @response) end it "should use a common method for determining the request parameters" do @indirection.expects(:destroy).with do |key, args| args[:foo] == :baz and args[:bar] == :xyzzy end @handler.do_destroy("my_handler", "my_result", {:foo => :baz, :bar => :xyzzy}, @request, @response) end it "should use the default status code a model destroy call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end it "should return a yaml-encoded result when a model destroy call succeeds" do @result = stub 'result', :to_yaml => "the result" @indirection.expects(:destroy).returns(@result) @handler.expects(:set_response).with { |response, body, status| body == "the result" } @handler.do_destroy("my_handler", "my_result", {}, @request, @response) end end describe "when saving a model instance" do before do Puppet::Indirector::Indirection.stubs(:instance).with(:my_handler).returns( stub "indirection", :model => @model_class ) @handler.stubs(:body).returns('my stuff') @handler.stubs(:content_type_header).returns("text/yaml") @result = stub 'result', :render => "the result" @model_instance = stub('indirected model instance') @model_class.stubs(:convert_from).returns(@model_instance) @indirection.stubs(:save) @format = stub 'format', :suitable? => true, :name => "format", :mime => "text/format" Puppet::Network::FormatHandler.stubs(:format).returns @format @yamlformat = stub 'yaml', :suitable? => true, :name => "yaml", :mime => "text/yaml" Puppet::Network::FormatHandler.stubs(:format).with("yaml").returns @yamlformat end it "should use the indirection request to find the model" do @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the 'body' hook to retrieve the body of the request" do @handler.expects(:body).returns "my body" @model_class.expects(:convert_from).with { |format, body| body == "my body" }.returns @model_instance @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should fail to save model if data is not specified" do @handler.stubs(:body).returns('') lambda { @handler.do_save("my_handler", "my_result", {}, @request, @response) }.should raise_error(ArgumentError) end it "should use a common method for determining the request parameters" do @indirection.expects(:save).with(@model_instance, 'key').once @handler.do_save("my_handler", "key", {}, @request, @response) end it "should use the default status when a model save call succeeds" do @handler.expects(:set_response).with { |response, body, status| status.nil? } @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should return the yaml-serialized result when a model save call succeeds" do @indirection.stubs(:save).returns(@model_instance) @model_instance.expects(:to_yaml).returns('foo') @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should set the content to yaml" do @handler.expects(:set_content_type).with(@response, @yamlformat) @handler.do_save("my_handler", "my_result", {}, @request, @response) end it "should use the content-type header to know the body format" do @handler.expects(:content_type_header).returns("text/format") Puppet::Network::FormatHandler.stubs(:mime).with("text/format").returns @format @model_class.expects(:convert_from).with { |format, body| format == "format" }.returns @model_instance @handler.do_save("my_handler", "my_result", {}, @request, @response) end end end describe "when resolving node" do it "should use a look-up from the ip address" do Resolv.expects(:getname).with("1.2.3.4").returns("host.domain.com") @handler.resolve_node(:ip => "1.2.3.4") end it "should return the look-up result" do Resolv.stubs(:getname).with("1.2.3.4").returns("host.domain.com") @handler.resolve_node(:ip => "1.2.3.4").should == "host.domain.com" end it "should return the ip address if resolving fails" do Resolv.stubs(:getname).with("1.2.3.4").raises(RuntimeError, "no such host") @handler.resolve_node(:ip => "1.2.3.4").should == "1.2.3.4" end end end diff --git a/spec/unit/network/http/mongrel/rest_spec.rb b/spec/unit/network/http/mongrel/rest_spec.rb index 51ce3fccb..a5fa6ef63 100755 --- a/spec/unit/network/http/mongrel/rest_spec.rb +++ b/spec/unit/network/http/mongrel/rest_spec.rb @@ -1,276 +1,276 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http' describe "Puppet::Network::HTTP::MongrelREST", :if => Puppet.features.mongrel? do before do require 'puppet/network/http/mongrel/rest' end it "should include the Puppet::Network::HTTP::Handler module" do Puppet::Network::HTTP::MongrelREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end describe "when initializing" do it "should call the Handler's initialization hook with its provided arguments as the server and handler" do Puppet::Network::HTTP::MongrelREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") Puppet::Network::HTTP::MongrelREST.new(:server => "my", :handler => "arguments") end end describe "when receiving a request" do before do @params = {} @request = stub('mongrel http request', :params => @params) @head = stub('response head') @body = stub('response body', :write => true) @response = stub('mongrel http response') @response.stubs(:start).yields(@head, @body) @model_class = stub('indirected model class') @mongrel = stub('mongrel http server', :register => true) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @handler = Puppet::Network::HTTP::MongrelREST.new(:server => @mongrel, :handler => :foo) end describe "and using the HTTP Handler interface" do it "should return the HTTP_ACCEPT parameter as the accept header" do @params.expects(:[]).with("HTTP_ACCEPT").returns "myaccept" @handler.accept_header(@request).should == "myaccept" end it "should return the Content-Type parameter as the Content-Type header" do @params.expects(:[]).with("HTTP_CONTENT_TYPE").returns "mycontent" @handler.content_type_header(@request).should == "mycontent" end it "should use the REQUEST_METHOD as the http method" do @params.expects(:[]).with(Mongrel::Const::REQUEST_METHOD).returns "mymethod" @handler.http_method(@request).should == "mymethod" end it "should return the request path as the path" do @params.expects(:[]).with(Mongrel::Const::REQUEST_PATH).returns "/foo/bar" @handler.path(@request).should == "/foo/bar" end it "should return the request body as the body" do @request.stubs(:body).returns StringIO.new("mybody") @handler.body(@request).should == "mybody" end it "should set the response's content-type header when setting the content type" do @header = mock 'header' @response.expects(:header).returns @header @header.expects(:[]=).with('Content-Type', "mytype") @handler.set_content_type(@response, "mytype") end it "should set the status and write the body when setting the response for a successful request" do head = mock 'head' body = mock 'body' @response.expects(:start).with(200).yields(head, body) body.expects(:write).with("mybody") @handler.set_response(@response, "mybody", 200) end describe "when the result is a File" do it "should use response send_file" do head = mock 'head' body = mock 'body' stat = stub 'stat', :size => 100 file = stub 'file', :stat => stat, :path => "/tmp/path" file.stubs(:is_a?).with(File).returns(true) @response.expects(:start).with(200).yields(head, body) @response.expects(:send_status).with(100) @response.expects(:send_header) @response.expects(:send_file).with("/tmp/path") @handler.set_response(@response, file, 200) end end it "should set the status and reason and write the body when setting the response for a successful request" do head = mock 'head' body = mock 'body' @response.expects(:start).with(400, false, "mybody").yields(head, body) body.expects(:write).with("mybody") @handler.set_response(@response, "mybody", 400) end end describe "and determining the request parameters" do before do @params = {'REQUEST_METHOD' => 'GET'} @request.stubs(:params).returns(@params) end it "should skip empty parameter values" do @params['QUERY_STRING'] = "&=" lambda { @handler.params(@request) }.should_not raise_error end it "should include the HTTP request parameters, with the keys as symbols" do @params['QUERY_STRING'] = 'foo=baz&bar=xyzzy' result = @handler.params(@request) result[:foo].should == "baz" result[:bar].should == "xyzzy" end it "should CGI-decode the HTTP parameters" do escaped = CGI.escape("foo bar") @params['QUERY_STRING'] = "foo=#{escaped}" result = @handler.params(@request) result[:foo].should == "foo bar" end it "should include parameters from the body of a POST request" do @params.merge!( 'QUERY_STRING' => nil, 'REQUEST_METHOD' => 'POST' ) body = StringIO.new('foo=bar&baz=qux') @request.stubs(:body).returns(body) @handler.params(@request).should include( :foo => 'bar', :baz => 'qux' ) end it "should convert the string 'true' to the boolean" do @params['QUERY_STRING'] = 'foo=true' result = @handler.params(@request) result[:foo].should be_true end it "should convert the string 'false' to the boolean" do @params['QUERY_STRING'] = 'foo=false' result = @handler.params(@request) result[:foo].should be_false end it "should convert integer arguments to Integers" do @params['QUERY_STRING'] = 'foo=15' result = @handler.params(@request) result[:foo].should == 15 end it "should convert floating point arguments to Floats" do @params['QUERY_STRING'] = 'foo=1.5' result = @handler.params(@request) result[:foo].should == 1.5 end it "should YAML-load and URI-decode values that are YAML-encoded" do escaping = CGI.escape(YAML.dump(%w{one two})) @params['QUERY_STRING'] = "foo=#{escaping}" result = @handler.params(@request) result[:foo].should == %w{one two} end it "should not allow the client to set the node via the query string" do @params['QUERY_STRING'] = "node=foo" @handler.params(@request)[:node].should be_nil end it "should not allow the client to set the IP address via the query string" do @params['QUERY_STRING'] = "ip=foo" @handler.params(@request)[:ip].should be_nil end it "should pass the client's ip address to model find" do @params['REMOTE_ADDR'] = "ipaddress" @handler.params(@request)[:ip].should == "ipaddress" end it "should pass the client's provided X-Forwared-For value as the ip" do @params["HTTP_X_FORWARDED_FOR"] = "ipaddress" @handler.params(@request)[:ip].should == "ipaddress" end it "should pass the client's provided X-Forwared-For first value as the ip" do @params["HTTP_X_FORWARDED_FOR"] = "ipproxy1,ipproxy2,ipaddress" @handler.params(@request)[:ip].should == "ipaddress" end it "should pass the client's provided X-Forwared-For value as the ip instead of the REMOTE_ADDR" do @params.merge!( "REMOTE_ADDR" => "remote_addr", "HTTP_X_FORWARDED_FOR" => "ipaddress" ) @handler.params(@request)[:ip].should == "ipaddress" end it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" @params["myheader"] = "/CN=host.domain.com" @handler.params(@request) end it "should retrieve the hostname by matching the certificate parameter" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" @params["myheader"] = "/CN=host.domain.com" @handler.params(@request)[:node].should == "host.domain.com" end it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" @params.merge!( "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" ) @handler.params(@request) end it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" @params.merge!( "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com" ) @handler.params(@request)[:authenticated].should be_true end it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" @params.merge!( "myheader" => "whatever", "certheader" => "/CN=host.domain.com" ) @handler.params(@request)[:authenticated].should be_false end it "should consider the host unauthenticated if no certificate information is present" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" @params.merge!( "myheader" => nil, "certheader" => "SUCCESS" ) @handler.params(@request)[:authenticated].should be_false end it "should resolve the node name with an ip address look-up if no certificate is present" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" @params["myheader"] = nil @handler.expects(:resolve_node).returns("host.domain.com") @handler.params(@request)[:node].should == "host.domain.com" end end end end diff --git a/spec/unit/network/http/mongrel_spec.rb b/spec/unit/network/http/mongrel_spec.rb index b8e486d25..9593e012b 100755 --- a/spec/unit/network/http/mongrel_spec.rb +++ b/spec/unit/network/http/mongrel_spec.rb @@ -1,91 +1,91 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http' describe "Puppet::Network::HTTP::Mongrel", "after initializing", :if => Puppet.features.mongrel? do it "should not be listening" do require 'puppet/network/http/mongrel' Puppet::Network::HTTP::Mongrel.new.should_not be_listening end end describe "Puppet::Network::HTTP::Mongrel", "when turning on listening", :if => Puppet.features.mongrel? do before do require 'puppet/network/http/mongrel' @server = Puppet::Network::HTTP::Mongrel.new @mock_mongrel = mock('mongrel') @mock_mongrel.stubs(:run) @mock_mongrel.stubs(:register) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @listen_params = { :address => "127.0.0.1", :port => 31337 } end it "should fail if already listening" do @server.listen(@listen_params) Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) end it "should require a listening address to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) end it "should require a listening port to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) end it "should order a mongrel server to start" do @mock_mongrel.expects(:run) @server.listen(@listen_params) end it "should tell mongrel to listen on the specified address and port" do Mongrel::HttpServer.expects(:new).with("127.0.0.1", 31337).returns(@mock_mongrel) @server.listen(@listen_params) end it "should be listening" do Mongrel::HttpServer.expects(:new).returns(@mock_mongrel) @server.listen(@listen_params) @server.should be_listening end describe "when providing REST services" do it "should instantiate a handler at / for handling REST calls" do Puppet::Network::HTTP::MongrelREST.expects(:new).returns "myhandler" @mock_mongrel.expects(:register).with("/", "myhandler") @server.listen(@listen_params) end end end describe "Puppet::Network::HTTP::Mongrel", "when turning off listening", :if => Puppet.features.mongrel? do before do @mock_mongrel = mock('mongrel httpserver') @mock_mongrel.stubs(:run) @mock_mongrel.stubs(:register) Mongrel::HttpServer.stubs(:new).returns(@mock_mongrel) @server = Puppet::Network::HTTP::Mongrel.new @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ]} end it "should fail unless listening" do Proc.new { @server.unlisten }.should raise_error(RuntimeError) end it "should order mongrel server to stop" do @server.listen(@listen_params) @mock_mongrel.expects(:stop) @server.unlisten end it "should not be listening" do @server.listen(@listen_params) @mock_mongrel.stubs(:stop) @server.unlisten @server.should_not be_listening end end diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb index 8a5666f56..59c24d5ed 100755 --- a/spec/unit/network/http/rack/rest_spec.rb +++ b/spec/unit/network/http/rack/rest_spec.rb @@ -1,246 +1,246 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http/rack' if Puppet.features.rack? require 'puppet/network/http/rack/rest' describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? do it "should include the Puppet::Network::HTTP::Handler module" do Puppet::Network::HTTP::RackREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end describe "when initializing" do it "should call the Handler's initialization hook with its provided arguments" do Puppet::Network::HTTP::RackREST.any_instance.expects(:initialize_for_puppet).with(:server => "my", :handler => "arguments") Puppet::Network::HTTP::RackREST.new(:server => "my", :handler => "arguments") end end describe "when serving a request" do before :all do @model_class = stub('indirected model class') Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @handler = Puppet::Network::HTTP::RackREST.new(:handler => :foo) end before :each do @response = Rack::Response.new end def mk_req(uri, opts = {}) env = Rack::MockRequest.env_for(uri, opts) Rack::Request.new(env) end describe "and using the HTTP Handler interface" do it "should return the HTTP_ACCEPT parameter as the accept header" do req = mk_req('/', 'HTTP_ACCEPT' => 'myaccept') @handler.accept_header(req).should == "myaccept" end it "should return the CONTENT_TYPE parameter as the content type header" do req = mk_req('/', 'CONTENT_TYPE' => 'mycontent') @handler.content_type_header(req).should == "mycontent" end it "should use the REQUEST_METHOD as the http method" do req = mk_req('/', :method => 'MYMETHOD') @handler.http_method(req).should == "MYMETHOD" end it "should return the request path as the path" do req = mk_req('/foo/bar') @handler.path(req).should == "/foo/bar" end it "should return the request body as the body" do req = mk_req('/foo/bar', :input => 'mybody') @handler.body(req).should == "mybody" end it "should set the response's content-type header when setting the content type" do @header = mock 'header' @response.expects(:header).returns @header @header.expects(:[]=).with('Content-Type', "mytype") @handler.set_content_type(@response, "mytype") end it "should set the status and write the body when setting the response for a request" do @response.expects(:status=).with(400) @response.expects(:write).with("mybody") @handler.set_response(@response, "mybody", 400) end describe "when result is a File" do before :each do stat = stub 'stat', :size => 100 @file = stub 'file', :stat => stat, :path => "/tmp/path" @file.stubs(:is_a?).with(File).returns(true) end it "should set the Content-Length header as a string" do @response.expects(:[]=).with("Content-Length", '100') @handler.set_response(@response, @file, 200) end it "should return a RackFile adapter as body" do @response.expects(:body=).with { |val| val.is_a?(Puppet::Network::HTTP::RackREST::RackFile) } @handler.set_response(@response, @file, 200) end end end describe "and determining the request parameters" do it "should include the HTTP request parameters, with the keys as symbols" do req = mk_req('/?foo=baz&bar=xyzzy') result = @handler.params(req) result[:foo].should == "baz" result[:bar].should == "xyzzy" end it "should CGI-decode the HTTP parameters" do encoding = CGI.escape("foo bar") req = mk_req("/?foo=#{encoding}") result = @handler.params(req) result[:foo].should == "foo bar" end it "should convert the string 'true' to the boolean" do req = mk_req("/?foo=true") result = @handler.params(req) result[:foo].should be_true end it "should convert the string 'false' to the boolean" do req = mk_req("/?foo=false") result = @handler.params(req) result[:foo].should be_false end it "should convert integer arguments to Integers" do req = mk_req("/?foo=15") result = @handler.params(req) result[:foo].should == 15 end it "should convert floating point arguments to Floats" do req = mk_req("/?foo=1.5") result = @handler.params(req) result[:foo].should == 1.5 end it "should YAML-load and CGI-decode values that are YAML-encoded" do escaping = CGI.escape(YAML.dump(%w{one two})) req = mk_req("/?foo=#{escaping}") result = @handler.params(req) result[:foo].should == %w{one two} end it "should not allow the client to set the node via the query string" do req = mk_req("/?node=foo") @handler.params(req)[:node].should be_nil end it "should not allow the client to set the IP address via the query string" do req = mk_req("/?ip=foo") @handler.params(req)[:ip].should be_nil end it "should pass the client's ip address to model find" do req = mk_req("/", 'REMOTE_ADDR' => 'ipaddress') @handler.params(req)[:ip].should == "ipaddress" end it "should set 'authenticated' to false if no certificate is present" do req = mk_req('/') @handler.params(req)[:authenticated].should be_false end end describe "with pre-validated certificates" do it "should use the :ssl_client_header to determine the parameter when looking for the certificate" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req('/', "myheader" => "/CN=host.domain.com") @handler.params(req) end it "should retrieve the hostname by matching the certificate parameter" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req('/', "myheader" => "/CN=host.domain.com") @handler.params(req)[:node].should == "host.domain.com" end it "should use the :ssl_client_header to determine the parameter for checking whether the host certificate is valid" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.expects(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") @handler.params(req) end it "should consider the host authenticated if the validity parameter contains 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => "SUCCESS", "certheader" => "/CN=host.domain.com") @handler.params(req)[:authenticated].should be_true end it "should consider the host unauthenticated if the validity parameter does not contain 'SUCCESS'" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => "whatever", "certheader" => "/CN=host.domain.com") @handler.params(req)[:authenticated].should be_false end it "should consider the host unauthenticated if no certificate information is present" do Puppet.settings.stubs(:value).with(:ssl_client_header).returns "certheader" Puppet.settings.stubs(:value).with(:ssl_client_verify_header).returns "myheader" req = mk_req('/', "myheader" => nil, "certheader" => "/CN=host.domain.com") @handler.params(req)[:authenticated].should be_false end it "should resolve the node name with an ip address look-up if no certificate is present" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:ssl_client_header).returns "myheader" req = mk_req('/', "myheader" => nil) @handler.expects(:resolve_node).returns("host.domain.com") @handler.params(req)[:node].should == "host.domain.com" end end end end describe Puppet::Network::HTTP::RackREST::RackFile do before(:each) do stat = stub 'stat', :size => 100 @file = stub 'file', :stat => stat, :path => "/tmp/path" @rackfile = Puppet::Network::HTTP::RackREST::RackFile.new(@file) end it "should have an each method" do @rackfile.should be_respond_to(:each) end it "should yield file chunks by chunks" do @file.expects(:read).times(3).with(8192).returns("1", "2", nil) i = 1 @rackfile.each do |chunk| chunk.to_i.should == i i += 1 end end it "should have a close method" do @rackfile.should be_respond_to(:close) end it "should delegate close to File close" do @file.expects(:close) @rackfile.close end end diff --git a/spec/unit/network/http/rack_spec.rb b/spec/unit/network/http/rack_spec.rb index 5f148091a..9b37bf050 100755 --- a/spec/unit/network/http/rack_spec.rb +++ b/spec/unit/network/http/rack_spec.rb @@ -1,43 +1,43 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http/rack' if Puppet.features.rack? describe "Puppet::Network::HTTP::Rack", :if => Puppet.features.rack? do describe "when called" do before :all do @app = Puppet::Network::HTTP::Rack.new() # let's use Rack::Lint to verify that we're OK with the rack specification @linted = Rack::Lint.new(@app) end before :each do @env = Rack::MockRequest.env_for('/') end it "should create a Request object" do request = Rack::Request.new(@env) Rack::Request.expects(:new).returns request @linted.call(@env) end it "should create a Response object" do Rack::Response.expects(:new).returns stub_everything @app.call(@env) # can't lint when Rack::Response is a stub end it "should let RackREST process the request" do Puppet::Network::HTTP::RackREST.any_instance.expects(:process).once @linted.call(@env) end it "should catch unhandled exceptions from RackREST" do Puppet::Network::HTTP::RackREST.any_instance.expects(:process).raises(ArgumentError, 'test error') Proc.new { @linted.call(@env) }.should_not raise_error end it "should finish() the Response" do Rack::Response.any_instance.expects(:finish).once @app.call(@env) # can't lint when finish is a stub end end end diff --git a/spec/unit/network/http/webrick/rest_spec.rb b/spec/unit/network/http/webrick/rest_spec.rb index 1c3122d3f..31ec93c7b 100755 --- a/spec/unit/network/http/webrick/rest_spec.rb +++ b/spec/unit/network/http/webrick/rest_spec.rb @@ -1,190 +1,190 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http' require 'webrick' require 'puppet/network/http/webrick/rest' describe Puppet::Network::HTTP::WEBrickREST do it "should include the Puppet::Network::HTTP::Handler module" do Puppet::Network::HTTP::WEBrickREST.ancestors.should be_include(Puppet::Network::HTTP::Handler) end describe "when initializing" do it "should call the Handler's initialization hook with its provided arguments as the server and handler" do server = WEBrick::HTTPServer.new(:BindAddress => '127.0.0.1', # Probablistically going to succeed # even if we run more than one test # instance at once. :Port => 40000 + rand(10000), # Just discard any log output, thanks. :Logger => stub_everything('logger')) Puppet::Network::HTTP::WEBrickREST.any_instance. expects(:initialize_for_puppet).with(:server => server, :handler => "arguments") Puppet::Network::HTTP::WEBrickREST.new(server, "arguments") end end describe "when receiving a request" do before do @request = stub('webrick http request', :query => {}, :peeraddr => %w{eh boo host ip}, :client_cert => nil) @response = stub('webrick http response', :status= => true, :body= => true) @model_class = stub('indirected model class') @webrick = stub('webrick http server', :mount => true, :[] => {}) Puppet::Indirector::Indirection.stubs(:model).with(:foo).returns(@model_class) @handler = Puppet::Network::HTTP::WEBrickREST.new(@webrick, :foo) end it "should delegate its :service method to its :process method" do @handler.expects(:process).with(@request, @response).returns "stuff" @handler.service(@request, @response).should == "stuff" end describe "when using the Handler interface" do it "should use the 'accept' request parameter as the Accept header" do @request.expects(:[]).with("accept").returns "foobar" @handler.accept_header(@request).should == "foobar" end it "should use the 'content-type' request header as the Content-Type header" do @request.expects(:[]).with("content-type").returns "foobar" @handler.content_type_header(@request).should == "foobar" end it "should use the request method as the http method" do @request.expects(:request_method).returns "FOO" @handler.http_method(@request).should == "FOO" end it "should return the request path as the path" do @request.expects(:path).returns "/foo/bar" @handler.path(@request).should == "/foo/bar" end it "should return the request body as the body" do @request.expects(:body).returns "my body" @handler.body(@request).should == "my body" end it "should set the response's 'content-type' header when setting the content type" do @response.expects(:[]=).with("content-type", "text/html") @handler.set_content_type(@response, "text/html") end it "should set the status and body on the response when setting the response for a successful query" do @response.expects(:status=).with 200 @response.expects(:body=).with "mybody" @handler.set_response(@response, "mybody", 200) end describe "when the result is a File" do before(:each) do stat = stub 'stat', :size => 100 @file = stub 'file', :stat => stat, :path => "/tmp/path" @file.stubs(:is_a?).with(File).returns(true) end it "should serve it" do @response.stubs(:[]=) @response.expects(:status=).with 200 @response.expects(:body=).with @file @handler.set_response(@response, @file, 200) end it "should set the Content-Length header" do @response.expects(:[]=).with('content-length', 100) @handler.set_response(@response, @file, 200) end end it "should set the status and message on the response when setting the response for a failed query" do @response.expects(:status=).with 400 @response.expects(:reason_phrase=).with "mybody" @handler.set_response(@response, "mybody", 400) end end describe "and determining the request parameters" do it "should include the HTTP request parameters, with the keys as symbols" do @request.stubs(:query).returns("foo" => "baz", "bar" => "xyzzy") result = @handler.params(@request) result[:foo].should == "baz" result[:bar].should == "xyzzy" end it "should CGI-decode the HTTP parameters" do encoding = CGI.escape("foo bar") @request.expects(:query).returns('foo' => encoding) result = @handler.params(@request) result[:foo].should == "foo bar" end it "should convert the string 'true' to the boolean" do @request.expects(:query).returns('foo' => "true") result = @handler.params(@request) result[:foo].should be_true end it "should convert the string 'false' to the boolean" do @request.expects(:query).returns('foo' => "false") result = @handler.params(@request) result[:foo].should be_false end it "should YAML-load and CGI-decode values that are YAML-encoded" do escaping = CGI.escape(YAML.dump(%w{one two})) @request.expects(:query).returns('foo' => escaping) result = @handler.params(@request) result[:foo].should == %w{one two} end it "should not allow clients to set the node via the request parameters" do @request.stubs(:query).returns("node" => "foo") @handler.stubs(:resolve_node) @handler.params(@request)[:node].should be_nil end it "should not allow clients to set the IP via the request parameters" do @request.stubs(:query).returns("ip" => "foo") @handler.params(@request)[:ip].should_not == "foo" end it "should pass the client's ip address to model find" do @request.stubs(:peeraddr).returns(%w{noidea dunno hostname ipaddress}) @handler.params(@request)[:ip].should == "ipaddress" end it "should set 'authenticated' to true if a certificate is present" do cert = stub 'cert', :subject => [%w{CN host.domain.com}] @request.stubs(:client_cert).returns cert @handler.params(@request)[:authenticated].should be_true end it "should set 'authenticated' to false if no certificate is present" do @request.stubs(:client_cert).returns nil @handler.params(@request)[:authenticated].should be_false end it "should pass the client's certificate name to model method if a certificate is present" do cert = stub 'cert', :subject => [%w{CN host.domain.com}] @request.stubs(:client_cert).returns cert @handler.params(@request)[:node].should == "host.domain.com" end it "should resolve the node name with an ip address look-up if no certificate is present" do @request.stubs(:client_cert).returns nil @handler.expects(:resolve_node).returns(:resolved_node) @handler.params(@request)[:node].should == :resolved_node end end end end diff --git a/spec/unit/network/http/webrick_spec.rb b/spec/unit/network/http/webrick_spec.rb index b7340d6d4..647221816 100755 --- a/spec/unit/network/http/webrick_spec.rb +++ b/spec/unit/network/http/webrick_spec.rb @@ -1,263 +1,263 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http' require 'puppet/network/http/webrick' describe Puppet::Network::HTTP::WEBrick, "after initializing" do it "should not be listening" do Puppet::Network::HTTP::WEBrick.new.should_not be_listening end end describe Puppet::Network::HTTP::WEBrick, "when turning on listening" do before do @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging @listen_params = { :address => "127.0.0.1", :port => 31337, :protocols => [ :rest ] } end it "should fail if already listening" do @server.listen(@listen_params) Proc.new { @server.listen(@listen_params) }.should raise_error(RuntimeError) end it "should require a listening address to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :address == k})}.should raise_error(ArgumentError) end it "should require a listening port to be specified" do Proc.new { @server.listen(@listen_params.delete_if {|k,v| :port == k})}.should raise_error(ArgumentError) end it "should order a webrick server to start in a separate thread" do @mock_webrick.expects(:start) # If you remove this you'll sometimes get race condition problems Thread.expects(:new).yields @server.listen(@listen_params) end it "should tell webrick to listen on the specified address and port" do WEBrick::HTTPServer.expects(:new).with {|args| args[:Port] == 31337 and args[:BindAddress] == "127.0.0.1" }.returns(@mock_webrick) @server.listen(@listen_params) end it "should configure a logger for webrick" do @server.expects(:setup_logger).returns(:Logger => :mylogger) WEBrick::HTTPServer.expects(:new).with {|args| args[:Logger] == :mylogger }.returns(@mock_webrick) @server.listen(@listen_params) end it "should configure SSL for webrick" do @server.expects(:setup_ssl).returns(:Ssl => :testing, :Other => :yay) WEBrick::HTTPServer.expects(:new).with {|args| args[:Ssl] == :testing and args[:Other] == :yay }.returns(@mock_webrick) @server.listen(@listen_params) end it "should be listening" do @server.listen(@listen_params) @server.should be_listening end describe "when the REST protocol is requested" do it "should register the REST handler at /" do # We don't care about the options here. @mock_webrick.expects(:mount).with { |path, klass, options| path == "/" and klass == Puppet::Network::HTTP::WEBrickREST } @server.listen(@listen_params.merge(:protocols => [:rest])) end end end describe Puppet::Network::HTTP::WEBrick, "when turning off listening" do before do @mock_webrick = stub('webrick', :[] => {}, :listeners => [], :status => :Running) [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new [:setup_logger, :setup_ssl].each {|meth| @server.stubs(meth).returns({})} # the empty hash is required because of how we're merging @listen_params = { :address => "127.0.0.1", :port => 31337, :handlers => [ :node, :catalog ], :protocols => [ :rest ] } end it "should fail unless listening" do Proc.new { @server.unlisten }.should raise_error(RuntimeError) end it "should order webrick server to stop" do @mock_webrick.expects(:shutdown) @server.listen(@listen_params) @server.unlisten end it "should no longer be listening" do @server.listen(@listen_params) @server.unlisten @server.should_not be_listening end end describe Puppet::Network::HTTP::WEBrick do before do @mock_webrick = stub('webrick', :[] => {}) [:mount, :start, :shutdown].each {|meth| @mock_webrick.stubs(meth)} WEBrick::HTTPServer.stubs(:new).returns(@mock_webrick) @server = Puppet::Network::HTTP::WEBrick.new end describe "when configuring an http logger" do before do Puppet.settings.stubs(:value).returns "something" Puppet.settings.stubs(:use) @filehandle = stub 'handle', :fcntl => nil, :sync= => nil File.stubs(:open).returns @filehandle end it "should use the settings for :main, :ssl, and :application" do Puppet.settings.expects(:use).with(:main, :ssl, :application) @server.setup_logger end it "should use the masterlog if the run_mode is master" do Puppet.run_mode.stubs(:master?).returns(true) Puppet.settings.expects(:value).with(:masterhttplog).returns "/master/log" File.expects(:open).with("/master/log", "a+").returns @filehandle @server.setup_logger end it "should use the httplog if the run_mode is not master" do Puppet.run_mode.stubs(:master?).returns(false) Puppet.settings.expects(:value).with(:httplog).returns "/other/log" File.expects(:open).with("/other/log", "a+").returns @filehandle @server.setup_logger end describe "and creating the logging filehandle" do it "should set the close-on-exec flag if supported" do if defined? Fcntl::FD_CLOEXEC @filehandle.expects(:fcntl).with(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) else @filehandle.expects(:fcntl).never end @server.setup_logger end it "should sync the filehandle" do @filehandle.expects(:sync=).with(true) @server.setup_logger end end it "should create a new WEBrick::Log instance with the open filehandle" do WEBrick::Log.expects(:new).with(@filehandle) @server.setup_logger end it "should set debugging if the current loglevel is :debug" do Puppet::Util::Log.expects(:level).returns :debug WEBrick::Log.expects(:new).with { |handle, debug| debug == WEBrick::Log::DEBUG } @server.setup_logger end it "should return the logger as the main log" do logger = mock 'logger' WEBrick::Log.expects(:new).returns logger @server.setup_logger[:Logger].should == logger end it "should return the logger as the access log using both the Common and Referer log format" do logger = mock 'logger' WEBrick::Log.expects(:new).returns logger @server.setup_logger[:AccessLog].should == [ [logger, WEBrick::AccessLog::COMMON_LOG_FORMAT], [logger, WEBrick::AccessLog::REFERER_LOG_FORMAT] ] end end describe "when configuring ssl" do before do @key = stub 'key', :content => "mykey" @cert = stub 'cert', :content => "mycert" @host = stub 'host', :key => @key, :certificate => @cert, :name => "yay", :ssl_store => "mystore" Puppet::SSL::Certificate.indirection.stubs(:find).with('ca').returns @cert Puppet::SSL::Host.stubs(:localhost).returns @host end it "should use the key from the localhost SSL::Host instance" do Puppet::SSL::Host.expects(:localhost).returns @host @host.expects(:key).returns @key @server.setup_ssl[:SSLPrivateKey].should == "mykey" end it "should configure the certificate" do @server.setup_ssl[:SSLCertificate].should == "mycert" end it "should fail if no CA certificate can be found" do Puppet::SSL::Certificate.indirection.stubs(:find).with('ca').returns nil lambda { @server.setup_ssl }.should raise_error(Puppet::Error) end it "should specify the path to the CA certificate" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:hostcrl).returns 'false' Puppet.settings.stubs(:value).with(:localcacert).returns '/ca/crt' @server.setup_ssl[:SSLCACertificateFile].should == "/ca/crt" end it "should start ssl immediately" do @server.setup_ssl[:SSLStartImmediately].should be_true end it "should enable ssl" do @server.setup_ssl[:SSLEnable].should be_true end it "should configure the verification method as 'OpenSSL::SSL::VERIFY_PEER'" do @server.setup_ssl[:SSLVerifyClient].should == OpenSSL::SSL::VERIFY_PEER end it "should add an x509 store" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:hostcrl).returns '/my/crl' @host.expects(:ssl_store).returns "mystore" @server.setup_ssl[:SSLCertificateStore].should == "mystore" end it "should set the certificate name to 'nil'" do @server.setup_ssl[:SSLCertName].should be_nil end end end diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb index 81258aade..5fde3c3cf 100755 --- a/spec/unit/network/http_pool_spec.rb +++ b/spec/unit/network/http_pool_spec.rb @@ -1,141 +1,141 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http_pool' describe Puppet::Network::HttpPool do after do Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) end it "should use the global SSL::Host instance to get its certificate information" do host = mock 'host' Puppet::SSL::Host.expects(:localhost).with.returns host Puppet::Network::HttpPool.ssl_host.should equal(host) end describe "when managing http instances" do before :each do # All of the cert stuff is tested elsewhere Puppet::Network::HttpPool.stubs(:cert_setup) end it "should return an http instance created with the passed host and port" do http = Puppet::Network::HttpPool.http_instance("me", 54321) http.should be_an_instance_of Net::HTTP http.address.should == 'me' http.port.should == 54321 end it "should enable ssl on the http instance" do Puppet::Network::HttpPool.http_instance("me", 54321).should be_use_ssl end context "proxy and timeout settings should propagate" do subject { Puppet::Network::HttpPool.http_instance("me", 54321) } before :each do Puppet[:http_proxy_host] = "myhost" Puppet[:http_proxy_port] = 432 Puppet[:configtimeout] = 120 end its(:open_timeout) { should == Puppet[:configtimeout] } its(:read_timeout) { should == Puppet[:configtimeout] } its(:proxy_address) { should == Puppet[:http_proxy_host] } its(:proxy_port) { should == Puppet[:http_proxy_port] } end it "should not set a proxy if the value is 'none'" do Puppet[:http_proxy_host] = 'none' Puppet::Network::HttpPool.http_instance("me", 54321).proxy_address.should be_nil end it "should not cache http instances" do Puppet::Network::HttpPool.http_instance("me", 54321). should_not equal Puppet::Network::HttpPool.http_instance("me", 54321) end end describe "when doing SSL setup for http instances" do let :http do http = Net::HTTP.new('localhost', 443) http.use_ssl = true http end let :store do stub('store') end before :each do Puppet[:hostcert] = '/host/cert' Puppet[:localcacert] = '/local/ca/cert' cert = stub 'cert', :content => 'real_cert' key = stub 'key', :content => 'real_key' host = stub 'host', :certificate => cert, :key => key, :ssl_store => store Puppet::Network::HttpPool.stubs(:ssl_host).returns(host) end shared_examples "HTTPS setup without all certificates" do subject { Puppet::Network::HttpPool.cert_setup(http); http } it { should be_use_ssl } its(:cert) { should be_nil } its(:cert_store) { should be_nil } its(:ca_file) { should be_nil } its(:key) { should be_nil } its(:verify_mode) { should == OpenSSL::SSL::VERIFY_NONE } end context "with neither a host cert or a local CA cert" do before :each do FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(false) FileTest.stubs(:exist?).with(Puppet[:localcacert]).returns(false) end include_examples "HTTPS setup without all certificates" end context "with there is no host certificate" do before :each do FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(false) FileTest.stubs(:exist?).with(Puppet[:localcacert]).returns(true) end include_examples "HTTPS setup without all certificates" end context "with there is no local CA certificate" do before :each do FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(true) FileTest.stubs(:exist?).with(Puppet[:localcacert]).returns(false) end include_examples "HTTPS setup without all certificates" end context "with both the host and CA cert" do subject { Puppet::Network::HttpPool.cert_setup(http); http } before :each do FileTest.expects(:exist?).with(Puppet[:hostcert]).returns(true) FileTest.expects(:exist?).with(Puppet[:localcacert]).returns(true) end it { should be_use_ssl } its(:cert_store) { should equal store } its(:cert) { should == "real_cert" } its(:key) { should == "real_key" } its(:verify_mode) { should == OpenSSL::SSL::VERIFY_PEER } its(:ca_file) { should == Puppet[:localcacert] } end it "should set up certificate information when creating http instances" do Puppet::Network::HttpPool.expects(:cert_setup).with do |http| http.should be_an_instance_of Net::HTTP http.address.should == "one" http.port.should == 2 end Puppet::Network::HttpPool.http_instance("one", 2) end end end diff --git a/spec/unit/network/http_spec.rb b/spec/unit/network/http_spec.rb index 3f807a50b..ac5b83fc5 100755 --- a/spec/unit/network/http_spec.rb +++ b/spec/unit/network/http_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http' describe Puppet::Network::HTTP do it "should return the webrick HTTP server class when asked for a webrick server" do Puppet::Network::HTTP.server_class_by_type(:webrick).should be(Puppet::Network::HTTP::WEBrick) end describe "when asked for a mongrel server" do if Puppet.features.mongrel? it "should return the mongrel server class" do Puppet::Network::HTTP.server_class_by_type(:mongrel).should be(Puppet::Network::HTTP::Mongrel) end else it "should fail" do lambda { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) end end end it "should fail to return the mongrel HTTP server class if mongrel is not available " do Puppet.features.expects(:mongrel?).returns(false) Proc.new { Puppet::Network::HTTP.server_class_by_type(:mongrel) }.should raise_error(ArgumentError) end it "should return an error when asked for an unknown server" do Proc.new { Puppet::Network::HTTP.server_class_by_type :foo }.should raise_error(ArgumentError) end end diff --git a/spec/unit/network/rest_authconfig_spec.rb b/spec/unit/network/rest_authconfig_spec.rb index bebbb874f..611d0b0b7 100755 --- a/spec/unit/network/rest_authconfig_spec.rb +++ b/spec/unit/network/rest_authconfig_spec.rb @@ -1,127 +1,127 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/rest_authconfig' describe Puppet::Network::RestAuthConfig do DEFAULT_ACL = Puppet::Network::RestAuthConfig::DEFAULT_ACL before :each do FileTest.stubs(:exists?).returns(true) File.stubs(:stat).returns(stub('stat', :ctime => :now)) Time.stubs(:now).returns Time.now @authconfig = Puppet::Network::RestAuthConfig.new("dummy", false) @authconfig.stubs(:read) @acl = stub_everything 'rights' @authconfig.rights = @acl end it "should use the puppet default rest authorization file" do Puppet.expects(:[]).with(:rest_authconfig).returns("dummy") Puppet::Network::RestAuthConfig.new(nil, false) end it "should ask for authorization to the ACL subsystem" do params = {:ip => "127.0.0.1", :node => "me", :environment => :env, :authenticated => true} @acl.expects(:is_request_forbidden_and_why?).with("path", :save, "to/resource", params).returns(nil) @authconfig.check_authorization("path", :save, "to/resource", params) end describe "when defining an acl with mk_acl" do it "should create a new right for each default acl" do @acl.expects(:newright).with(:path) @authconfig.mk_acl(:acl => :path) end it "should allow everyone for each default right" do @acl.expects(:allow).with(:path, "*") @authconfig.mk_acl(:acl => :path) end it "should restrict the ACL to a method" do @acl.expects(:restrict_method).with(:path, :method) @authconfig.mk_acl(:acl => :path, :method => :method) end it "should restrict the ACL to a specific authentication state" do @acl.expects(:restrict_authenticated).with(:path, :authentication) @authconfig.mk_acl(:acl => :path, :authenticated => :authentication) end end describe "when parsing the configuration file" do it "should check for missing ACL after reading the authconfig file" do File.stubs(:open) @authconfig.expects(:insert_default_acl) @authconfig.parse end end DEFAULT_ACL.each do |acl| it "should insert #{acl[:acl]} if not present" do @authconfig.rights.stubs(:[]).returns(true) @authconfig.rights.stubs(:[]).with(acl[:acl]).returns(nil) @authconfig.expects(:mk_acl).with { |h| h[:acl] == acl[:acl] } @authconfig.insert_default_acl end it "should not insert #{acl[:acl]} if present" do @authconfig.rights.stubs(:[]).returns(true) @authconfig.rights.stubs(:[]).with(acl).returns(true) @authconfig.expects(:mk_acl).never @authconfig.insert_default_acl end end it "should create default ACL entries if no file have been read" do Puppet::Network::RestAuthConfig.any_instance.stubs(:exists?).returns(false) Puppet::Network::RestAuthConfig.any_instance.expects(:insert_default_acl) Puppet::Network::RestAuthConfig.main end describe "when adding default ACLs" do DEFAULT_ACL.each do |acl| it "should create a default right for #{acl[:acl]}" do @authconfig.stubs(:mk_acl) @authconfig.expects(:mk_acl).with(acl) @authconfig.insert_default_acl end end it "should log at info loglevel" do Puppet.expects(:info).at_least_once @authconfig.insert_default_acl end it "should create a last catch-all deny all rule" do @authconfig.stubs(:mk_acl) @acl.expects(:newright).with("/") @authconfig.insert_default_acl end it "should create a last catch-all deny all rule for any authenticated request state" do @authconfig.stubs(:mk_acl) @acl.stubs(:newright).with("/") @acl.expects(:restrict_authenticated).with("/", :any) @authconfig.insert_default_acl end end end diff --git a/spec/unit/network/rights_spec.rb b/spec/unit/network/rights_spec.rb index 30a4024fc..9d2666489 100755 --- a/spec/unit/network/rights_spec.rb +++ b/spec/unit/network/rights_spec.rb @@ -1,529 +1,529 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/rights' describe Puppet::Network::Rights do before do @right = Puppet::Network::Rights.new end describe "when validating a :head request" do [:find, :save].each do |allowed_method| it "should allow the request if only #{allowed_method} is allowed" do rights = Puppet::Network::Rights.new rights.newright("/") rights.allow("/", "*") rights.restrict_method("/", allowed_method) rights.restrict_authenticated("/", :any) rights.is_request_forbidden_and_why?(:indirection_name, :head, "key", {}).should == nil end end it "should disallow the request if neither :find nor :save is allowed" do rights = Puppet::Network::Rights.new why_forbidden = rights.is_request_forbidden_and_why?(:indirection_name, :head, "key", {}) why_forbidden.should be_instance_of(Puppet::Network::AuthorizationError) why_forbidden.to_s.should == "Forbidden request: access to /indirection_name/key [find]" end end [:allow, :deny, :restrict_method, :restrict_environment, :restrict_authenticated].each do |m| it "should have a #{m} method" do @right.should respond_to(m) end describe "when using #{m}" do it "should delegate to the correct acl" do acl = stub 'acl' @right.stubs(:[]).returns(acl) acl.expects(m).with("me") @right.send(m, 'thisacl', "me") end end end it "should throw an error if type can't be determined" do lambda { @right.newright("name") }.should raise_error end describe "when creating new namespace ACLs" do it "should throw an error if the ACL already exists" do @right.newright("[name]") lambda { @right.newright("[name]") }.should raise_error end it "should create a new ACL with the correct name" do @right.newright("[name]") @right["name"].key.should == :name end it "should create an ACL of type Puppet::Network::AuthStore" do @right.newright("[name]") @right["name"].should be_a_kind_of(Puppet::Network::AuthStore) end end describe "when creating new path ACLs" do it "should not throw an error if the ACL already exists" do @right.newright("/name") lambda { @right.newright("/name")}.should_not raise_error end it "should throw an error if the acl uri path is not absolute" do lambda { @right.newright("name")}.should raise_error end it "should create a new ACL with the correct path" do @right.newright("/name") @right["/name"].should_not be_nil end it "should create an ACL of type Puppet::Network::AuthStore" do @right.newright("/name") @right["/name"].should be_a_kind_of(Puppet::Network::AuthStore) end end describe "when creating new regex ACLs" do it "should not throw an error if the ACL already exists" do @right.newright("~ .rb$") lambda { @right.newright("~ .rb$")}.should_not raise_error end it "should create a new ACL with the correct regex" do @right.newright("~ .rb$") @right.include?(".rb$").should_not be_nil end it "should be able to lookup the regex" do @right.newright("~ .rb$") @right[".rb$"].should_not be_nil end it "should be able to lookup the regex by its full name" do @right.newright("~ .rb$") @right["~ .rb$"].should_not be_nil end it "should create an ACL of type Puppet::Network::AuthStore" do @right.newright("~ .rb$").should be_a_kind_of(Puppet::Network::AuthStore) end end describe "when checking ACLs existence" do it "should return false if there are no matching rights" do @right.include?("name").should be_false end it "should return true if a namespace rights exist" do @right.newright("[name]") @right.include?("name").should be_true end it "should return false if no matching namespace rights exist" do @right.newright("[name]") @right.include?("notname").should be_false end it "should return true if a path right exists" do @right.newright("/name") @right.include?("/name").should be_true end it "should return false if no matching path rights exist" do @right.newright("/name") @right.include?("/differentname").should be_false end it "should return true if a regex right exists" do @right.newright("~ .rb$") @right.include?(".rb$").should be_true end it "should return false if no matching path rights exist" do @right.newright("~ .rb$") @right.include?(".pp$").should be_false end end describe "when checking if right is allowed" do before :each do @right.stubs(:right).returns(nil) @pathacl = stub 'pathacl', :acl_type => :regex, :"<=>" => 1, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).returns(@pathacl) end it "should delegate to is_forbidden_and_why?" do @right.expects(:is_forbidden_and_why?).with("namespace", :node => "host.domain.com", :ip => "127.0.0.1").returns(nil) @right.allowed?("namespace", "host.domain.com", "127.0.0.1") end it "should return true if is_forbidden_and_why? returns nil" do @right.stubs(:is_forbidden_and_why?).returns(nil) @right.allowed?("namespace", :args).should be_true end it "should return false if is_forbidden_and_why? returns an AuthorizationError" do @right.stubs(:is_forbidden_and_why?).returns(Puppet::Network::AuthorizationError.new("forbidden")) @right.allowed?("namespace", :args1, :args2).should be_false end it "should first check namespace rights" do acl = stub 'acl', :acl_type => :name, :key => :namespace Puppet::Network::Rights::Right.stubs(:new).returns(acl) @right.newright("[namespace]") acl.expects(:match?).returns(true) acl.expects(:allowed?).with { |node,ip,h| node == "node" and ip == "ip" }.returns(true) @right.is_forbidden_and_why?("namespace", { :node => "node", :ip => "ip" } ).should == nil end it "should then check for path rights if no namespace match" do acl = stub 'nmacl', :acl_type => :name, :key => :namespace, :"<=>" => -1, :line => 0, :file => 'dummy' acl.stubs(:match?).returns(false) Puppet::Network::Rights::Right.stubs(:new).with("[namespace]").returns(acl) @right.newright("[namespace]") @right.newright("/path/to/there", 0, nil) @pathacl.stubs(:match?).returns(true) acl.expects(:allowed?).never @pathacl.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there", {}).should == nil end it "should pass the match? return to allowed?" do @right.newright("/path/to/there") @pathacl.expects(:match?).returns(:match) @pathacl.expects(:allowed?).with { |node,ip,h| h[:match] == :match }.returns(true) @right.is_forbidden_and_why?("/path/to/there", {}).should == nil end describe "with namespace acls" do it "should return an ArgumentError if this namespace right doesn't exist" do lambda { @right.is_forbidden_and_why?("namespace") }.should raise_error(ArgumentError) end end describe "with path acls" do before :each do @long_acl = stub 'longpathacl', :name => "/path/to/there", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("/path/to/there", 0, nil).returns(@long_acl) @short_acl = stub 'shortpathacl', :name => "/path/to", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("/path/to", 0, nil).returns(@short_acl) @long_acl.stubs(:"<=>").with(@short_acl).returns(0) @short_acl.stubs(:"<=>").with(@long_acl).returns(0) end it "should select the first match" do @right.newright("/path/to/there", 0) @right.newright("/path/to", 0) @long_acl.stubs(:match?).returns(true) @short_acl.stubs(:match?).returns(true) @long_acl.expects(:allowed?).returns(true) @short_acl.expects(:allowed?).never @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should select the first match that doesn't return :dunno" do @right.newright("/path/to/there", 0, nil) @right.newright("/path/to", 0, nil) @long_acl.stubs(:match?).returns(true) @short_acl.stubs(:match?).returns(true) @long_acl.expects(:allowed?).returns(:dunno) @short_acl.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should not select an ACL that doesn't match" do @right.newright("/path/to/there", 0) @right.newright("/path/to", 0) @long_acl.stubs(:match?).returns(false) @short_acl.stubs(:match?).returns(true) @long_acl.expects(:allowed?).never @short_acl.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should not raise an AuthorizationError if allowed" do @right.newright("/path/to/there", 0) @long_acl.stubs(:match?).returns(true) @long_acl.stubs(:allowed?).returns(true) @right.is_forbidden_and_why?("/path/to/there/and/there", {}).should == nil end it "should raise an AuthorizationError if the match is denied" do @right.newright("/path/to/there", 0, nil) @long_acl.stubs(:match?).returns(true) @long_acl.stubs(:allowed?).returns(false) @right.is_forbidden_and_why?("/path/to/there", {}).should be_instance_of(Puppet::Network::AuthorizationError) end it "should raise an AuthorizationError if no path match" do @right.is_forbidden_and_why?("/nomatch", {}).should be_instance_of(Puppet::Network::AuthorizationError) end end describe "with regex acls" do before :each do @regex_acl1 = stub 'regex_acl1', :name => "/files/(.*)/myfile", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile", 0, nil).returns(@regex_acl1) @regex_acl2 = stub 'regex_acl2', :name => "/files/(.*)/myfile/", :acl_type => :regex, :line => 0, :file => 'dummy' Puppet::Network::Rights::Right.stubs(:new).with("~ /files/(.*)/myfile/", 0, nil).returns(@regex_acl2) @regex_acl1.stubs(:"<=>").with(@regex_acl2).returns(0) @regex_acl2.stubs(:"<=>").with(@regex_acl1).returns(0) end it "should select the first match" do @right.newright("~ /files/(.*)/myfile", 0) @right.newright("~ /files/(.*)/myfile/", 0) @regex_acl1.stubs(:match?).returns(true) @regex_acl2.stubs(:match?).returns(true) @regex_acl1.expects(:allowed?).returns(true) @regex_acl2.expects(:allowed?).never @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should select the first match that doesn't return :dunno" do @right.newright("~ /files/(.*)/myfile", 0) @right.newright("~ /files/(.*)/myfile/", 0) @regex_acl1.stubs(:match?).returns(true) @regex_acl2.stubs(:match?).returns(true) @regex_acl1.expects(:allowed?).returns(:dunno) @regex_acl2.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should not select an ACL that doesn't match" do @right.newright("~ /files/(.*)/myfile", 0) @right.newright("~ /files/(.*)/myfile/", 0) @regex_acl1.stubs(:match?).returns(false) @regex_acl2.stubs(:match?).returns(true) @regex_acl1.expects(:allowed?).never @regex_acl2.expects(:allowed?).returns(true) @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should not raise an AuthorizationError if allowed" do @right.newright("~ /files/(.*)/myfile", 0) @regex_acl1.stubs(:match?).returns(true) @regex_acl1.stubs(:allowed?).returns(true) @right.is_forbidden_and_why?("/files/repository/myfile/other", {}).should == nil end it "should raise an error if no regex acl match" do @right.is_forbidden_and_why?("/path", {}).should be_instance_of(Puppet::Network::AuthorizationError) end it "should raise an AuthorizedError on deny" do @right.is_forbidden_and_why?("/path", {}).should be_instance_of(Puppet::Network::AuthorizationError) end end end describe Puppet::Network::Rights::Right do before :each do @acl = Puppet::Network::Rights::Right.new("/path",0, nil) end describe "with path" do it "should say it's a regex ACL" do @acl.acl_type.should == :regex end it "should match up to its path length" do @acl.match?("/path/that/works").should_not be_nil end it "should match up to its path length" do @acl.match?("/paththatalsoworks").should_not be_nil end it "should return nil if no match" do @acl.match?("/notpath").should be_nil end end describe "with regex" do before :each do @acl = Puppet::Network::Rights::Right.new("~ .rb$",0, nil) end it "should say it's a regex ACL" do @acl.acl_type.should == :regex end it "should match as a regex" do @acl.match?("this should work.rb").should_not be_nil end it "should return nil if no match" do @acl.match?("do not match").should be_nil end end it "should allow all rest methods by default" do @acl.methods.should == Puppet::Network::Rights::Right::ALL end it "should allow only authenticated request by default" do @acl.authentication.should be_true end it "should allow modification of the methods filters" do @acl.restrict_method(:save) @acl.methods.should == [:save] end it "should stack methods filters" do @acl.restrict_method(:save) @acl.restrict_method(:destroy) @acl.methods.should == [:save, :destroy] end it "should raise an error if the method is already filtered" do @acl.restrict_method(:save) lambda { @acl.restrict_method(:save) }.should raise_error end it "should allow setting an environment filters" do Puppet::Node::Environment.stubs(:new).with(:environment).returns(:env) @acl.restrict_environment(:environment) @acl.environment.should == [:env] end ["on", "yes", "true", true].each do |auth| it "should allow filtering on authenticated requests with '#{auth}'" do @acl.restrict_authenticated(auth) @acl.authentication.should be_true end end ["off", "no", "false", false, "all", "any", :all, :any].each do |auth| it "should allow filtering on authenticated or unauthenticated requests with '#{auth}'" do @acl.restrict_authenticated(auth) @acl.authentication.should be_false end end describe "when checking right authorization" do it "should return :dunno if this right is not restricted to the given method" do @acl.restrict_method(:destroy) @acl.allowed?("me","127.0.0.1", { :method => :save } ).should == :dunno end it "should return allow/deny if this right is restricted to the given method" do @acl.restrict_method(:save) @acl.allow("127.0.0.1") @acl.allowed?("me","127.0.0.1", { :method => :save }).should be_true end it "should return :dunno if this right is not restricted to the given environment" do Puppet::Node::Environment.stubs(:new).returns(:production) @acl.restrict_environment(:production) @acl.allowed?("me","127.0.0.1", { :method => :save, :environment => :development }).should == :dunno end it "should return :dunno if this right is not restricted to the given request authentication state" do @acl.restrict_authenticated(true) @acl.allowed?("me","127.0.0.1", { :method => :save, :authenticated => false }).should == :dunno end it "should return allow/deny if this right is restricted to the given request authentication state" do @acl.restrict_authenticated(false) @acl.allow("127.0.0.1") @acl.allowed?("me","127.0.0.1", { :authenticated => false }).should be_true end it "should interpolate allow/deny patterns with the given match" do @acl.expects(:interpolate).with(:match) @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) end it "should reset interpolation after the match" do @acl.expects(:reset_interpolation) @acl.allowed?("me","127.0.0.1", { :method => :save, :match => :match, :authenticated => true }) end # mocha doesn't allow testing super... # it "should delegate to the AuthStore for the result" do # @acl.method(:save) # # @acl.expects(:allowed?).with("me","127.0.0.1") # # @acl.allowed?("me","127.0.0.1", :save) # end end end end diff --git a/spec/unit/network/server_spec.rb b/spec/unit/network/server_spec.rb index 6cc07851f..08475e0c5 100755 --- a/spec/unit/network/server_spec.rb +++ b/spec/unit/network/server_spec.rb @@ -1,410 +1,410 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/server' describe Puppet::Network::Server do before do @mock_http_server_class = mock('http server class') Puppet.settings.stubs(:use) Puppet.run_mode.stubs(:name).returns "me" Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) Puppet.settings.stubs(:value).with(:bindaddress).returns("") Puppet.settings.stubs(:value).with(:masterport).returns(8140) Puppet.settings.stubs(:value).with(:daemonize).returns(true) Puppet::Network::HTTP.stubs(:server_class_by_type).returns(@mock_http_server_class) Puppet.settings.stubs(:value).with(:servertype).returns(:suparserver) @server = Puppet::Network::Server.new(:port => 31337) @server.stubs(:close_streams).returns(nil) end describe "when initializing" do before do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') Puppet.settings.stubs(:value).with(:bindaddress).returns("") Puppet.settings.stubs(:value).with(:masterport).returns('') end it 'should fail if an unknown option is provided' do lambda { Puppet::Network::Server.new(:foo => 31337) }.should raise_error(ArgumentError) end it "should allow specifying a listening port" do Puppet.settings.stubs(:value).with(:bindaddress).returns('') @server = Puppet::Network::Server.new(:port => 31337) @server.port.should == 31337 end it "should use the :bindaddress setting to determine the default listening address" do Puppet.settings.stubs(:value).with(:masterport).returns('') Puppet.settings.expects(:value).with(:bindaddress).returns("10.0.0.1") @server = Puppet::Network::Server.new @server.address.should == "10.0.0.1" end it "should set the bind address to '127.0.0.1' if the default address is an empty string and the server type is mongrel" do Puppet.settings.stubs(:value).with(:servertype).returns("mongrel") Puppet.settings.expects(:value).with(:bindaddress).returns("") @server = Puppet::Network::Server.new @server.address.should == '127.0.0.1' end it "should set the bind address to '0.0.0.0' if the default address is an empty string and the server type is webrick" do Puppet.settings.stubs(:value).with(:servertype).returns("webrick") Puppet.settings.expects(:value).with(:bindaddress).returns("") @server = Puppet::Network::Server.new @server.address.should == '0.0.0.0' end it "should use the Puppet configurator to find a default listening port" do Puppet.settings.stubs(:value).with(:bindaddress).returns('') Puppet.settings.expects(:value).with(:masterport).returns(6667) @server = Puppet::Network::Server.new @server.port.should == 6667 end it "should fail to initialize if no listening port can be found" do Puppet.settings.stubs(:value).with(:bindaddress).returns("127.0.0.1") Puppet.settings.stubs(:value).with(:masterport).returns(nil) lambda { Puppet::Network::Server.new }.should raise_error(ArgumentError) end it "should use the Puppet configurator to determine which HTTP server will be used to provide access to clients" do Puppet.settings.expects(:value).with(:servertype).returns(:suparserver) @server = Puppet::Network::Server.new(:port => 31337) @server.server_type.should == :suparserver end it "should fail to initialize if there is no HTTP server known to the Puppet configurator" do Puppet.settings.expects(:value).with(:servertype).returns(nil) lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error end it "should ask the Puppet::Network::HTTP class to fetch the proper HTTP server class" do Puppet::Network::HTTP.expects(:server_class_by_type).with(:suparserver).returns(@mock_http_server_class) @server = Puppet::Network::Server.new(:port => 31337) end it "should fail if the HTTP server class is unknown" do Puppet::Network::HTTP.stubs(:server_class_by_type).returns(nil) lambda { Puppet::Network::Server.new(:port => 31337) }.should raise_error(ArgumentError) end it "should allow registering REST handlers" do @server = Puppet::Network::Server.new(:port => 31337, :handlers => [ :foo, :bar, :baz]) lambda { @server.unregister(:foo, :bar, :baz) }.should_not raise_error end it "should not be listening after initialization" do Puppet::Network::Server.new(:port => 31337).should_not be_listening end it "should use the :main setting section" do Puppet.settings.expects(:use).with { |*args| args.include?(:main) } @server = Puppet::Network::Server.new(:port => 31337) end it "should use the :application setting section" do Puppet.settings.expects(:use).with { |*args| args.include?(:application) } @server = Puppet::Network::Server.new(:port => 31337) end end # We don't test the method, because it's too much of a Unix-y pain. it "should be able to daemonize" do @server.should respond_to(:daemonize) end describe "when being started" do before do @server.stubs(:listen) @server.stubs(:create_pidfile) end it "should listen" do @server.expects(:listen) @server.start end it "should create its PID file" do @server.expects(:create_pidfile) @server.start end end describe "when being stopped" do before do @server.stubs(:unlisten) @server.stubs(:remove_pidfile) end it "should unlisten" do @server.expects(:unlisten) @server.stop end it "should remove its PID file" do @server.expects(:remove_pidfile) @server.stop end end describe "when creating its pidfile" do it "should use an exclusive mutex" do Puppet.run_mode.expects(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @server.create_pidfile end it "should lock the pidfile using the Pidlock class" do pidfile = mock 'pidfile' Puppet.run_mode.expects(:name).returns "eh" Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns true @server.create_pidfile end it "should fail if it cannot lock" do pidfile = mock 'pidfile' Puppet.run_mode.stubs(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns false lambda { @server.create_pidfile }.should raise_error end end describe "when removing its pidfile" do it "should use an exclusive mutex" do Puppet.run_mode.expects(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @server.remove_pidfile end it "should do nothing if the pidfile is not present" do pidfile = mock 'pidfile', :unlock => false Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" @server.remove_pidfile end it "should unlock the pidfile using the Pidlock class" do pidfile = mock 'pidfile', :unlock => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" @server.remove_pidfile end end describe "when managing indirection registrations" do before do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') end it "should allow registering an indirection for client access by specifying its indirection name" do lambda { @server.register(:foo) }.should_not raise_error end it "should require that the indirection be valid" do Puppet::Indirector::Indirection.expects(:model).with(:foo).returns nil lambda { @server.register(:foo) }.should raise_error(ArgumentError) end it "should require at least one indirection name when registering indirections for client access" do lambda { @server.register }.should raise_error(ArgumentError) end it "should allow for numerous indirections to be registered at once for client access" do lambda { @server.register(:foo, :bar, :baz) }.should_not raise_error end it "should allow the use of indirection names to specify which indirections are to be no longer accessible to clients" do @server.register(:foo) lambda { @server.unregister(:foo) }.should_not raise_error end it "should leave other indirections accessible to clients when turning off indirections" do @server.register(:foo, :bar) @server.unregister(:foo) lambda { @server.unregister(:bar)}.should_not raise_error end it "should allow specifying numerous indirections which are to be no longer accessible to clients" do @server.register(:foo, :bar) lambda { @server.unregister(:foo, :bar) }.should_not raise_error end it "should not turn off any indirections if given unknown indirection names to turn off" do @server.register(:foo, :bar) lambda { @server.unregister(:foo, :bar, :baz) }.should raise_error(ArgumentError) lambda { @server.unregister(:foo, :bar) }.should_not raise_error end it "should not allow turning off unknown indirection names" do @server.register(:foo, :bar) lambda { @server.unregister(:baz) }.should raise_error(ArgumentError) end it "should disable client access immediately when turning off indirections" do @server.register(:foo, :bar) @server.unregister(:foo) lambda { @server.unregister(:foo) }.should raise_error(ArgumentError) end it "should allow turning off all indirections at once" do @server.register(:foo, :bar) @server.unregister [ :foo, :bar, :baz].each do |indirection| lambda { @server.unregister(indirection) }.should raise_error(ArgumentError) end end end it "should provide a means of determining whether it is listening" do @server.should respond_to(:listening?) end it "should provide a means of determining which HTTP server will be used to provide access to clients" do @server.server_type.should == :suparserver end it "should provide a means of determining the listening address" do @server.address.should == "127.0.0.1" end it "should provide a means of determining the listening port" do @server.port.should == 31337 end it "should allow for multiple configurations, each handling different indirections" do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') @server2 = Puppet::Network::Server.new(:port => 31337) @server.register(:foo, :bar) @server2.register(:foo, :xyzzy) @server.unregister(:foo, :bar) @server2.unregister(:foo, :xyzzy) lambda { @server.unregister(:xyzzy) }.should raise_error(ArgumentError) lambda { @server2.unregister(:bar) }.should raise_error(ArgumentError) end describe "when listening is off" do before do @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @server.stubs(:http_server).returns(@mock_http_server) end it "should indicate that it is not listening" do @server.should_not be_listening end it "should not allow listening to be turned off" do lambda { @server.unlisten }.should raise_error(RuntimeError) end it "should allow listening to be turned on" do lambda { @server.listen }.should_not raise_error end end describe "when listening is on" do before do @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @mock_http_server.stubs(:unlisten) @server.stubs(:http_server).returns(@mock_http_server) @server.listen end it "should indicate that it is listening" do @server.should be_listening end it "should not allow listening to be turned on" do lambda { @server.listen }.should raise_error(RuntimeError) end it "should allow listening to be turned off" do lambda { @server.unlisten }.should_not raise_error end end describe "when listening is being turned on" do before do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') @server = Puppet::Network::Server.new(:port => 31337, :handlers => [:node]) @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) end it "should fetch an instance of an HTTP server" do @server.stubs(:http_server_class).returns(@mock_http_server_class) @mock_http_server_class.expects(:new).returns(@mock_http_server) @server.listen end it "should cause the HTTP server to listen" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen) @server.listen end it "should pass the listening address to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:address] == '127.0.0.1' end @server.listen end it "should pass the listening port to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:port] == 31337 end @server.listen end it "should pass a list of REST handlers to the HTTP server" do @server.stubs(:http_server).returns(@mock_http_server) @mock_http_server.expects(:listen).with do |args| args[:handlers] == [ :node ] end @server.listen end end describe "when listening is being turned off" do before do @mock_http_server = mock('http server') @mock_http_server.stubs(:listen) @server.stubs(:http_server).returns(@mock_http_server) @server.listen end it "should cause the HTTP server to stop listening" do @mock_http_server.expects(:unlisten) @server.unlisten end it "should not allow for indirections to be turned off" do Puppet::Indirector::Indirection.stubs(:model).returns mock('indirection') @server.register(:foo) lambda { @server.unregister(:foo) }.should raise_error(RuntimeError) end end end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index 260a8ea8e..a4ad46467 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -1,452 +1,452 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'tmpdir' require 'puppet/node/environment' require 'puppet/util/execution' require 'puppet_spec/modules' describe Puppet::Node::Environment do let(:env) { Puppet::Node::Environment.new("testing") } include PuppetSpec::Files after do Puppet::Node::Environment.clear end it "should use the filetimeout for the ttl for the modulepath" do Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the module list" do Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) end it "should use the default environment if no name is provided while initializing an environment" do Puppet.settings.expects(:value).with(:environment).returns("one") Puppet::Node::Environment.new.name.should == :one end it "should treat environment instances as singletons" do Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) end it "should treat an environment specified as names or strings as equivalent" do Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) end it "should return its name when converted to a string" do Puppet::Node::Environment.new(:one).to_s.should == "one" end it "should just return any provided environment if an environment is provided as the name" do one = Puppet::Node::Environment.new(:one) Puppet::Node::Environment.new(one).should equal(one) end describe "when managing known resource types" do before do @collection = Puppet::Resource::TypeCollection.new(env) env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) Thread.current[:known_resource_types] = nil end it "should create a resource type collection if none exists" do Puppet::Resource::TypeCollection.expects(:new).with(env).returns @collection env.known_resource_types.should equal(@collection) end it "should reuse any existing resource type collection" do env.known_resource_types.should equal(env.known_resource_types) end it "should perform the initial import when creating a new collection" do env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) env.known_resource_types end it "should return the same collection even if stale if it's the same thread" do Puppet::Resource::TypeCollection.stubs(:new).returns @collection env.known_resource_types.stubs(:stale?).returns true env.known_resource_types.should equal(@collection) end it "should return the current thread associated collection if there is one" do Thread.current[:known_resource_types] = @collection env.known_resource_types.should equal(@collection) end it "should give to all threads using the same environment the same collection if the collection isn't stale" do original_thread_type_collection = Puppet::Resource::TypeCollection.new(env) Puppet::Resource::TypeCollection.expects(:new).with(env).returns original_thread_type_collection env.known_resource_types.should equal(original_thread_type_collection) original_thread_type_collection.expects(:require_reparse?).returns(false) Puppet::Resource::TypeCollection.stubs(:new).with(env).returns @collection t = Thread.new { env.known_resource_types.should equal(original_thread_type_collection) } t.join end it "should generate a new TypeCollection if the current one requires reparsing" do old_type_collection = env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true Thread.current[:known_resource_types] = nil new_type_collection = env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) end end it "should validate the modulepath directories" do real_file = tmpdir('moduledir') path = %W[/one /two #{real_file}].join(File::PATH_SEPARATOR) Puppet[:modulepath] = path env.modulepath.should == [real_file] end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do Puppet::Util.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do module_path = %w{/one /two}.join(File::PATH_SEPARATOR) env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} env.expects(:[]).with(:modulepath).returns module_path env.modulepath.should == %w{/l1 /l2 /one /two} end end describe "when validating modulepath or manifestdir directories" do before :each do @path_one = tmpdir("path_one") @path_two = tmpdir("path_one") sep = File::PATH_SEPARATOR Puppet[:modulepath] = "#{@path_one}#{sep}#{@path_two}" end it "should not return non-directories" do FileTest.expects(:directory?).with(@path_one).returns true FileTest.expects(:directory?).with(@path_two).returns false env.validate_dirs([@path_one, @path_two]).should == [@path_one] end it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true two = File.expand_path(File.join(Dir.getwd, "two")) env.validate_dirs([@path_one, 'two']).should == [@path_one, two] end end describe "when modeling a specific environment" do it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing end it "should provide an array-like accessor method for returning any environment-specific setting" do env.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") env["myvar"].should == "myval" end it "should be able to return an individual module that exists in its module path" do env.stubs(:modules).returns [Puppet::Module.new('one'), Puppet::Module.new('two'), Puppet::Module.new('three')] mod = env.module('one') mod.should be_a(Puppet::Module) mod.name.should == 'one' end it "should not return a module if the module doesn't exist" do env.stubs(:modules).returns [Puppet::Module.new('one'), Puppet::Module.new('two'), Puppet::Module.new('three')] env.module('four').should be_nil end it "should return nil if asked for a module that does not exist in its path" do modpath = tmpdir('modpath') env.modulepath = [modpath] env.module("one").should be_nil end describe "module data" do before do dir = tmpdir("deep_path") @first = File.join(dir, "first") @second = File.join(dir, "second") Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}" FileUtils.mkdir_p(@first) FileUtils.mkdir_p(@second) end describe "#modules_by_path" do it "should return an empty list if there are no modules" do env.modules_by_path.should == { @first => [], @second => [] } end it "should include modules even if they exist in multiple dirs in the modulepath" do modpath1 = File.join(@first, "foo") FileUtils.mkdir_p(modpath1) modpath2 = File.join(@second, "foo") FileUtils.mkdir_p(modpath2) env.modules_by_path.should == { @first => [Puppet::Module.new('foo', :environment => env, :path => modpath1)], @second => [Puppet::Module.new('foo', :environment => env, :path => modpath2)] } end it "should ignore modules with invalid names" do FileUtils.mkdir_p(File.join(@first, 'foo')) FileUtils.mkdir_p(File.join(@first, 'foo2')) FileUtils.mkdir_p(File.join(@first, 'foo-bar')) FileUtils.mkdir_p(File.join(@first, 'foo_bar')) FileUtils.mkdir_p(File.join(@first, 'foo=bar')) FileUtils.mkdir_p(File.join(@first, 'foo bar')) FileUtils.mkdir_p(File.join(@first, 'foo.bar')) FileUtils.mkdir_p(File.join(@first, '-foo')) FileUtils.mkdir_p(File.join(@first, 'foo-')) FileUtils.mkdir_p(File.join(@first, 'foo--bar')) env.modules_by_path[@first].collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar} end end describe "#module_requirements" do it "should return a list of what modules depend on other modules" do PuppetSpec::Modules.create( 'foo', @first, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => ">= 1.0.0" }] } ) PuppetSpec::Modules.create( 'bar', @second, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/foo', "version_requirement" => "<= 2.0.0" }] } ) PuppetSpec::Modules.create( 'baz', @first, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "3.0.0" }] } ) PuppetSpec::Modules.create( 'alpha', @first, :metadata => { :author => 'puppetlabs', :dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "~3.0.0" }] } ) env.module_requirements.should == { 'puppetlabs/alpha' => [], 'puppetlabs/foo' => [ { "name" => "puppetlabs/bar", "version" => "9.9.9", "version_requirement" => "<= 2.0.0" } ], 'puppetlabs/bar' => [ { "name" => "puppetlabs/alpha", "version" => "9.9.9", "version_requirement" => "~3.0.0" }, { "name" => "puppetlabs/baz", "version" => "9.9.9", "version_requirement" => "3.0.0" }, { "name" => "puppetlabs/foo", "version" => "9.9.9", "version_requirement" => ">= 1.0.0" } ], 'puppetlabs/baz' => [] } end end describe ".module_by_forge_name" do it "should find modules by forge_name" do mod = PuppetSpec::Modules.create( 'baz', @first, :metadata => {:author => 'puppetlabs'}, :environment => env ) env.module_by_forge_name('puppetlabs/baz').should == mod end it "should not find modules with same name by the wrong author" do mod = PuppetSpec::Modules.create( 'baz', @first, :metadata => {:author => 'sneakylabs'}, :environment => env ) env.module_by_forge_name('puppetlabs/baz').should == nil end it "should return nil when the module can't be found" do env.module_by_forge_name('ima/nothere').should be_nil end end describe ".modules" do it "should return an empty list if there are no modules" do env.modules.should == [] end it "should return a module named for every directory in each module path" do %w{foo bar}.each do |mod_name| FileUtils.mkdir_p(File.join(@first, mod_name)) end %w{bee baz}.each do |mod_name| FileUtils.mkdir_p(File.join(@second, mod_name)) end env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort end it "should remove duplicates" do FileUtils.mkdir_p(File.join(@first, 'foo')) FileUtils.mkdir_p(File.join(@second, 'foo')) env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should ignore modules with invalid names" do FileUtils.mkdir_p(File.join(@first, 'foo')) FileUtils.mkdir_p(File.join(@first, 'foo2')) FileUtils.mkdir_p(File.join(@first, 'foo-bar')) FileUtils.mkdir_p(File.join(@first, 'foo_bar')) FileUtils.mkdir_p(File.join(@first, 'foo=bar')) FileUtils.mkdir_p(File.join(@first, 'foo bar')) env.modules.collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar} end it "should create modules with the correct environment" do FileUtils.mkdir_p(File.join(@first, 'foo')) env.modules.each {|mod| mod.environment.should == env } end end end it "should cache the module list" do env.modulepath = %w{/a} Dir.expects(:entries).once.with("/a").returns %w{foo} env.modules env.modules end end describe Puppet::Node::Environment::Helper do before do @helper = Object.new @helper.extend(Puppet::Node::Environment::Helper) end it "should be able to set and retrieve the environment as a symbol" do @helper.environment = :foo @helper.environment.name.should == :foo end it "should accept an environment directly" do @helper.environment = Puppet::Node::Environment.new(:foo) @helper.environment.name.should == :foo end it "should accept an environment as a string" do @helper.environment = 'foo' @helper.environment.name.should == :foo end end describe "when performing initial import" do before do @parser = Puppet::Parser::Parser.new("test") Puppet::Parser::Parser.stubs(:new).returns @parser end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet.settings[:code] = "my code" @parser.expects(:string=).with "my code" @parser.expects(:parse) env.instance_eval { perform_initial_import } end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do filename = tmpfile('myfile') File.open(filename, 'w'){|f| } Puppet.settings[:manifest] = filename @parser.expects(:file=).with filename @parser.expects(:parse) env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do filename = tmpfile('myfile') Puppet.settings[:code] = "" Puppet.settings[:manifest] = filename @parser.expects(:file=).with(filename).once @parser.expects(:parse).once env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do File.stubs(:exist?).returns true env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) @parser.expects(:file=).once @parser.expects(:parse).raises ArgumentError lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do Puppet.settings[:ignoreimport] = true @parser.expects(:string=).never @parser.expects(:file=).never @parser.expects(:parse).never env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(env) lambda { env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) env.known_resource_types.require_reparse?.should be_true end end end diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb index b3a0f92dd..3404955e2 100755 --- a/spec/unit/node/facts_spec.rb +++ b/spec/unit/node/facts_spec.rb @@ -1,143 +1,143 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'matchers/json' require 'puppet/node/facts' describe Puppet::Node::Facts, "when indirecting" do before do @facts = Puppet::Node::Facts.new("me") end it "should be able to convert all fact values to strings" do @facts.values["one"] = 1 @facts.stringify @facts.values["one"].should == "1" end it "should add the node's certificate name as the 'clientcert' fact when adding local facts" do @facts.add_local_facts @facts.values["clientcert"].should == Puppet.settings[:certname] end it "should add the Puppet version as a 'clientversion' fact when adding local facts" do @facts.add_local_facts @facts.values["clientversion"].should == Puppet.version.to_s end it "should add the current environment as a fact if one is not set when adding local facts" do @facts.add_local_facts @facts.values["environment"].should == Puppet[:environment] end it "should not replace any existing environment fact when adding local facts" do @facts.values["environment"] = "foo" @facts.add_local_facts @facts.values["environment"].should == "foo" end it "should be able to downcase fact values" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:downcasefacts).returns true @facts.values["one"] = "Two" @facts.downcase_if_necessary @facts.values["one"].should == "two" end it "should only try to downcase strings" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:downcasefacts).returns true @facts.values["now"] = Time.now @facts.downcase_if_necessary @facts.values["now"].should be_instance_of(Time) end it "should not downcase facts if not configured to do so" do Puppet.settings.stubs(:value).returns "eh" Puppet.settings.expects(:value).with(:downcasefacts).returns false @facts.values["one"] = "Two" @facts.downcase_if_necessary @facts.values["one"].should == "Two" end describe "when indirecting" do before do @indirection = stub 'indirection', :request => mock('request'), :name => :facts @facts = Puppet::Node::Facts.new("me", "one" => "two") end it "should redirect to the specified fact store for storage" do Puppet::Node::Facts.stubs(:indirection).returns(@indirection) @indirection.expects(:save) Puppet::Node::Facts.indirection.save(@facts) end describe "when the Puppet application is 'master'" do it "should default to the 'yaml' terminus" do pending "Cannot test the behavior of defaults in defaults.rb" # Puppet::Node::Facts.indirection.terminus_class.should == :yaml end end describe "when the Puppet application is not 'master'" do it "should default to the 'facter' terminus" do pending "Cannot test the behavior of defaults in defaults.rb" # Puppet::Node::Facts.indirection.terminus_class.should == :facter end end end describe "when storing and retrieving" do it "should add metadata to the facts" do facts = Puppet::Node::Facts.new("me", "one" => "two", "three" => "four") facts.values[:_timestamp].should be_instance_of(Time) end describe "using pson" do before :each do @timestamp = Time.parse("Thu Oct 28 11:16:31 -0700 2010") @expiration = Time.parse("Thu Oct 28 11:21:31 -0700 2010") end it "should accept properly formatted pson" do pson = %Q({"name": "foo", "expiration": "#{@expiration}", "timestamp": "#{@timestamp}", "values": {"a": "1", "b": "2", "c": "3"}}) format = Puppet::Network::FormatHandler.format('pson') facts = format.intern(Puppet::Node::Facts,pson) facts.name.should == 'foo' facts.expiration.should == @expiration facts.values.should == {'a' => '1', 'b' => '2', 'c' => '3', :_timestamp => @timestamp} end it "should generate properly formatted pson" do Time.stubs(:now).returns(@timestamp) facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) facts.expiration = @expiration result = PSON.parse(facts.to_pson) result['name'].should == facts.name result['values'].should == facts.values.reject { |key, value| key.to_s =~ /_/ } result['timestamp'].should == facts.timestamp.to_s result['expiration'].should == facts.expiration.to_s end it "should not include nil values" do facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3}) pson = PSON.parse(facts.to_pson) pson.should_not be_include("expiration") end it "should be able to handle nil values" do pson = %Q({"name": "foo", "values": {"a": "1", "b": "2", "c": "3"}}) format = Puppet::Network::FormatHandler.format('pson') facts = format.intern(Puppet::Node::Facts,pson) facts.name.should == 'foo' facts.expiration.should be_nil end end end end diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb index 79eb03c79..e209161e8 100755 --- a/spec/unit/node_spec.rb +++ b/spec/unit/node_spec.rb @@ -1,268 +1,268 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'matchers/json' describe Puppet::Node do it "should register its document type as Node" do PSON.registered_document_types["Node"].should equal(Puppet::Node) end describe "when managing its environment" do it "should use any set environment" do Puppet::Node.new("foo", :environment => "bar").environment.name.should == :bar end it "should support providing an actual environment instance" do Puppet::Node.new("foo", :environment => Puppet::Node::Environment.new(:bar)).environment.name.should == :bar end it "should determine its environment from its parameters if no environment is set" do Puppet::Node.new("foo", :parameters => {"environment" => :bar}).environment.name.should == :bar end it "should use the default environment if no environment is provided" do Puppet::Node.new("foo").environment.name.should == Puppet::Node::Environment.new.name end it "should always return an environment instance rather than a string" do Puppet::Node.new("foo").environment.should be_instance_of(Puppet::Node::Environment) end it "should allow the environment to be set after initialization" do node = Puppet::Node.new("foo") node.environment = :bar node.environment.name.should == :bar end it "should allow its environment to be set by parameters after initialization" do node = Puppet::Node.new("foo") node.parameters["environment"] = :bar node.environment.name.should == :bar end end describe "when converting to json" do before do @node = Puppet::Node.new("mynode") end it "should provide its name" do @node.should set_json_attribute('name').to("mynode") end it "should produce a hash with the document_type set to 'Node'" do @node.should set_json_document_type_to("Node") end it "should include the classes if set" do @node.classes = %w{a b c} @node.should set_json_attribute("classes").to(%w{a b c}) end it "should not include the classes if there are none" do @node.should_not set_json_attribute('classes') end it "should include parameters if set" do @node.parameters = {"a" => "b", "c" => "d"} @node.should set_json_attribute('parameters').to({"a" => "b", "c" => "d"}) end it "should not include the parameters if there are none" do @node.should_not set_json_attribute('parameters') end it "should include the environment" do @node.environment = "production" @node.should set_json_attribute('environment').to('production') end end describe "when converting from json" do before do @node = Puppet::Node.new("mynode") @format = Puppet::Network::FormatHandler.format('pson') end def from_json(json) @format.intern(Puppet::Node, json) end it "should set its name" do Puppet::Node.should read_json_attribute('name').from(@node.to_pson).as("mynode") end it "should include the classes if set" do @node.classes = %w{a b c} Puppet::Node.should read_json_attribute('classes').from(@node.to_pson).as(%w{a b c}) end it "should include parameters if set" do @node.parameters = {"a" => "b", "c" => "d"} Puppet::Node.should read_json_attribute('parameters').from(@node.to_pson).as({"a" => "b", "c" => "d"}) end it "should include the environment" do @node.environment = "production" Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(Puppet::Node::Environment.new(:production)) end end end describe Puppet::Node, "when initializing" do before do @node = Puppet::Node.new("testnode") end it "should set the node name" do @node.name.should == "testnode" end it "should not allow nil node names" do proc { Puppet::Node.new(nil) }.should raise_error(ArgumentError) end it "should default to an empty parameter hash" do @node.parameters.should == {} end it "should default to an empty class array" do @node.classes.should == [] end it "should note its creation time" do @node.time.should be_instance_of(Time) end it "should accept parameters passed in during initialization" do params = {"a" => "b"} @node = Puppet::Node.new("testing", :parameters => params) @node.parameters.should == params end it "should accept classes passed in during initialization" do classes = %w{one two} @node = Puppet::Node.new("testing", :classes => classes) @node.classes.should == classes end it "should always return classes as an array" do @node = Puppet::Node.new("testing", :classes => "myclass") @node.classes.should == ["myclass"] end end describe Puppet::Node, "when merging facts" do before do @node = Puppet::Node.new("testnode") Puppet::Node::Facts.indirection.stubs(:find).with(@node.name, instance_of(Hash)).returns(Puppet::Node::Facts.new(@node.name, "one" => "c", "two" => "b")) end it "should fail intelligently if it cannot find facts" do Puppet::Node::Facts.indirection.expects(:find).with(@node.name, instance_of(Hash)).raises "foo" lambda { @node.fact_merge }.should raise_error(Puppet::Error) end it "should prefer parameters already set on the node over facts from the node" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["one"].should == "a" end it "should add passed parameters to the parameter list" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.fact_merge @node.parameters["two"].should == "b" end it "should accept arbitrary parameters to merge into its parameters" do @node = Puppet::Node.new("testnode", :parameters => {"one" => "a"}) @node.merge "two" => "three" @node.parameters["two"].should == "three" end it "should add the environment to the list of parameters" do Puppet.settings.stubs(:value).with(:environments).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "two" => "three" @node.parameters["environment"].should == "one" end it "should not set the environment if it is already set in the parameters" do Puppet.settings.stubs(:value).with(:environments).returns("one,two") Puppet.settings.stubs(:value).with(:environment).returns("one") @node = Puppet::Node.new("testnode", :environment => "one") @node.merge "environment" => "two" @node.parameters["environment"].should == "two" end end describe Puppet::Node, "when indirecting" do it "should default to the 'plain' node terminus" do Puppet::Node.indirection.reset_terminus_class Puppet::Node.indirection.terminus_class.should == :plain end end describe Puppet::Node, "when generating the list of names to search through" do before do @node = Puppet::Node.new("foo.domain.com", :parameters => {"hostname" => "yay", "domain" => "domain.com"}) end it "should return an array of names" do @node.names.should be_instance_of(Array) end describe "and the node name is fully qualified" do it "should contain an entry for each part of the node name" do @node.names.should be_include("foo.domain.com") @node.names.should be_include("foo.domain") @node.names.should be_include("foo") end end it "should include the node's fqdn" do @node.names.should be_include("yay.domain.com") end it "should combine and include the node's hostname and domain if no fqdn is available" do @node.names.should be_include("yay.domain.com") end it "should contain an entry for each name available by stripping a segment of the fqdn" do @node.parameters["fqdn"] = "foo.deep.sub.domain.com" @node.names.should be_include("foo.deep.sub.domain") @node.names.should be_include("foo.deep.sub") end describe "and :node_name is set to 'cert'" do before do Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false Puppet.settings.stubs(:value).with(:node_name).returns "cert" end it "should use the passed-in key as the first value" do @node.names[0].should == "foo.domain.com" end describe "and strict hostname checking is enabled" do it "should only use the passed-in key" do Puppet.settings.expects(:value).with(:strict_hostname_checking).returns true @node.names.should == ["foo.domain.com"] end end end describe "and :node_name is set to 'facter'" do before do Puppet.settings.stubs(:value).with(:strict_hostname_checking).returns false Puppet.settings.stubs(:value).with(:node_name).returns "facter" end it "should use the node's 'hostname' fact as the first value" do @node.names[0].should == "yay" end end end diff --git a/spec/unit/other/selinux_spec.rb b/spec/unit/other/selinux_spec.rb index f20951868..e234f824e 100755 --- a/spec/unit/other/selinux_spec.rb +++ b/spec/unit/other/selinux_spec.rb @@ -1,95 +1,95 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/type/selboolean' require 'puppet/type/selmodule' describe Puppet::Type.type(:file), " when manipulating file contexts" do include PuppetSpec::Files before :each do @file = Puppet::Type::File.new( :name => make_absolute("/tmp/foo"), :ensure => "file", :seluser => "user_u", :selrole => "role_r", :seltype => "type_t" ) end it "should use :seluser to get/set an SELinux user file context attribute" do @file.property(:seluser).should == "user_u" end it "should use :selrole to get/set an SELinux role file context attribute" do @file.property(:selrole).should == "role_r" end it "should use :seltype to get/set an SELinux user file context attribute" do @file.property(:seltype).should == "type_t" end end describe Puppet::Type.type(:selboolean), " when manipulating booleans" do before :each do provider_class = Puppet::Type::Selboolean.provider(Puppet::Type::Selboolean.providers[0]) Puppet::Type::Selboolean.stubs(:defaultprovider).returns provider_class @bool = Puppet::Type::Selboolean.new( :name => "foo", :value => "on", :persistent => true ) end it "should be able to access :name" do @bool[:name].should == "foo" end it "should be able to access :value" do @bool.property(:value).should == :on end it "should set :value to off" do @bool[:value] = :off @bool.property(:value).should == :off end it "should be able to access :persistent" do @bool[:persistent].should == :true end it "should set :persistent to false" do @bool[:persistent] = false @bool[:persistent].should == :false end end describe Puppet::Type.type(:selmodule), " when checking policy modules" do before :each do provider_class = Puppet::Type::Selmodule.provider(Puppet::Type::Selmodule.providers[0]) Puppet::Type::Selmodule.stubs(:defaultprovider).returns provider_class @module = Puppet::Type::Selmodule.new( :name => "foo", :selmoduledir => "/some/path", :selmodulepath => "/some/path/foo.pp", :syncversion => true) end it "should be able to access :name" do @module[:name].should == "foo" end it "should be able to access :selmoduledir" do @module[:selmoduledir].should == "/some/path" end it "should be able to access :selmodulepath" do @module[:selmodulepath].should == "/some/path/foo.pp" end it "should be able to access :syncversion" do @module.property(:syncversion).should == :true end it "should set the syncversion value to false" do @module[:syncversion] = :false @module.property(:syncversion).should == :false end end diff --git a/spec/unit/parameter/path_spec.rb b/spec/unit/parameter/path_spec.rb index d113a1581..c4c37bb68 100755 --- a/spec/unit/parameter/path_spec.rb +++ b/spec/unit/parameter/path_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parameter/path' [false, true].each do |arrays| describe "Puppet::Parameter::Path with arrays #{arrays}" do it_should_behave_like "all path parameters", :path, :array => arrays do # The new type allows us a test that is guaranteed to go direct to our # validation code, without passing through any "real type" overrides or # whatever on the way. Puppet::newtype(:test_puppet_parameter_path) do newparam(:path, :parent => Puppet::Parameter::Path, :arrays => arrays) do isnamevar accept_arrays arrays end end def instance(path) Puppet::Type.type(:test_puppet_parameter_path).new(:path => path) end end end end diff --git a/spec/unit/parameter/value_collection_spec.rb b/spec/unit/parameter/value_collection_spec.rb index af70160c1..611f6b9bd 100755 --- a/spec/unit/parameter/value_collection_spec.rb +++ b/spec/unit/parameter/value_collection_spec.rb @@ -1,166 +1,166 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parameter' describe Puppet::Parameter::ValueCollection do before do @collection = Puppet::Parameter::ValueCollection.new end it "should have a method for defining new values" do @collection.should respond_to(:newvalues) end it "should have a method for adding individual values" do @collection.should respond_to(:newvalue) end it "should be able to retrieve individual values" do value = @collection.newvalue(:foo) @collection.value(:foo).should equal(value) end it "should be able to add an individual value with a block" do @collection.newvalue(:foo) { raise "testing" } @collection.value(:foo).block.should be_instance_of(Proc) end it "should be able to add values that are empty strings" do lambda { @collection.newvalue('') }.should_not raise_error end it "should be able to add values that are empty strings" do value = @collection.newvalue('') @collection.match?('').should equal(value) end it "should set :call to :none when adding a value with no block" do value = @collection.newvalue(:foo) value.call.should == :none end describe "when adding a value with a block" do it "should set the method name to 'set_' plus the value name" do value = @collection.newvalue(:myval) { raise "testing" } value.method.should == "set_myval" end end it "should be able to add an individual value with options" do value = @collection.newvalue(:foo, :call => :bar) value.call.should == :bar end it "should have a method for validating a value" do @collection.should respond_to(:validate) end it "should have a method for munging a value" do @collection.should respond_to(:munge) end it "should be able to generate documentation when it has both values and regexes" do @collection.newvalues :foo, "bar", %r{test} @collection.doc.should be_instance_of(String) end it "should correctly generate documentation for values" do @collection.newvalues :foo @collection.doc.should be_include("Valid values are `foo`") end it "should correctly generate documentation for regexes" do @collection.newvalues %r{\w+} @collection.doc.should be_include("Values can match `/\\w+/`") end it "should be able to find the first matching value" do @collection.newvalues :foo, :bar @collection.match?("foo").should be_instance_of(Puppet::Parameter::Value) end it "should be able to match symbols" do @collection.newvalues :foo, :bar @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) end it "should be able to match symbols when a regex is provided" do @collection.newvalues %r{.} @collection.match?(:foo).should be_instance_of(Puppet::Parameter::Value) end it "should be able to match values using regexes" do @collection.newvalues %r{.} @collection.match?("foo").should_not be_nil end it "should prefer value matches to regex matches" do @collection.newvalues %r{.}, :foo @collection.match?("foo").name.should == :foo end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do @collection.validate("foo") end it "should fail if the value is not a defined value or alias and does not match a regex" do @collection.newvalues :foo lambda { @collection.validate("bar") }.should raise_error(ArgumentError) end it "should succeed if the value is one of the defined values" do @collection.newvalues :foo lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do @collection.newvalues :foo lambda { @collection.validate("foo") }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do @collection.newvalues "foo" lambda { @collection.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined aliases" do @collection.newvalues :foo @collection.aliasvalue :bar, :foo lambda { @collection.validate("bar") }.should_not raise_error(ArgumentError) end it "should succeed if the value matches one of the regexes" do @collection.newvalues %r{\d} lambda { @collection.validate("10") }.should_not raise_error(ArgumentError) end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do @collection.munge("foo").should == "foo" end it "should return return any matching defined values" do @collection.newvalues :foo, :bar @collection.munge("foo").should == :foo end it "should return any matching aliases" do @collection.newvalues :foo @collection.aliasvalue :bar, :foo @collection.munge("bar").should == :foo end it "should return the value if it matches a regex" do @collection.newvalues %r{\w} @collection.munge("bar").should == "bar" end it "should return the value if no other option is matched" do @collection.newvalues :foo @collection.munge("bar").should == "bar" end end end diff --git a/spec/unit/parameter/value_spec.rb b/spec/unit/parameter/value_spec.rb index f3414e4e0..74320e649 100755 --- a/spec/unit/parameter/value_spec.rb +++ b/spec/unit/parameter/value_spec.rb @@ -1,87 +1,87 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parameter' describe Puppet::Parameter::Value do it "should require a name" do lambda { Puppet::Parameter::Value.new }.should raise_error(ArgumentError) end it "should set its name" do Puppet::Parameter::Value.new(:foo).name.should == :foo end it "should support regexes as names" do lambda { Puppet::Parameter::Value.new(%r{foo}) }.should_not raise_error end it "should mark itself as a regex if its name is a regex" do Puppet::Parameter::Value.new(%r{foo}).should be_regex end it "should always convert its name to a symbol if it is not a regex" do Puppet::Parameter::Value.new("foo").name.should == :foo Puppet::Parameter::Value.new(true).name.should == :true end it "should support adding aliases" do Puppet::Parameter::Value.new("foo").should respond_to(:alias) end it "should be able to return its aliases" do value = Puppet::Parameter::Value.new("foo") value.alias("bar") value.alias("baz") value.aliases.should == [:bar, :baz] end [:block, :call, :method, :event, :required_features].each do |attr| it "should support a #{attr} attribute" do value = Puppet::Parameter::Value.new("foo") value.should respond_to(attr.to_s + "=") value.should respond_to(attr) end end it "should default to :instead for :call if a block is provided" do Puppet::Parameter::Value.new("foo").call.should == :instead end it "should always return events as symbols" do value = Puppet::Parameter::Value.new("foo") value.event = "foo_test" value.event.should == :foo_test end describe "when matching" do describe "a regex" do it "should return true if the regex matches the value" do Puppet::Parameter::Value.new(/\w/).should be_match("foo") end it "should return false if the regex does not match the value" do Puppet::Parameter::Value.new(/\d/).should_not be_match("foo") end end describe "a non-regex" do it "should return true if the value, converted to a symbol, matches the name" do Puppet::Parameter::Value.new("foo").should be_match("foo") Puppet::Parameter::Value.new(:foo).should be_match(:foo) Puppet::Parameter::Value.new(:foo).should be_match("foo") Puppet::Parameter::Value.new("foo").should be_match(:foo) end it "should return false if the value, converted to a symbol, does not match the name" do Puppet::Parameter::Value.new(:foo).should_not be_match(:bar) end it "should return true if any of its aliases match" do value = Puppet::Parameter::Value.new("foo") value.alias("bar") value.should be_match("bar") end end end end diff --git a/spec/unit/parameter_spec.rb b/spec/unit/parameter_spec.rb index d76a32bd7..40ab2c5e4 100755 --- a/spec/unit/parameter_spec.rb +++ b/spec/unit/parameter_spec.rb @@ -1,196 +1,196 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parameter' describe Puppet::Parameter do before do @class = Class.new(Puppet::Parameter) do @name = :foo end @class.initvars @resource = mock 'resource' @resource.stub_everything @parameter = @class.new :resource => @resource end it "should create a value collection" do @class = Class.new(Puppet::Parameter) @class.value_collection.should be_nil @class.initvars @class.value_collection.should be_instance_of(Puppet::Parameter::ValueCollection) end it "should return its name as a string when converted to a string" do @parameter.to_s.should == @parameter.name.to_s end [:line, :file, :version].each do |data| it "should return its resource's #{data} as its #{data}" do @resource.expects(data).returns "foo" @parameter.send(data).should == "foo" end end it "should return the resource's tags plus its name as its tags" do @resource.expects(:tags).returns %w{one two} @parameter.tags.should == %w{one two foo} end it "should provide source_descriptors" do @resource.expects(:line).returns 10 @resource.expects(:file).returns "file" @resource.expects(:tags).returns %w{one two} @parameter.source_descriptors.should == {:tags=>["one", "two", "foo"], :path=>"//foo", :file => "file", :line => 10} end describe "when returning the value" do it "should return nil if no value is set" do @parameter.value.should be_nil end it "should validate the value" do @parameter.expects(:validate).with("foo") @parameter.value = "foo" end it "should munge the value and use any result as the actual value" do @parameter.expects(:munge).with("foo").returns "bar" @parameter.value = "foo" @parameter.value.should == "bar" end it "should unmunge the value when accessing the actual value" do @parameter.class.unmunge do |value| value.to_sym end @parameter.value = "foo" @parameter.value.should == :foo end it "should return the actual value by default when unmunging" do @parameter.unmunge("bar").should == "bar" end it "should return any set value" do @parameter.value = "foo" @parameter.value.should == "foo" end end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do @parameter.validate("foo") end it "should catch abnormal failures thrown during validation" do @class.validate { |v| raise "This is broken" } lambda { @parameter.validate("eh") }.should raise_error(Puppet::DevError) end it "should fail if the value is not a defined value or alias and does not match a regex" do @class.newvalues :foo lambda { @parameter.validate("bar") }.should raise_error(Puppet::Error) end it "should succeed if the value is one of the defined values" do @class.newvalues :foo lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do @class.newvalues :foo lambda { @parameter.validate("foo") }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do @class.newvalues "foo" lambda { @parameter.validate(:foo) }.should_not raise_error(ArgumentError) end it "should succeed if the value is one of the defined aliases" do @class.newvalues :foo @class.aliasvalue :bar, :foo lambda { @parameter.validate("bar") }.should_not raise_error(ArgumentError) end it "should succeed if the value matches one of the regexes" do @class.newvalues %r{\d} lambda { @parameter.validate("10") }.should_not raise_error(ArgumentError) end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do @parameter.munge("foo").should == "foo" end it "should catch abnormal failures thrown during munging" do @class.munge { |v| raise "This is broken" } lambda { @parameter.munge("eh") }.should raise_error(Puppet::DevError) end it "should return return any matching defined values" do @class.newvalues :foo, :bar @parameter.munge("foo").should == :foo end it "should return any matching aliases" do @class.newvalues :foo @class.aliasvalue :bar, :foo @parameter.munge("bar").should == :foo end it "should return the value if it matches a regex" do @class.newvalues %r{\w} @parameter.munge("bar").should == "bar" end it "should return the value if no other option is matched" do @class.newvalues :foo @parameter.munge("bar").should == "bar" end end describe "when logging" do it "should use its resource's log level and the provided message" do @resource.expects(:[]).with(:loglevel).returns :notice @parameter.expects(:send_log).with(:notice, "mymessage") @parameter.log "mymessage" end end describe ".format_value_for_display" do it 'should format strings appropriately' do described_class.format_value_for_display('foo').should == "'foo'" end it 'should format numbers appropriately' do described_class.format_value_for_display(1).should == "'1'" end it 'should format symbols appropriately' do described_class.format_value_for_display(:bar).should == "'bar'" end it 'should format arrays appropriately' do described_class.format_value_for_display([1, 'foo', :bar]).should == "['1', 'foo', 'bar']" end it 'should format hashes appropriately' do described_class.format_value_for_display( {1 => 'foo', :bar => 2, 'baz' => :qux} ).should == "{'1' => 'foo', 'bar' => '2', 'baz' => 'qux'}" end it 'should format arrays with nested data appropriately' do described_class.format_value_for_display( [1, 'foo', :bar, [1, 2, 3], {1 => 2, 3 => 4}] ).should == "['1', 'foo', 'bar', ['1', '2', '3'], {'1' => '2', '3' => '4'}]" end it 'should format hashes with nested data appropriately' do described_class.format_value_for_display( {1 => 'foo', :bar => [2, 3, 4], 'baz' => {:qux => 1, :quux => 'two'}} ).should == "{'1' => 'foo', 'bar' => ['2', '3', '4'], 'baz' => {'quux' => 'two', 'qux' => '1'}}" end end end diff --git a/spec/unit/parser/ast/arithmetic_operator_spec.rb b/spec/unit/parser/ast/arithmetic_operator_spec.rb index 144ebd78c..57ec3007f 100755 --- a/spec/unit/parser/ast/arithmetic_operator_spec.rb +++ b/spec/unit/parser/ast/arithmetic_operator_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ArithmeticOperator do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new @one = stub 'lval', :safeevaluate => 1 @two = stub 'rval', :safeevaluate => 2 end it "should evaluate both branches" do lval = stub "lval" lval.expects(:safeevaluate).with(@scope).returns(1) rval = stub "rval" rval.expects(:safeevaluate).with(@scope).returns(2) operator = ast::ArithmeticOperator.new :rval => rval, :operator => "+", :lval => lval operator.evaluate(@scope) end it "should fail for an unknown operator" do lambda { operator = ast::ArithmeticOperator.new :lval => @one, :operator => "%", :rval => @two }.should raise_error end it "should call Puppet::Parser::Scope.number?" do Puppet::Parser::Scope.expects(:number?).with(1).returns(1) Puppet::Parser::Scope.expects(:number?).with(2).returns(2) ast::ArithmeticOperator.new(:lval => @one, :operator => "+", :rval => @two).evaluate(@scope) end %w{ + - * / << >>}.each do |op| it "should call ruby Numeric '#{op}'" do one = stub 'one' two = stub 'two' operator = ast::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two Puppet::Parser::Scope.stubs(:number?).with(1).returns(one) Puppet::Parser::Scope.stubs(:number?).with(2).returns(two) one.expects(:send).with(op,two) operator.evaluate(@scope) end end it "should work even with numbers embedded in strings" do two = stub 'two', :safeevaluate => "2" one = stub 'one', :safeevaluate => "1" operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one operator.evaluate(@scope).should == 3 end it "should work even with floats" do two = stub 'two', :safeevaluate => 2.53 one = stub 'one', :safeevaluate => 1.80 operator = ast::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one operator.evaluate(@scope).should == 4.33 end end diff --git a/spec/unit/parser/ast/astarray_spec.rb b/spec/unit/parser/ast/astarray_spec.rb index a3f56052a..b5ea19d14 100755 --- a/spec/unit/parser/ast/astarray_spec.rb +++ b/spec/unit/parser/ast/astarray_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ASTArray do before :each do @scope = Puppet::Parser::Scope.new end it "should have a [] accessor" do array = Puppet::Parser::AST::ASTArray.new :children => [] array.should respond_to(:[]) end it "should evaluate all its children" do item1 = stub "item1", :is_a? => true item2 = stub "item2", :is_a? => true item1.expects(:safeevaluate).with(@scope).returns(123) item2.expects(:safeevaluate).with(@scope).returns(246) operator = Puppet::Parser::AST::ASTArray.new :children => [item1,item2] operator.evaluate(@scope) end it "should not flatten children coming from children ASTArray" do item = Puppet::Parser::AST::String.new :value => 'foo' inner_array = Puppet::Parser::AST::ASTArray.new :children => [item, item] operator = Puppet::Parser::AST::ASTArray.new :children => [inner_array, inner_array] operator.evaluate(@scope).should == [['foo', 'foo'], ['foo', 'foo']] end it "should not flatten the results of children evaluation" do item = Puppet::Parser::AST::String.new :value => 'foo' item.stubs(:evaluate).returns(['foo']) operator = Puppet::Parser::AST::ASTArray.new :children => [item, item] operator.evaluate(@scope).should == [['foo'], ['foo']] end it "should discard nil results from children evaluation" do item1 = Puppet::Parser::AST::String.new :value => 'foo' item2 = Puppet::Parser::AST::String.new :value => 'foo' item2.stubs(:evaluate).returns(nil) operator = Puppet::Parser::AST::ASTArray.new :children => [item1, item2] operator.evaluate(@scope).should == ['foo'] end it "should return a valid string with to_s" do a = stub 'a', :is_a? => true, :to_s => "a" b = stub 'b', :is_a? => true, :to_s => "b" array = Puppet::Parser::AST::ASTArray.new :children => [a,b] array.to_s.should == "[a, b]" end end diff --git a/spec/unit/parser/ast/asthash_spec.rb b/spec/unit/parser/ast/asthash_spec.rb index ab1281f91..648d15fe9 100755 --- a/spec/unit/parser/ast/asthash_spec.rb +++ b/spec/unit/parser/ast/asthash_spec.rb @@ -1,96 +1,96 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ASTHash do before :each do @scope = Puppet::Parser::Scope.new end it "should have a merge functionality" do hash = Puppet::Parser::AST::ASTHash.new(:value => {}) hash.should respond_to(:merge) end it "should be able to merge 2 AST hashes" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) hash.merge(Puppet::Parser::AST::ASTHash.new(:value => {"c" => "d"})) hash.value.should == { "a" => "b", "c" => "d" } end it "should be able to merge with a ruby Hash" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b" }) hash.merge({"c" => "d"}) hash.value.should == { "a" => "b", "c" => "d" } end it "should evaluate each hash value" do key1 = stub "key1" value1 = stub "value1" key2 = stub "key2" value2 = stub "value2" value1.expects(:safeevaluate).with(@scope).returns("b") value2.expects(:safeevaluate).with(@scope).returns("d") operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) operator.evaluate(@scope) end it "should evaluate the hash keys if they are AST instances" do key1 = stub "key1" value1 = stub "value1", :safeevaluate => "one" key2 = stub "key2" value2 = stub "value2", :safeevaluate => "two" key1.expects(:safeevaluate).with(@scope).returns("1") key2.expects(:safeevaluate).with(@scope).returns("2") operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) hash = operator.evaluate(@scope) hash["1"].should == "one" hash["2"].should == "two" end it "should evaluate the hash keys if they are not AST instances" do key1 = "1" value1 = stub "value1", :safeevaluate => "one" key2 = "2" value2 = stub "value2", :safeevaluate => "two" operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) hash = operator.evaluate(@scope) hash["1"].should == "one" hash["2"].should == "two" end it "should return an evaluated hash" do key1 = stub "key1" value1 = stub "value1", :safeevaluate => "b" key2 = stub "key2" value2 = stub "value2", :safeevaluate => "d" operator = Puppet::Parser::AST::ASTHash.new(:value => { key1 => value1, key2 => value2}) operator.evaluate(@scope).should == { key1 => "b", key2 => "d" } end describe "when being initialized without arguments" do it "should evaluate to an empty hash" do hash = Puppet::Parser::AST::ASTHash.new({}) hash.evaluate(@scope).should == {} end it "should support merging" do hash = Puppet::Parser::AST::ASTHash.new({}) hash.merge({"a" => "b"}).should == {"a" => "b"} end end it "should return a valid string with to_s" do hash = Puppet::Parser::AST::ASTHash.new(:value => { "a" => "b", "c" => "d" }) ["{a => b, c => d}", "{c => d, a => b}"].should be_include hash.to_s end end diff --git a/spec/unit/parser/ast/boolean_operator_spec.rb b/spec/unit/parser/ast/boolean_operator_spec.rb index 287f466a7..14151e421 100755 --- a/spec/unit/parser/ast/boolean_operator_spec.rb +++ b/spec/unit/parser/ast/boolean_operator_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::BooleanOperator do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new @true_ast = ast::Boolean.new( :value => true) @false_ast = ast::Boolean.new( :value => false) end it "should evaluate left operand inconditionally" do lval = stub "lval" lval.expects(:safeevaluate).with(@scope).returns("true") rval = stub "rval", :safeevaluate => false rval.expects(:safeevaluate).never operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval operator.evaluate(@scope) end it "should evaluate right 'and' operand only if left operand is true" do lval = stub "lval", :safeevaluate => true rval = stub "rval", :safeevaluate => false rval.expects(:safeevaluate).with(@scope).returns(false) operator = ast::BooleanOperator.new :rval => rval, :operator => "and", :lval => lval operator.evaluate(@scope) end it "should evaluate right 'or' operand only if left operand is false" do lval = stub "lval", :safeevaluate => false rval = stub "rval", :safeevaluate => false rval.expects(:safeevaluate).with(@scope).returns(false) operator = ast::BooleanOperator.new :rval => rval, :operator => "or", :lval => lval operator.evaluate(@scope) end it "should return true for false OR true" do ast::BooleanOperator.new(:rval => @true_ast, :operator => "or", :lval => @false_ast).evaluate(@scope).should be_true end it "should return false for true AND false" do ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @false_ast ).evaluate(@scope).should be_false end it "should return true for true AND true" do ast::BooleanOperator.new(:rval => @true_ast, :operator => "and", :lval => @true_ast ).evaluate(@scope).should be_true end end diff --git a/spec/unit/parser/ast/casestatement_spec.rb b/spec/unit/parser/ast/casestatement_spec.rb index a76a9ae1f..b99479542 100755 --- a/spec/unit/parser/ast/casestatement_spec.rb +++ b/spec/unit/parser/ast/casestatement_spec.rb @@ -1,164 +1,164 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::CaseStatement do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do before :each do @test = stub 'test' @test.stubs(:safeevaluate).with(@scope).returns("value") @option1 = Puppet::Parser::AST::CaseOpt.new({}) @option1.stubs(:eachopt) @option1.stubs(:default?).returns false @option2 = Puppet::Parser::AST::CaseOpt.new({}) @option2.stubs(:eachopt) @option2.stubs(:default?).returns false @options = Puppet::Parser::AST::ASTArray.new(:children => [@option1, @option2]) @casestmt = Puppet::Parser::AST::CaseStatement.new :test => @test, :options => @options end it "should evaluate test" do @test.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should scan each option" do @casestmt.evaluate(@scope) end describe "when scanning options" do before :each do @opval1 = stub_everything 'opval1' @option1.stubs(:eachopt).yields(@opval1) @opval2 = stub_everything 'opval2' @option2.stubs(:eachopt).yields(@opval2) end it "should evaluate each sub-option" do @option1.expects(:eachopt) @option2.expects(:eachopt) @casestmt.evaluate(@scope) end it "should evaluate first matching option" do @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @option2.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return the first matching evaluated option" do @opval2.stubs(:evaluate_match).with { |*arg| arg[0] == "value" }.returns(true) @option2.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should evaluate the default option if none matched" do @option1.stubs(:default?).returns(true) @option1.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return the default evaluated option if none matched" do @option1.stubs(:default?).returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should return nil if nothing matched" do @casestmt.evaluate(@scope).should be_nil end it "should match and set scope ephemeral variables" do @opval1.expects(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope } @casestmt.evaluate(@scope) end it "should evaluate this regex option if it matches" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.expects(:safeevaluate).with(@scope) @casestmt.evaluate(@scope) end it "should return this evaluated regex option if it matches" do @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @casestmt.evaluate(@scope).should == :result end it "should unset scope ephemeral variables after option evaluation" do @scope.stubs(:ephemeral_level).returns(:level) @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).returns(:result) @scope.expects(:unset_ephemeral_var).with(:level) @casestmt.evaluate(@scope) end it "should not leak ephemeral variables even if evaluation fails" do @scope.stubs(:ephemeral_level).returns(:level) @opval1.stubs(:evaluate_match).with { |*arg| arg[0] == "value" and arg[1] == @scope }.returns(true) @option1.stubs(:safeevaluate).with(@scope).raises @scope.expects(:unset_ephemeral_var).with(:level) lambda { @casestmt.evaluate(@scope) }.should raise_error end end end it "should match if any of the provided options evaluate as true" do ast = nil AST = Puppet::Parser::AST tests = { "one" => %w{a b c}, "two" => %w{e f g} } options = tests.collect do |result, values| values = values.collect { |v| AST::Leaf.new :value => v } AST::CaseOpt.new( :value => AST::ASTArray.new(:children => values), :statements => AST::Leaf.new(:value => result) ) end options << AST::CaseOpt.new( :value => AST::Default.new(:value => "default"), :statements => AST::Leaf.new(:value => "default") ) ast = nil param = AST::Variable.new(:value => "testparam") ast = AST::CaseStatement.new(:test => param, :options => options) tests.each do |should, values| values.each do |value| @scope = Puppet::Parser::Scope.new @scope['testparam'] = value result = ast.evaluate(@scope) result.should == should end end end end diff --git a/spec/unit/parser/ast/collection_spec.rb b/spec/unit/parser/ast/collection_spec.rb index 78094e68d..f82d71bfc 100755 --- a/spec/unit/parser/ast/collection_spec.rb +++ b/spec/unit/parser/ast/collection_spec.rb @@ -1,70 +1,70 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Collection do before :each do @mytype = Puppet::Resource::Type.new(:definition, "mytype") @environment = Puppet::Node::Environment.new @environment.known_resource_types.add @mytype @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foonode", :environment => @environment)) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @overrides = stub_everything 'overrides' @overrides.stubs(:is_a?).with(Puppet::Parser::AST).returns(true) end it "should evaluate its query" do query = mock 'query' collection = Puppet::Parser::AST::Collection.new :query => query, :form => :virtual collection.type = 'mytype' query.expects(:safeevaluate).with(@scope) collection.evaluate(@scope) end it "should instantiate a Collector for this type" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "test" @test_type = Puppet::Resource::Type.new(:definition, "test") @environment.known_resource_types.add @test_type Puppet::Parser::Collector.expects(:new).with(@scope, "test", nil, nil, :virtual) collection.evaluate(@scope) end it "should tell the compiler about this collector" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype" Puppet::Parser::Collector.stubs(:new).returns("whatever") @compiler.expects(:add_collection).with("whatever") collection.evaluate(@scope) end it "should evaluate overriden paramaters" do collector = stub_everything 'collector' collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) @overrides.expects(:safeevaluate).with(@scope) collection.evaluate(@scope) end it "should tell the collector about overrides" do collector = mock 'collector' collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "mytype", :override => @overrides Puppet::Parser::Collector.stubs(:new).returns(collector) collector.expects(:add_override) collection.evaluate(@scope) end it "should fail when evaluating undefined resource types" do collection = Puppet::Parser::AST::Collection.new :form => :virtual, :type => "bogus" lambda { collection.evaluate(@scope) }.should raise_error "Resource type bogus doesn't exist" end end diff --git a/spec/unit/parser/ast/collexpr_spec.rb b/spec/unit/parser/ast/collexpr_spec.rb index 87829624d..9fb5c530a 100755 --- a/spec/unit/parser/ast/collexpr_spec.rb +++ b/spec/unit/parser/ast/collexpr_spec.rb @@ -1,118 +1,118 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::CollExpr do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating with two operands" do before :each do @test1 = mock 'test1' @test1.expects(:safeevaluate).with(@scope).returns("test1") @test2 = mock 'test2' @test2.expects(:safeevaluate).with(@scope).returns("test2") end it "should evaluate both" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") collexpr.evaluate(@scope) end it "should produce a data and code representation of the expression" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") result = collexpr.evaluate(@scope) result[0].should == ["test1", "==", "test2"] result[1].should be_an_instance_of(Proc) end it "should propagate expression type and form to child if expression themselves" do [@test1, @test2].each do |t| t.expects(:is_a?).returns(true) t.expects(:form).returns(false) t.expects(:type).returns(false) t.expects(:type=) t.expects(:form=) end collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper=>"==", :form => true, :type => true) result = collexpr.evaluate(@scope) end describe "and when evaluating the produced code" do before :each do @resource = mock 'resource' @resource.expects(:[]).with("test1").at_least(1).returns("test2") end it "should evaluate like the original expression for ==" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==") collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] == "test2") end it "should evaluate like the original expression for !=" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "!=") collexpr.evaluate(@scope)[1].call(@resource).should === (@resource["test1"] != "test2") end end it "should work if this is an exported collection containing parenthesis" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => "==", :parens => true, :form => :exported) match, code = collexpr.evaluate(@scope) match.should == ["test1", "==", "test2"] @logs.should be_empty # no warnings emitted end %w{and or}.each do |op| it "should parse / eval if this is an exported collection with #{op} operator" do collexpr = ast::CollExpr.new(:test1 => @test1, :test2 => @test2, :oper => op, :form => :exported) match, code = collexpr.evaluate(@scope) match.should == ["test1", op, "test2"] end end end describe "when evaluating with tags" do before :each do @tag = stub 'tag', :safeevaluate => 'tag' @value = stub 'value', :safeevaluate => 'value' @resource = stub 'resource' @resource.stubs(:tagged?).with("value").returns(true) end it "should produce a data representation of the expression" do collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper=>"==") result = collexpr.evaluate(@scope) result[0].should == ["tag", "==", "value"] end it "should inspect resource tags if the query term is on tags" do collexpr = ast::CollExpr.new(:test1 => @tag, :test2 => @value, :oper => "==") collexpr.evaluate(@scope)[1].call(@resource).should be_true end end [:exported,:virtual].each do |mode| it "should check for array member equality if resource parameter is an array for == in mode #{mode}" do array = mock 'array', :safeevaluate => "array" test1 = mock 'test1' test1.expects(:safeevaluate).with(@scope).returns("test1") resource = mock 'resource' resource.expects(:[]).with("array").at_least(1).returns(["test1","test2","test3"]) collexpr = ast::CollExpr.new(:test1 => array, :test2 => test1, :oper => "==", :form => mode) collexpr.evaluate(@scope)[1].call(resource).should be_true end end it "should raise an error for invalid operator" do lambda { collexpr = ast::CollExpr.new(:oper=>">") }.should raise_error end end diff --git a/spec/unit/parser/ast/comparison_operator_spec.rb b/spec/unit/parser/ast/comparison_operator_spec.rb index 96f4562e9..70bb49067 100755 --- a/spec/unit/parser/ast/comparison_operator_spec.rb +++ b/spec/unit/parser/ast/comparison_operator_spec.rb @@ -1,115 +1,115 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ComparisonOperator do before :each do @scope = Puppet::Parser::Scope.new @one = Puppet::Parser::AST::Leaf.new(:value => "1") @two = Puppet::Parser::AST::Leaf.new(:value => "2") @lval = Puppet::Parser::AST::Leaf.new(:value => "one") @rval = Puppet::Parser::AST::Leaf.new(:value => "two") end it "should evaluate both values" do @lval.expects(:safeevaluate).with(@scope) @rval.expects(:safeevaluate).with(@scope) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "==", :rval => @rval operator.evaluate(@scope) end it "should convert the arguments to numbers if they are numbers in string" do Puppet::Parser::Scope.expects(:number?).with("1").returns(1) Puppet::Parser::Scope.expects(:number?).with("2").returns(2) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "==", :rval => @two operator.evaluate(@scope) end %w{< > <= >=}.each do |oper| it "should use string comparison #{oper} if operands are strings" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => oper, :rval => @rval operator.evaluate(@scope).should == "one".send(oper,"two") end end describe "with string comparison" do it "should use matching" do @rval.expects(:evaluate_match).with("one", @scope) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "==", :rval => @rval operator.evaluate(@scope) end it "should return true for :undef to '' equality" do astundef = Puppet::Parser::AST::Leaf.new(:value => :undef) empty = Puppet::Parser::AST::Leaf.new(:value => '') operator = Puppet::Parser::AST::ComparisonOperator.new :lval => astundef, :operator => "==", :rval => empty operator.evaluate(@scope).should be_true end [true, false].each do |result| it "should return #{(result).inspect} with '==' when matching return #{result.inspect}" do @rval.expects(:evaluate_match).with("one", @scope).returns result operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "==", :rval => @rval operator.evaluate(@scope).should == result end it "should return #{(!result).inspect} with '!=' when matching return #{result.inspect}" do @rval.expects(:evaluate_match).with("one", @scope).returns result operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @lval, :operator => "!=", :rval => @rval operator.evaluate(@scope).should == !result end end end it "should fail with arguments of different types" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => ">", :rval => @rval lambda { operator.evaluate(@scope) }.should raise_error(ArgumentError) end it "should fail for an unknown operator" do lambda { operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => "or", :rval => @two }.should raise_error end %w{< > <= >= ==}.each do |oper| it "should return the result of using '#{oper}' to compare the left and right sides" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => oper, :rval => @two operator.evaluate(@scope).should == 1.send(oper,2) end end it "should return the result of using '!=' to compare the left and right sides" do operator = Puppet::Parser::AST::ComparisonOperator.new :lval => @one, :operator => '!=', :rval => @two operator.evaluate(@scope).should == true end it "should work for variables too" do one = Puppet::Parser::AST::Variable.new( :value => "one" ) two = Puppet::Parser::AST::Variable.new( :value => "two" ) one.expects(:safeevaluate).with(@scope).returns(1) two.expects(:safeevaluate).with(@scope).returns(2) operator = Puppet::Parser::AST::ComparisonOperator.new :lval => one, :operator => "<", :rval => two operator.evaluate(@scope).should == true end # see ticket #1759 %w{< > <= >=}.each do |oper| it "should return the correct result of using '#{oper}' to compare 10 and 9" do ten = Puppet::Parser::AST::Leaf.new(:value => "10") nine = Puppet::Parser::AST::Leaf.new(:value => "9") operator = Puppet::Parser::AST::ComparisonOperator.new :lval => ten, :operator => oper, :rval => nine operator.evaluate(@scope).should == 10.send(oper,9) end end end diff --git a/spec/unit/parser/ast/definition_spec.rb b/spec/unit/parser/ast/definition_spec.rb index 8b2f7f26f..cafa9dc34 100755 --- a/spec/unit/parser/ast/definition_spec.rb +++ b/spec/unit/parser/ast/definition_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Definition do it "should make its context available through an accessor" do definition = Puppet::Parser::AST::Definition.new('foo', :line => 5) definition.context.should == {:line => 5} end describe "when instantiated" do it "should create a definition with the proper type, name, context, and module name" do definition = Puppet::Parser::AST::Definition.new('foo', :line => 5) instantiated_definitions = definition.instantiate('modname') instantiated_definitions.length.should == 1 instantiated_definitions[0].type.should == :definition instantiated_definitions[0].name.should == 'foo' instantiated_definitions[0].line.should == 5 instantiated_definitions[0].module_name.should == 'modname' end end end diff --git a/spec/unit/parser/ast/function_spec.rb b/spec/unit/parser/ast/function_spec.rb index 4ad176dab..cb9019311 100755 --- a/spec/unit/parser/ast/function_spec.rb +++ b/spec/unit/parser/ast/function_spec.rb @@ -1,92 +1,92 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Function do before :each do @scope = mock 'scope' end describe "when initializing" do it "should not fail if the function doesn't exist" do Puppet::Parser::Functions.stubs(:function).returns(false) lambda{ Puppet::Parser::AST::Function.new :name => "dontexist" }.should_not raise_error(Puppet::ParseError) end end it "should return its representation with to_s" do args = stub 'args', :is_a? => true, :to_s => "[a, b]" Puppet::Parser::AST::Function.new(:name => "func", :arguments => args).to_s.should == "func(a, b)" end describe "when evaluating" do it "should fail if the function doesn't exist" do Puppet::Parser::Functions.stubs(:function).returns(false) func = Puppet::Parser::AST::Function.new :name => "dontexist" lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError) end it "should fail if the function is a statement used as rvalue" do Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(false) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :rvalue lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError, "Function 'exist' does not return a value") end it "should fail if the function is an rvalue used as statement" do Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) Puppet::Parser::Functions.stubs(:rvalue?).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement lambda{ func.evaluate(@scope) }.should raise_error(Puppet::ParseError,"Function 'exist' must be the value of a statement") end it "should evaluate its arguments" do argument = stub 'arg' Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.stubs(:function_exist) argument.expects(:safeevaluate).with(@scope).returns(["argument"]) func.evaluate(@scope) end it "should call the underlying ruby function" do argument = stub 'arg', :safeevaluate => ["nothing"] Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.expects(:function_exist).with(["nothing"]) func.evaluate(@scope) end it "should convert :undef to '' in arguments" do argument = stub 'arg', :safeevaluate => ["foo", :undef, "bar"] Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.expects(:function_exist).with(["foo", "", "bar"]) func.evaluate(@scope) end it "should return the ruby function return for rvalue functions" do argument = stub 'arg', :safeevaluate => ["nothing"] Puppet::Parser::Functions.stubs(:function).with("exist").returns(true) func = Puppet::Parser::AST::Function.new :name => "exist", :ftype => :statement, :arguments => argument @scope.stubs(:function_exist).with(["nothing"]).returns("returning") func.evaluate(@scope).should == "returning" end end end diff --git a/spec/unit/parser/ast/hostclass_spec.rb b/spec/unit/parser/ast/hostclass_spec.rb index ee154fac8..8d4162850 100755 --- a/spec/unit/parser/ast/hostclass_spec.rb +++ b/spec/unit/parser/ast/hostclass_spec.rb @@ -1,72 +1,72 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Hostclass do def ast Puppet::Parser::AST end def newarray(*elems) ast::ASTArray.new({}).push(*elems) end it "should make its name and context available through accessors" do hostclass = ast::Hostclass.new('foo', :line => 5) hostclass.name.should == 'foo' hostclass.context.should == {:line => 5} end it "should make its code available through an accessor" do code = newarray hostclass = ast::Hostclass.new('foo', :code => code) hostclass.code.should be_equal(code) end describe "when instantiated" do it "should create a class with the proper type, code, name, context, and module name" do code = newarray hostclass = ast::Hostclass.new('foo', :code => code, :line => 5) instantiated_class = hostclass.instantiate('modname')[0] instantiated_class.type.should == :hostclass instantiated_class.name.should == 'foo' instantiated_class.code.should be_equal(code) instantiated_class.line.should == 5 instantiated_class.module_name.should == 'modname' end it "should instantiate all nested classes, defines, and nodes with the same module name." do nested_objects = newarray(ast::Hostclass.new('foo::child1'), ast::Definition.new('foo::child2'), ast::Definition.new('child3')) hostclass = ast::Hostclass.new('foo', :code => nested_objects) instantiated_classes = hostclass.instantiate('modname') instantiated_classes.length.should == 4 instantiated_classes[0].name.should == 'foo' instantiated_classes[1].name.should == 'foo::child1' instantiated_classes[2].name.should == 'foo::child2' instantiated_classes[3].name.should == 'child3' instantiated_classes.each { |cls| cls.module_name.should == 'modname' } end it "should handle a nested class that contains its own nested classes." do foo_bar_baz = ast::Hostclass.new('foo::bar::baz') foo_bar = ast::Hostclass.new('foo::bar', :code => newarray(foo_bar_baz)) foo = ast::Hostclass.new('foo', :code => newarray(foo_bar)) instantiated_classes = foo.instantiate('') instantiated_classes.length.should == 3 instantiated_classes[0].name.should == 'foo' instantiated_classes[1].name.should == 'foo::bar' instantiated_classes[2].name.should == 'foo::bar::baz' end it "should skip nested elements that are not classes, definitions, or nodes." do func = ast::Function.new(:name => 'biz', :arguments => newarray(ast::Name.new(:value => 'baz'))) foo = ast::Hostclass.new('foo', :code => newarray(func)) instantiated_classes = foo.instantiate('') instantiated_classes.length.should == 1 instantiated_classes[0].should be_a(Puppet::Resource::Type) instantiated_classes[0].name.should == 'foo' end end end diff --git a/spec/unit/parser/ast/ifstatement_spec.rb b/spec/unit/parser/ast/ifstatement_spec.rb index 4b6e0b8e5..a03c8209e 100755 --- a/spec/unit/parser/ast/ifstatement_spec.rb +++ b/spec/unit/parser/ast/ifstatement_spec.rb @@ -1,75 +1,75 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::IfStatement do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do before :each do @test = stub 'test' @test.stubs(:safeevaluate).with(@scope) @stmt = stub 'stmt' @stmt.stubs(:safeevaluate).with(@scope) @else = stub 'else' @else.stubs(:safeevaluate).with(@scope) @ifstmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt @ifelsestmt = Puppet::Parser::AST::IfStatement.new :test => @test, :statements => @stmt, :else => @else end it "should evaluate test" do Puppet::Parser::Scope.stubs(:true?).returns(false) @test.expects(:safeevaluate).with(@scope) @ifstmt.evaluate(@scope) end it "should evaluate if statements if test is true" do Puppet::Parser::Scope.stubs(:true?).returns(true) @stmt.expects(:safeevaluate).with(@scope) @ifstmt.evaluate(@scope) end it "should not evaluate if statements if test is false" do Puppet::Parser::Scope.stubs(:true?).returns(false) @stmt.expects(:safeevaluate).with(@scope).never @ifstmt.evaluate(@scope) end it "should evaluate the else branch if test is false" do Puppet::Parser::Scope.stubs(:true?).returns(false) @else.expects(:safeevaluate).with(@scope) @ifelsestmt.evaluate(@scope) end it "should not evaluate the else branch if test is true" do Puppet::Parser::Scope.stubs(:true?).returns(true) @else.expects(:safeevaluate).with(@scope).never @ifelsestmt.evaluate(@scope) end it "should reset ephemeral statements after evaluation" do @scope.expects(:ephemeral_level).returns(:level) Puppet::Parser::Scope.stubs(:true?).returns(true) @stmt.expects(:safeevaluate).with(@scope) @scope.expects(:unset_ephemeral_var).with(:level) @ifstmt.evaluate(@scope) end end end diff --git a/spec/unit/parser/ast/in_operator_spec.rb b/spec/unit/parser/ast/in_operator_spec.rb index b6b6fbb89..f158fd381 100755 --- a/spec/unit/parser/ast/in_operator_spec.rb +++ b/spec/unit/parser/ast/in_operator_spec.rb @@ -1,59 +1,59 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/ast/in_operator' describe Puppet::Parser::AST::InOperator do before :each do @scope = Puppet::Parser::Scope.new @lval = stub 'lval' @lval.stubs(:safeevaluate).with(@scope).returns("left") @rval = stub 'rval' @rval.stubs(:safeevaluate).with(@scope).returns("right") @operator = Puppet::Parser::AST::InOperator.new :lval => @lval, :rval => @rval end it "should evaluate the left operand" do @lval.expects(:safeevaluate).with(@scope).returns("string") @operator.evaluate(@scope) end it "should evaluate the right operand" do @rval.expects(:safeevaluate).with(@scope).returns("string") @operator.evaluate(@scope) end it "should raise an argument error if lval is not a string" do @lval.expects(:safeevaluate).with(@scope).returns([12,13]) lambda { @operator.evaluate(@scope) }.should raise_error end it "should raise an argument error if rval doesn't support the include? method" do @rval.expects(:safeevaluate).with(@scope).returns(stub('value')) lambda { @operator.evaluate(@scope) }.should raise_error end it "should not raise an argument error if rval supports the include? method" do @rval.expects(:safeevaluate).with(@scope).returns(stub('value', :include? => true)) lambda { @operator.evaluate(@scope) }.should_not raise_error end it "should return rval.include?(lval)" do lval = stub 'lvalue', :is_a? => true @lval.stubs(:safeevaluate).with(@scope).returns(lval) rval = stub 'rvalue' @rval.stubs(:safeevaluate).with(@scope).returns(rval) rval.expects(:include?).with(lval).returns(:result) @operator.evaluate(@scope).should == :result end end diff --git a/spec/unit/parser/ast/match_operator_spec.rb b/spec/unit/parser/ast/match_operator_spec.rb index 0f9235aeb..33e3b9516 100755 --- a/spec/unit/parser/ast/match_operator_spec.rb +++ b/spec/unit/parser/ast/match_operator_spec.rb @@ -1,49 +1,49 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::MatchOperator do before :each do @scope = Puppet::Parser::Scope.new @lval = stub 'lval' @lval.stubs(:safeevaluate).with(@scope).returns("this is a string") @rval = stub 'rval' @rval.stubs(:evaluate_match) @operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => "=~" end it "should evaluate the left operand" do @lval.expects(:safeevaluate).with(@scope) @operator.evaluate(@scope) end it "should fail for an unknown operator" do lambda { operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :operator => "unknown", :rval => @rval }.should raise_error end it "should evaluate_match the left operand" do @rval.expects(:evaluate_match).with("this is a string", @scope).returns(:match) @operator.evaluate(@scope) end { "=~" => true, "!~" => false }.each do |op, res| it "should return #{res} if the regexp matches with #{op}" do match = stub 'match' @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(match) operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op operator.evaluate(@scope).should == res end it "should return #{!res} if the regexp doesn't match" do @rval.stubs(:evaluate_match).with("this is a string", @scope).returns(nil) operator = Puppet::Parser::AST::MatchOperator.new :lval => @lval, :rval => @rval, :operator => op operator.evaluate(@scope).should == !res end end end diff --git a/spec/unit/parser/ast/minus_spec.rb b/spec/unit/parser/ast/minus_spec.rb index 8ebd14e80..9b2f7f74e 100755 --- a/spec/unit/parser/ast/minus_spec.rb +++ b/spec/unit/parser/ast/minus_spec.rb @@ -1,35 +1,35 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Minus do before :each do @scope = Puppet::Parser::Scope.new end it "should evaluate its argument" do value = stub "value" value.expects(:safeevaluate).with(@scope).returns(123) operator = Puppet::Parser::AST::Minus.new :value => value operator.evaluate(@scope) end it "should fail if argument is not a string or integer" do array_ast = stub 'array_ast', :safeevaluate => [2] operator = Puppet::Parser::AST::Minus.new :value => array_ast lambda { operator.evaluate(@scope) }.should raise_error end it "should work with integer as string" do string = stub 'string', :safeevaluate => "123" operator = Puppet::Parser::AST::Minus.new :value => string operator.evaluate(@scope).should == -123 end it "should work with integers" do int = stub 'int', :safeevaluate => 123 operator = Puppet::Parser::AST::Minus.new :value => int operator.evaluate(@scope).should == -123 end end diff --git a/spec/unit/parser/ast/node_spec.rb b/spec/unit/parser/ast/node_spec.rb index c2e187184..eb384d902 100755 --- a/spec/unit/parser/ast/node_spec.rb +++ b/spec/unit/parser/ast/node_spec.rb @@ -1,30 +1,30 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Node do describe "when instantiated" do it "should make its names and context available through accessors" do node = Puppet::Parser::AST::Node.new(['foo', 'bar'], :line => 5) node.names.should == ['foo', 'bar'] node.context.should == {:line => 5} end it "should create a node with the proper type, name, context, and module name" do node = Puppet::Parser::AST::Node.new(['foo'], :line => 5) instantiated_nodes = node.instantiate('modname') instantiated_nodes.length.should == 1 instantiated_nodes[0].type.should == :node instantiated_nodes[0].name.should == 'foo' instantiated_nodes[0].line.should == 5 instantiated_nodes[0].module_name.should == 'modname' end it "should handle multiple names" do node = Puppet::Parser::AST::Node.new(['foo', 'bar']) instantiated_nodes = node.instantiate('modname') instantiated_nodes.length.should == 2 instantiated_nodes[0].name.should == 'foo' instantiated_nodes[1].name.should == 'bar' end end end diff --git a/spec/unit/parser/ast/nop_spec.rb b/spec/unit/parser/ast/nop_spec.rb index 81302fa55..e50b5ff5a 100755 --- a/spec/unit/parser/ast/nop_spec.rb +++ b/spec/unit/parser/ast/nop_spec.rb @@ -1,19 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Nop do before do @scope = mock 'scope' end it "should do nothing on evaluation" do Puppet::Parser::AST.expects(:safeevaluate).never Puppet::Parser::AST::Nop.new({}).evaluate(@scope) end it "should not return anything" do Puppet::Parser::AST::Nop.new({}).evaluate(@scope).should be_nil end end diff --git a/spec/unit/parser/ast/not_spec.rb b/spec/unit/parser/ast/not_spec.rb index 6569af699..7f26d3bb2 100755 --- a/spec/unit/parser/ast/not_spec.rb +++ b/spec/unit/parser/ast/not_spec.rb @@ -1,29 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Not do before :each do @scope = Puppet::Parser::Scope.new @true_ast = Puppet::Parser::AST::Boolean.new( :value => true) @false_ast = Puppet::Parser::AST::Boolean.new( :value => false) end it "should evaluate its child expression" do val = stub "val" val.expects(:safeevaluate).with(@scope) operator = Puppet::Parser::AST::Not.new :value => val operator.evaluate(@scope) end it "should return true for ! false" do operator = Puppet::Parser::AST::Not.new :value => @false_ast operator.evaluate(@scope).should == true end it "should return false for ! true" do operator = Puppet::Parser::AST::Not.new :value => @true_ast operator.evaluate(@scope).should == false end end diff --git a/spec/unit/parser/ast/relationship_spec.rb b/spec/unit/parser/ast/relationship_spec.rb index 441ac45b1..daa5be79f 100755 --- a/spec/unit/parser/ast/relationship_spec.rb +++ b/spec/unit/parser/ast/relationship_spec.rb @@ -1,87 +1,87 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Relationship do before do @class = Puppet::Parser::AST::Relationship end it "should set its 'left' and 'right' arguments accordingly" do dep = @class.new(:left, :right, '->') dep.left.should == :left dep.right.should == :right end it "should set its arrow to whatever arrow is passed" do @class.new(:left, :right, '->').arrow.should == '->' end it "should set its type to :relationship if the relationship type is '<-'" do @class.new(:left, :right, '<-').type.should == :relationship end it "should set its type to :relationship if the relationship type is '->'" do @class.new(:left, :right, '->').type.should == :relationship end it "should set its type to :subscription if the relationship type is '~>'" do @class.new(:left, :right, '~>').type.should == :subscription end it "should set its type to :subscription if the relationship type is '<~'" do @class.new(:left, :right, '<~').type.should == :subscription end it "should set its line and file if provided" do dep = @class.new(:left, :right, '->', :line => 50, :file => "/foo") dep.line.should == 50 dep.file.should == "/foo" end describe "when evaluating" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should create a relationship with the evaluated source and target and add it to the scope" do source = stub 'source', :safeevaluate => :left target = stub 'target', :safeevaluate => :right @class.new(source, target, '->').evaluate(@scope) @compiler.relationships[0].source.should == :left @compiler.relationships[0].target.should == :right end describe "a chained relationship" do before do @left = stub 'left', :safeevaluate => :left @middle = stub 'middle', :safeevaluate => :middle @right = stub 'right', :safeevaluate => :right @first = @class.new(@left, @middle, '->') @second = @class.new(@first, @right, '->') end it "should evaluate the relationship to the left" do @first.expects(:evaluate).with(@scope).returns Puppet::Parser::Relationship.new(:left, :right, :relationship) @second.evaluate(@scope) end it "should use the right side of the left relationship as its source" do @second.evaluate(@scope) @compiler.relationships[0].source.should == :left @compiler.relationships[0].target.should == :middle @compiler.relationships[1].source.should == :middle @compiler.relationships[1].target.should == :right end it "should only evaluate a given AST node once" do @left.expects(:safeevaluate).once.returns :left @middle.expects(:safeevaluate).once.returns :middle @right.expects(:safeevaluate).once.returns :right @second.evaluate(@scope) end end end end diff --git a/spec/unit/parser/ast/resource_defaults_spec.rb b/spec/unit/parser/ast/resource_defaults_spec.rb index 8164828e1..a387f0c85 100755 --- a/spec/unit/parser/ast/resource_defaults_spec.rb +++ b/spec/unit/parser/ast/resource_defaults_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ResourceDefaults do ast = Puppet::Parser::AST before :each do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @params = Puppet::Parser::AST::ASTArray.new({}) @compiler.stubs(:add_override) end it "should add defaults when evaluated" do default = Puppet::Parser::AST::ResourceDefaults.new :type => "file", :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) default.evaluate @scope @scope.lookupdefaults("file").should_not be_nil end end diff --git a/spec/unit/parser/ast/resource_override_spec.rb b/spec/unit/parser/ast/resource_override_spec.rb index 458d9a4bf..16ff9beb5 100755 --- a/spec/unit/parser/ast/resource_override_spec.rb +++ b/spec/unit/parser/ast/resource_override_spec.rb @@ -1,50 +1,50 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ResourceOverride do ast = Puppet::Parser::AST before :each do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @params = ast::ASTArray.new({}) @compiler.stubs(:add_override) end it "should evaluate the overriden object" do klass = stub 'klass', :title => "title", :type => "type" object = mock 'object' object.expects(:safeevaluate).with(@scope).returns(klass) ast::ResourceOverride.new(:object => object, :parameters => @params ).evaluate(@scope) end it "should tell the compiler to override the resource with our own" do @compiler.expects(:add_override) klass = stub 'klass', :title => "title", :type => "one" object = mock 'object', :safeevaluate => klass ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) end it "should return the overriden resource directly when called with one item" do klass = stub 'klass', :title => "title", :type => "one" object = mock 'object', :safeevaluate => klass override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) override.should be_an_instance_of(Puppet::Parser::Resource) override.title.should == "title" override.type.should == "One" end it "should return an array of overriden resources when called with an array of titles" do klass1 = stub 'klass1', :title => "title1", :type => "one" klass2 = stub 'klass2', :title => "title2", :type => "one" object = mock 'object', :safeevaluate => [klass1,klass2] override = ast::ResourceOverride.new(:object => object , :parameters => @params).evaluate(@scope) override.should have(2).elements override.each {|o| o.should be_an_instance_of(Puppet::Parser::Resource) } end end diff --git a/spec/unit/parser/ast/resource_reference_spec.rb b/spec/unit/parser/ast/resource_reference_spec.rb index 4e069cca0..d7f4ba503 100755 --- a/spec/unit/parser/ast/resource_reference_spec.rb +++ b/spec/unit/parser/ast/resource_reference_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::ResourceReference do ast = Puppet::Parser::AST before :each do @scope = Puppet::Parser::Scope.new end def ast_name(value) Puppet::Parser::AST::Name.new(:value => value) end def newref(type, title) title_array = Puppet::Parser::AST::ASTArray.new(:children => [title]) ref = Puppet::Parser::AST::ResourceReference.new(:type => type, :title => title_array) end it "should correctly produce reference strings" do newref("File", ast_name("/tmp/yay")).evaluate(@scope).to_s.should == "File[/tmp/yay]" end it "should produce a single resource when the title evaluates to a string" do newref("File", ast_name("/tmp/yay")).evaluate(@scope).should == Puppet::Resource.new("file", "/tmp/yay") end it "should return an array of resources if given an array of titles" do titles = Puppet::Parser::AST::ASTArray.new(:children => [ast_name("title1"), ast_name("title2")]) ref = ast::ResourceReference.new( :title => titles, :type => "File" ) ref.evaluate(@scope).should == [ Puppet::Resource.new("file", "title1"), Puppet::Resource.new("file", "title2") ] end it "should return an array of resources if given a variable containing an array of titles" do @scope["my_files"] = ["foo", "bar"] titles = Puppet::Parser::AST::Variable.new(:value => "my_files") ref = newref('File', titles) ref.evaluate(@scope).should == [ Puppet::Resource.new("file", "foo"), Puppet::Resource.new("file", "bar") ] end it "should return a correct representation when converting to string" do type = stub 'type', :is_a? => true, :to_s => "file" title = stub 'title', :is_a? => true, :to_s => "[/tmp/a, /tmp/b]" ast::ResourceReference.new( :type => type, :title => title ).to_s.should == "File[/tmp/a, /tmp/b]" end end diff --git a/spec/unit/parser/ast/resource_spec.rb b/spec/unit/parser/ast/resource_spec.rb index e4fb946c4..f26e85eaa 100755 --- a/spec/unit/parser/ast/resource_spec.rb +++ b/spec/unit/parser/ast/resource_spec.rb @@ -1,183 +1,183 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Resource do ast = Puppet::Parser::AST describe "for builtin types" do before :each do @title = Puppet::Parser::AST::String.new(:value => "mytitle") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @scope.stubs(:resource).returns(stub_everything) @instance = ast::ResourceInstance.new(:title => @title, :parameters => ast::ASTArray.new(:children => [])) @resource = ast::Resource.new(:type => "file", :instances => ast::ASTArray.new(:children => [@instance])) @resource.stubs(:qualified_type).returns("Resource") end it "should evaluate all its parameters" do param = stub 'param' param.expects(:safeevaluate).with(@scope).returns Puppet::Parser::Resource::Param.new(:name => "myparam", :value => "myvalue", :source => stub("source")) @instance.stubs(:parameters).returns [param] @resource.evaluate(@scope) end it "should evaluate its title" do @resource.evaluate(@scope)[0].title.should == "mytitle" end it "should flatten the titles array" do titles = [] %w{one two}.each do |title| titles << Puppet::Parser::AST::String.new(:value => title) end array = Puppet::Parser::AST::ASTArray.new(:children => titles) @instance.title = array result = @resource.evaluate(@scope).collect { |r| r.title } result.should be_include("one") result.should be_include("two") end it "should create and return one resource objects per title" do titles = [] %w{one two}.each do |title| titles << Puppet::Parser::AST::String.new(:value => title) end array = Puppet::Parser::AST::ASTArray.new(:children => titles) @instance.title = array result = @resource.evaluate(@scope).collect { |r| r.title } result.should be_include("one") result.should be_include("two") end it "should implicitly iterate over instances" do new_title = Puppet::Parser::AST::String.new(:value => "other_title") new_instance = ast::ResourceInstance.new(:title => new_title, :parameters => ast::ASTArray.new(:children => [])) @resource.instances.push(new_instance) @resource.evaluate(@scope).collect { |r| r.title }.should == ["mytitle", "other_title"] end it "should handover resources to the compiler" do titles = [] %w{one two}.each do |title| titles << Puppet::Parser::AST::String.new(:value => title) end array = Puppet::Parser::AST::ASTArray.new(:children => titles) @instance.title = array result = @resource.evaluate(@scope) result.each do |res| @compiler.catalog.resource(res.ref).should be_instance_of(Puppet::Parser::Resource) end end it "should generate virtual resources if it is virtual" do @resource.virtual = true result = @resource.evaluate(@scope) result[0].should be_virtual end it "should generate virtual and exported resources if it is exported" do @resource.exported = true result = @resource.evaluate(@scope) result[0].should be_virtual result[0].should be_exported end # Related to #806, make sure resources always look up the full path to the resource. describe "when generating qualified resources" do before do @scope = Puppet::Parser::Scope.new :compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @parser = Puppet::Parser::Parser.new(Puppet::Node::Environment.new) ["one", "one::two", "three"].each do |name| @parser.environment.known_resource_types.add(Puppet::Resource::Type.new(:definition, name, {})) end @twoscope = @scope.newscope(:namespace => "one") @twoscope.resource = @scope.resource end def resource(type, params = nil) params ||= Puppet::Parser::AST::ASTArray.new(:children => []) instance = Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::String.new(:value => "myresource"), :parameters => params) Puppet::Parser::AST::Resource.new(:type => type, :instances => Puppet::Parser::AST::ASTArray.new(:children => [instance])) end it "should be able to generate resources with fully qualified type information" do resource("two").evaluate(@twoscope)[0].type.should == "One::Two" end it "should be able to generate resources with unqualified type information" do resource("one").evaluate(@twoscope)[0].type.should == "One" end it "should correctly generate resources that can look up builtin types" do resource("file").evaluate(@twoscope)[0].type.should == "File" end it "should correctly generate resources that can look up defined classes by title" do @scope.known_resource_types.add_hostclass Puppet::Resource::Type.new(:hostclass, "Myresource", {}) @scope.compiler.stubs(:evaluate_classes) res = resource("class").evaluate(@twoscope)[0] res.type.should == "Class" res.title.should == "Myresource" end it "should evaluate parameterized classes when they are instantiated" do @scope.known_resource_types.add_hostclass Puppet::Resource::Type.new(:hostclass, "Myresource", {}) @scope.compiler.expects(:evaluate_classes).with(['myresource'],@twoscope,false,true) resource("class").evaluate(@twoscope)[0] end it "should fail for resource types that do not exist" do lambda { resource("nosuchtype").evaluate(@twoscope) }.should raise_error(Puppet::ParseError) end end end describe "for class resources" do before do @title = Puppet::Parser::AST::String.new(:value => "classname") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @scope.stubs(:resource).returns(stub_everything) @instance = ast::ResourceInstance.new(:title => @title, :parameters => ast::ASTArray.new(:children => [])) @resource = ast::Resource.new(:type => "Class", :instances => ast::ASTArray.new(:children => [@instance])) @resource.stubs(:qualified_type).returns("Resource") @type = Puppet::Resource::Type.new(:hostclass, "classname") @compiler.known_resource_types.add(@type) end it "should instantiate the class" do @compiler.stubs(:evaluate_classes) result = @resource.evaluate(@scope) result.length.should == 1 result.first.ref.should == "Class[Classname]" @compiler.catalog.resource("Class[Classname]").should equal(result.first) end it "should cause its parent to be evaluated" do parent_type = Puppet::Resource::Type.new(:hostclass, "parentname") @compiler.stubs(:evaluate_classes) @compiler.known_resource_types.add(parent_type) @type.parent = "parentname" result = @resource.evaluate(@scope) result.length.should == 1 result.first.ref.should == "Class[Classname]" @compiler.catalog.resource("Class[Classname]").should equal(result.first) @compiler.catalog.resource("Class[Parentname]").should be_instance_of(Puppet::Parser::Resource) end end end diff --git a/spec/unit/parser/ast/selector_spec.rb b/spec/unit/parser/ast/selector_spec.rb index 771b5df6c..0a4921fd2 100755 --- a/spec/unit/parser/ast/selector_spec.rb +++ b/spec/unit/parser/ast/selector_spec.rb @@ -1,86 +1,86 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::Selector do let :scope do Puppet::Parser::Scope.new end # Take a code expression containing a selector, and return that portion of # the AST. This does the magic required to make that legal and all. def parse(selector) Puppet::Parser::Parser.new(scope.environment). parse("$foo = #{selector}"). code[0].value # extract only the selector end describe "when evaluating" do it "should evaluate param" do selector = parse 'value ? { default => result }' selector.param.expects(:safeevaluate) selector.evaluate(scope) end it "should try to match each option in sequence" do selector = parse '"a" ? { "a" => "a", "b" => "b", default => "default" }' order = sequence('evaluation of matching options') selector.values.each do |slot| slot.param.expects(:evaluate_match).in_sequence(order).returns(false) end selector.evaluate(scope) end describe "when scanning values" do it "should evaluate and return first matching option" do selector = parse '"b" ? { "a" => "=a", "b" => "=b", "c" => "=c" }' selector.evaluate(scope).should == '=b' end it "should evaluate the default option if none matched" do selector = parse '"a" ? { "b" => "=b", default => "=default" }' selector.evaluate(scope).should == "=default" end it "should return the default even if that isn't the last option" do selector = parse '"a" ? { "b" => "=b", default => "=default", "c" => "=c" }' selector.evaluate(scope).should == "=default" end it "should raise ParseError if nothing matched, and no default" do selector = parse '"a" ? { "b" => "=b" }' msg = /No matching value for selector param/ expect { selector.evaluate(scope) }.to raise_error Puppet::ParseError, msg end it "should unset scope ephemeral variables after option evaluation" do selector = parse '"a" ? { "a" => "=a" }' scope.expects(:unset_ephemeral_var).with(scope.ephemeral_level) selector.evaluate(scope) end it "should not leak ephemeral variables even if evaluation fails" do selector = parse '"a" ? { "b" => "=b" }' scope.expects(:unset_ephemeral_var).with(scope.ephemeral_level) expect { selector.evaluate(scope) }.to raise_error end end end describe "when converting to string" do it "should work with a single match" do parse('$input ? { "a" => "a+" }').to_s.should == '$input ? { "a" => "a+" }' end it "should work with multiple matches" do parse('$input ? { "a" => "a+", "b" => "b+" }').to_s. should == '$input ? { "a" => "a+", "b" => "b+" }' end it "should preserve order of inputs" do match = ('a' .. 'z').map {|x| "#{x} => #{x}" }.join(', ') selector = parse "$input ? { #{match} }" selector.to_s.should == "$input ? { #{match} }" end end end diff --git a/spec/unit/parser/ast/vardef_spec.rb b/spec/unit/parser/ast/vardef_spec.rb index 7dd2b31e7..4a1dcc3ed 100755 --- a/spec/unit/parser/ast/vardef_spec.rb +++ b/spec/unit/parser/ast/vardef_spec.rb @@ -1,66 +1,66 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::AST::VarDef do before :each do @scope = Puppet::Parser::Scope.new end describe "when evaluating" do it "should evaluate arguments" do name = mock 'name' value = mock 'value' name.expects(:safeevaluate).with(@scope) value.expects(:safeevaluate).with(@scope) vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, :line => nil vardef.evaluate(@scope) end it "should be in append=false mode if called without append" do name = stub 'name', :safeevaluate => "var" value = stub 'value', :safeevaluate => "1" @scope.expects(:setvar).with { |name,value,options| options[:append] == nil } vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, :line => nil vardef.evaluate(@scope) end it "should call scope in append mode if append is true" do name = stub 'name', :safeevaluate => "var" value = stub 'value', :safeevaluate => "1" @scope.expects(:setvar).with { |name,value,options| options[:append] == true } vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil, :line => nil, :append => true vardef.evaluate(@scope) end it "should call pass the source location to setvar" do name = stub 'name', :safeevaluate => "var" value = stub 'value', :safeevaluate => "1" @scope.expects(:setvar).with { |name,value,options| options[:file] == 'setvar.pp' and options[:line] == 917 } vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => 'setvar.pp', :line => 917 vardef.evaluate(@scope) end describe "when dealing with hash" do it "should delegate to the HashOrArrayAccess assign" do access = stub 'name' access.stubs(:is_a?).with(Puppet::Parser::AST::HashOrArrayAccess).returns(true) value = stub 'value', :safeevaluate => "1" vardef = Puppet::Parser::AST::VarDef.new :name => access, :value => value, :file => nil, :line => nil access.expects(:assign).with(@scope, '1') vardef.evaluate(@scope) end end end end diff --git a/spec/unit/parser/collector_spec.rb b/spec/unit/parser/collector_spec.rb index 251fec78a..28fa068fa 100755 --- a/spec/unit/parser/collector_spec.rb +++ b/spec/unit/parser/collector_spec.rb @@ -1,437 +1,437 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' require 'puppet/parser/collector' describe Puppet::Parser::Collector, "when initializing" do before do @scope = mock 'scope' @resource_type = 'resource_type' @form = :exported @vquery = mock 'vquery' @equery = mock 'equery' @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, @form) end it "should require a scope" do @collector.scope.should equal(@scope) end it "should require a resource type" do @collector.type.should == 'Resource_type' end it "should only accept :virtual or :exported as the collector form" do proc { @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @vquery, @equery, :other) }.should raise_error(ArgumentError) end it "should accept an optional virtual query" do @collector.vquery.should equal(@vquery) end it "should accept an optional exported query" do @collector.equery.should equal(@equery) end it "should canonize the type name" do @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) @collector.type.should == "Resource::Type" end it "should accept an optional resource override" do @collector = Puppet::Parser::Collector.new(@scope, "resource::type", @equery, @vquery, @form) override = { :parameters => "whatever" } @collector.add_override(override) @collector.overrides.should equal(override) end end describe Puppet::Parser::Collector, "when collecting specific virtual resources" do before do @scope = mock 'scope' @vquery = mock 'vquery' @equery = mock 'equery' @collector = Puppet::Parser::Collector.new(@scope, "resource_type", @equery, @vquery, :virtual) end it "should not fail when it does not find any resources to collect" do @collector.resources = ["File[virtual1]", "File[virtual2]"] @scope.stubs(:findresource).returns(false) proc { @collector.evaluate }.should_not raise_error end it "should mark matched resources as non-virtual" do @collector.resources = ["File[virtual1]", "File[virtual2]"] one = stub_everything 'one' one.expects(:virtual=).with(false) @scope.stubs(:findresource).with("File[virtual1]").returns(one) @scope.stubs(:findresource).with("File[virtual2]").returns(nil) @collector.evaluate end it "should return matched resources" do @collector.resources = ["File[virtual1]", "File[virtual2]"] one = stub_everything 'one' @scope.stubs(:findresource).with("File[virtual1]").returns(one) @scope.stubs(:findresource).with("File[virtual2]").returns(nil) @collector.evaluate.should == [one] end it "should delete itself from the compile's collection list if it has found all of its resources" do @collector.resources = ["File[virtual1]"] one = stub_everything 'one' @compiler.expects(:delete_collection).with(@collector) @scope.expects(:compiler).returns(@compiler) @scope.stubs(:findresource).with("File[virtual1]").returns(one) @collector.evaluate end it "should not delete itself from the compile's collection list if it has unfound resources" do @collector.resources = ["File[virtual1]"] one = stub_everything 'one' @compiler.expects(:delete_collection).never @scope.stubs(:findresource).with("File[virtual1]").returns(nil) @collector.evaluate end end describe Puppet::Parser::Collector, "when collecting virtual and catalog resources" do before do @scope = mock 'scope' @compiler = mock 'compile' @scope.stubs(:compiler).returns(@compiler) @resource_type = "Mytype" @vquery = proc { |res| true } @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, @vquery, :virtual) end it "should find all virtual resources matching the vquery" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => "Mytype", :virtual? => true @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should find all non-virtual resources matching the vquery" do one = stub_everything 'one', :type => "Mytype", :virtual? => false two = stub_everything 'two', :type => "Mytype", :virtual? => false @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should mark all matched resources as non-virtual" do one = stub_everything 'one', :type => "Mytype", :virtual? => true one.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one]) @collector.evaluate end it "should return matched resources" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => "Mytype", :virtual? => true @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one, two] end it "should return all resources of the correct type if there is no virtual query" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => "Mytype", :virtual? => true one.expects(:virtual=).with(false) two.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one, two]) @collector = Puppet::Parser::Collector.new(@scope, @resource_type, nil, nil, :virtual) @collector.evaluate.should == [one, two] end it "should not return or mark resources of a different type" do one = stub_everything 'one', :type => "Mytype", :virtual? => true two = stub_everything 'two', :type => :other, :virtual? => true one.expects(:virtual=).with(false) two.expects(:virtual=).never @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one] end it "should create a resource with overridden parameters" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' @compiler.stubs(:add_override) @compiler.expects(:resources).returns([one]) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.expects(:new).with { |type, title, h| h[:parameters] == param } @collector.evaluate end it "should define a new allow all child_of? on overriden resource" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' source = stub 'source' @compiler.stubs(:add_override) @compiler.expects(:resources).returns([one]) @collector.add_override(:parameters => param, :source => source ) Puppet::Parser::Resource.stubs(:new) source.expects(:meta_def).with { |name,block| name == :child_of? } @collector.evaluate end it "should not override already overriden resources for this same collection in a previous run" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' @compiler.stubs(:add_override) @compiler.expects(:resources).at_least(2).returns([one]) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.expects(:new).once.with { |type, title, h| h[:parameters] == param } @collector.evaluate @collector.evaluate end it "should not return resources that were collected in a previous run of this collector" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" @compiler.stubs(:resources).returns([one]) @collector.evaluate @collector.evaluate.should be_false end it "should tell the compiler about the overriden resources" do one = stub_everything 'one', :type => "Mytype", :virtual? => true, :title => "test" param = stub 'param' one.expects(:virtual=).with(false) @compiler.expects(:resources).returns([one]) @collector.add_override(:parameters => param ) Puppet::Parser::Resource.stubs(:new).returns("whatever") @compiler.expects(:add_override).with("whatever") @collector.evaluate end it "should not return or mark non-matching resources" do @collector.vquery = proc { |res| res.name == :one } one = stub_everything 'one', :name => :one, :type => "Mytype", :virtual? => true two = stub_everything 'two', :name => :two, :type => "Mytype", :virtual? => true one.expects(:virtual=).with(false) two.expects(:virtual=).never @compiler.expects(:resources).returns([one, two]) @collector.evaluate.should == [one] end end describe Puppet::Parser::Collector, "when collecting exported resources", :if => can_use_scratch_database? do include PuppetSpec::Files before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new :compiler => @compiler @resource_type = "notify" @equery = ["title", "!=", ""] @vquery = proc { |r| true } @collector = Puppet::Parser::Collector.new(@scope, @resource_type, @equery, @vquery, :exported) end it "should just return false if :storeconfigs is not enabled" do Puppet[:storeconfigs] = false @collector.evaluate.should be_false end context "with storeconfigs enabled" do before :each do setup_scratch_database Puppet[:storeconfigs] = true Puppet[:environment] = "production" Puppet[:storeconfigs_backend] = "active_record" end it "should return all matching resources from the current compile and mark them non-virtual and non-exported" do one = Puppet::Parser::Resource.new('notify', 'one', :virtual => true, :exported => true, :scope => @scope) two = Puppet::Parser::Resource.new('notify', 'two', :virtual => true, :exported => true, :scope => @scope) @compiler.resources << one @compiler.resources << two @collector.evaluate.should == [one, two] one.should_not be_virtual two.should_not be_virtual # REVISIT: Apparently we never actually marked local resources as # non-exported. So, this is what the previous test asserted, and checking # what it claims to do causes test failures. --daniel 2011-08-23 end it "should mark all returned resources as not virtual" do one = Puppet::Parser::Resource.new('notify', 'one', :virtual => true, :exported => true, :scope => @scope) @compiler.resources << one @collector.evaluate.should == [one] one.should_not be_virtual end it "should convert all found resources into parser resources if necessary" do host = Puppet::Rails::Host.create!(:name => 'one.local') Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'whammo', :exported => true) result = @collector.evaluate result.length.should == 1 result.first.should be_an_instance_of Puppet::Parser::Resource result.first.type.should == 'Notify' result.first.title.should == 'whammo' end it "should leave parser resources alone" do resource = Puppet::Parser::Resource.new(:file, "/tmp/foo", :scope => @scope) resource2 = Puppet::Parser::Resource.new(:file, "/tmp/bar", :scope => @scope) resource.expects(:to_resource).never resource2.expects(:to_resource).never resources = [resource, resource2] Puppet::Resource.indirection.stubs(:search).returns resources @collector.evaluate.should == resources end it "should override all exported collected resources if collector has an override" do host = Puppet::Rails::Host.create!(:name => 'one.local') Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'whammo', :exported => true) param = Puppet::Parser::Resource::Param. new(:name => 'message', :value => 'howdy') @collector.add_override(:parameters => [param], :scope => @scope) got = @collector.evaluate got.first[:message].should == param.value end it "should store converted resources in the compile's resource list" do host = Puppet::Rails::Host.create!(:name => 'one.local') Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'whammo', :exported => true) @compiler.expects(:add_resource).with do |scope, resource| scope.should be_an_instance_of Puppet::Parser::Scope resource.type.should == 'Notify' resource.title.should == 'whammo' true end @collector.evaluate end # This way one host doesn't store another host's resources as exported. it "should mark resources collected from the database as not exported" do host = Puppet::Rails::Host.create!(:name => 'one.local') Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'whammo', :exported => true) got = @collector.evaluate got.length.should == 1 got.first.type.should == "Notify" got.first.title.should == "whammo" got.first.should_not be_exported end it "should fail if an equivalent resource already exists in the compile" do host = Puppet::Rails::Host.create!(:name => 'one.local') Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'whammo', :exported => true) local = Puppet::Parser::Resource.new('notify', 'whammo', :scope => @scope) @compiler.add_resource(@scope, local) expect { @collector.evaluate }. to raise_error Puppet::ParseError, /exists with the type and title/ end it "should ignore exported resources that match already-collected resources" do host = Puppet::Rails::Host.create!(:name => 'one.local') # One that we already collected... db = Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'whammo', :exported => true) # ...and one we didn't. Puppet::Rails::Resource. create!(:host => host, :restype => 'Notify', :title => 'boingy-boingy', :exported => true) local = Puppet::Parser::Resource.new('notify', 'whammo', :scope => @scope, :collector_id => db.id) @compiler.add_resource(@scope, local) got = nil expect { got = @collector.evaluate }.not_to raise_error(Puppet::ParseError) got.length.should == 1 got.first.type.should == "Notify" got.first.title.should == "boingy-boingy" end end end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index e3a5b4271..4561938db 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -1,806 +1,806 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' class CompilerTestResource attr_accessor :builtin, :virtual, :evaluated, :type, :title def initialize(type, title) @type = type @title = title end def [](attr) return nil if attr == :stage :main end def ref "#{type.to_s.capitalize}[#{title}]" end def evaluated? @evaluated end def builtin_type? @builtin end def virtual? @virtual end def evaluate end def file "/fake/file/goes/here" end def line "42" end end describe Puppet::Parser::Compiler do include PuppetSpec::Files def resource(type, title) Puppet::Parser::Resource.new(type, title, :scope => @scope) end before :each do # Push me faster, I wanna go back in time! (Specifically, freeze time # across the test since we have a bunch of version == timestamp code # hidden away in the implementation and we keep losing the race.) # --daniel 2011-04-21 now = Time.now Time.stubs(:now).returns(now) @node = Puppet::Node.new("testnode", :facts => Puppet::Node::Facts.new("facts", {})) @known_resource_types = Puppet::Resource::TypeCollection.new "development" @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => stub('source')) @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) @scope.resource = @scope_resource @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end it "should have a class method that compiles, converts, and returns a catalog" do compiler = stub 'compiler' Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler catalog = stub 'catalog' compiler.expects(:compile).returns catalog converted_catalog = stub 'converted_catalog' catalog.expects(:to_resource).returns converted_catalog Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) end it "should fail intelligently when a class-level compile fails" do Puppet::Parser::Compiler.expects(:new).raises ArgumentError lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) end it "should use the node's environment as its environment" do @compiler.environment.should equal(@node.environment) end it "should include the resource type collection helper" do Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should be able to return a class list containing all added classes" do @compiler.add_class "" @compiler.add_class "one" @compiler.add_class "two" @compiler.classlist.sort.should == %w{one two}.sort end describe "when initializing" do it "should set its node attribute" do @compiler.node.should equal(@node) end it "should detect when ast nodes are absent" do @compiler.ast_nodes?.should be_false end it "should detect when ast nodes are present" do @known_resource_types.expects(:nodes?).returns true @compiler.ast_nodes?.should be_true end it "should copy the known_resource_types version to the catalog" do @compiler.catalog.version.should == @known_resource_types.version end it "should copy any node classes into the class list" do node = Puppet::Node.new("mynode") node.classes = %w{foo bar} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should transform node class hashes into a class list" do node = Puppet::Node.new("mynode") node.classes = {'foo'=>{'one'=>'1'}, 'bar'=>{'two'=>'2'}} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should add a 'main' stage to the catalog" do @compiler.catalog.resource(:stage, :main).should be_instance_of(Puppet::Parser::Resource) end end describe "when managing scopes" do it "should create a top scope" do @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) end it "should be able to create new scopes" do @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) end it "should set the parent scope of the new scope to be the passed-in parent" do scope = mock 'scope' newscope = @compiler.newscope(scope) newscope.parent.should equal(scope) end it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do scope = mock 'scope' newscope = @compiler.newscope(nil) newscope.parent.should equal(@compiler.topscope) end end describe "when compiling" do def compile_methods [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, :finish, :store, :extract, :evaluate_relationships] end # Stub all of the main compile methods except the ones we're specifically interested in. def compile_stub(*except) (compile_methods - except).each { |m| @compiler.stubs(m) } end it "should set node parameters as variables in the top scope" do params = {"a" => "b", "c" => "d"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.topscope['a'].should == "b" @compiler.topscope['c'].should == "d" end it "should set the client and server versions on the catalog" do params = {"clientversion" => "2", "serverversion" => "3"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.catalog.client_version.should == "2" @compiler.catalog.server_version.should == "3" end it "should evaluate any existing classes named in the node" do classes = %w{one two three four} main = stub 'main' one = stub 'one', :name => "one" three = stub 'three', :name => "three" @node.stubs(:name).returns("whatever") @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } end it "should evaluate any parameterized classes named in the node" do classes = {'foo'=>{'1'=>'one'}, 'bar'=>{'2'=>'two'}} @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.compile end it "should evaluate the main class if it exists" do compile_stub(:evaluate_main) main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } @compiler.topscope.expects(:source=).with(main_class) @compiler.compile end it "should create a new, empty 'main' if no main class exists" do compile_stub(:evaluate_main) @compiler.compile @known_resource_types.find_hostclass([""], "").should be_instance_of(Puppet::Resource::Type) end it "should add an edge between the main stage and main class" do @compiler.compile (stage = @compiler.catalog.resource(:stage, "main")).should be_instance_of(Puppet::Parser::Resource) (klass = @compiler.catalog.resource(:class, "")).should be_instance_of(Puppet::Parser::Resource) @compiler.catalog.edge?(stage, klass).should be_true end it "should evaluate any node classes" do @node.stubs(:classes).returns(%w{one two three four}) @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) @compiler.send(:evaluate_node_classes) end it "should evaluate all added collections" do colls = [] # And when the collections fail to evaluate. colls << mock("coll1-false") colls << mock("coll2-false") colls.each { |c| c.expects(:evaluate).returns(false) } @compiler.add_collection(colls[0]) @compiler.add_collection(colls[1]) compile_stub(:evaluate_generators) @compiler.compile end it "should ignore builtin resources" do resource = resource(:file, "testing") @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } @compiler.compile end it "should not evaluate already-evaluated resources" do resource = resource(:file, "testing") resource.stubs(:evaluated?).returns true @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources created by evaluating other resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) resource2 = CompilerTestResource.new(:file, "other") # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } @compiler.compile end describe "when finishing" do before do @compiler.send(:evaluate_main) @catalog = @compiler.catalog end def add_resource(name, parent = nil) resource = Puppet::Parser::Resource.new "file", name, :scope => @scope @compiler.add_resource(@scope, resource) @catalog.add_edge(parent, resource) if parent resource end it "should call finish() on all resources" do # Add a resource that does respond to :finish resource = Puppet::Parser::Resource.new "file", "finish", :scope => @scope resource.expects(:finish) @compiler.add_resource(@scope, resource) # And one that does not dnf_resource = stub_everything "dnf", :ref => "File[dnf]", :type => "file" @compiler.add_resource(@scope, dnf_resource) @compiler.send(:finish) end it "should call finish() in add_resource order" do resources = sequence('resources') resource1 = add_resource("finish1") resource1.expects(:finish).in_sequence(resources) resource2 = add_resource("finish2") resource2.expects(:finish).in_sequence(resources) @compiler.send(:finish) end it "should add each container's metaparams to its contained resources" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) @compiler.send(:finish) resource1[:noop].should be_true end it "should add metaparams recursively" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end it "should prefer metaparams from immediate parents" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) resource1[:noop] = false @compiler.send(:finish) resource2[:noop].should be_false end it "should merge tags downward" do main = @catalog.resource(:class, :main) main.tag("one") resource1 = add_resource("meh", main) resource1.tag "two" resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2.tags.should be_include("one") resource2.tags.should be_include("two") end it "should work if only middle resources have metaparams set" do main = @catalog.resource(:class, :main) resource1 = add_resource("meh", main) resource1[:noop] = true resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end end it "should return added resources in add order" do resource1 = resource(:file, "yay") @compiler.add_resource(@scope, resource1) resource2 = resource(:file, "youpi") @compiler.add_resource(@scope, resource2) @compiler.resources.should == [resource1, resource2] end it "should add resources that do not conflict with existing resources" do resource = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_vertex(resource) end it "should fail to add resources that conflict with existing resources" do path = make_absolute("/foo") file1 = Puppet::Type.type(:file).new :path => path file2 = Puppet::Type.type(:file).new :path => path @compiler.add_resource(@scope, file1) lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should add an edge from the scope resource to the added resource" do resource = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should not add non-class resources that don't specify a stage to the 'main' stage" do main = @compiler.catalog.resource(:stage, :main) resource = resource(:file, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should_not be_edge(main, resource) end it "should not add any parent-edges to stages" do stage = resource(:stage, "other") @compiler.add_resource(@scope, stage) @scope.resource = resource(:class, "foo") @compiler.catalog.edge?(@scope.resource, stage).should be_false end it "should not attempt to add stages to other stages" do other_stage = resource(:stage, "other") second_stage = resource(:stage, "second") @compiler.add_resource(@scope, other_stage) @compiler.add_resource(@scope, second_stage) second_stage[:stage] = "other" @compiler.catalog.edge?(other_stage, second_stage).should be_false end it "should have a method for looking up resources" do resource = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay[foo]").should equal(resource) end it "should be able to look resources up by type and title" do resource = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay", "foo").should equal(resource) end it "should not evaluate virtual defined resources" do resource = resource(:file, "testing") resource.virtual = true @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end end describe "when evaluating collections" do it "should evaluate each collection" do 2.times { |i| coll = mock 'coll%s' % i @compiler.add_collection(coll) # This is the hard part -- we have to emulate the fact that # collections delete themselves if they are done evaluating. coll.expects(:evaluate).with do @compiler.delete_collection(coll) end } @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } end it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(nil) @compiler.add_collection(coll) lambda { @compiler.compile }.should_not raise_error end it "should fail when there are unevaluated resource collections that refer to a specific resource" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(:something) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources something' end it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns([:one, :two]) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources one, two' end end describe "when evaluating relationships" do it "should evaluate each relationship with its catalog" do dep = stub 'dep' dep.expects(:evaluate).with(@compiler.catalog) @compiler.add_relationship dep @compiler.evaluate_relationships end end describe "when told to evaluate missing classes" do it "should fail if there's no source listed for the scope" do scope = stub 'scope', :source => nil proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) end it "should raise an error if a class is not found" do @scope.expects(:find_hostclass).with("notfound", {:assume_fqname => false}).returns(nil) lambda{ @compiler.evaluate_classes(%w{notfound}, @scope) }.should raise_error(Puppet::Error, /Could not find class/) end it "should raise an error when it can't find class" do klasses = {'foo'=>nil} @node.classes = klasses @compiler.topscope.stubs(:find_hostclass).with('foo', {:assume_fqname => false}).returns(nil) lambda{ @compiler.compile }.should raise_error(Puppet::Error, /Could not find class foo for testnode/) end end describe "when evaluating found classes" do before do Puppet.settings[:data_binding_terminus] = "none" @class = stub 'class', :name => "my::class" @scope.stubs(:find_hostclass).with("myclass", {:assume_fqname => false}).returns(@class) @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" end it "should evaluate each class" do @compiler.catalog.stubs(:tag) @class.expects(:ensure_in_catalog).with(@scope) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end describe "and the classes are specified as a hash with parameters" do before do @node.classes = {} @ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') end # Define the given class with default parameters def define_class(name, parameters) @node.classes[name] = parameters klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => @ast_obj, '2' => @ast_obj}) @compiler.topscope.known_resource_types.add klass end def compile @catalog = @compiler.compile end it "should record which classes are evaluated" do classes = {'foo'=>{}, 'bar::foo'=>{}, 'bar'=>{}} classes.each { |c, params| define_class(c, params) } compile() classes.each { |name, p| @catalog.classes.should include(name) } end it "should provide default values for parameters that have no values specified" do define_class('foo', {}) compile() @catalog.resource(:class, 'foo')['1'].should == "foo" end it "should use any provided values" do define_class('foo', {'1' => 'real_value'}) compile() @catalog.resource(:class, 'foo')['1'].should == "real_value" end it "should support providing some but not all values" do define_class('foo', {'1' => 'real_value'}) compile() @catalog.resource(:class, 'Foo')['1'].should == "real_value" @catalog.resource(:class, 'Foo')['2'].should == "foo" end it "should ensure each node class is in catalog and has appropriate tags" do klasses = ['bar::foo'] @node.classes = klasses ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') klasses.each do |name| klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj}) @compiler.topscope.known_resource_types.add klass end catalog = @compiler.compile r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo'] end end it "should fail if required parameters are missing" do klass = {'foo'=>{'1'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Must pass 2 to Class[Foo]") end it "should fail if invalid parameters are passed" do klass = {'foo'=>{'3'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Invalid parameter 3") end it "should ensure class is in catalog without params" do @node.classes = klasses = {'foo'=>nil} foo = Puppet::Resource::Type.new(:hostclass, 'foo') @compiler.topscope.known_resource_types.add foo catalog = @compiler.compile catalog.classes.should include 'foo' end it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate).never @class.expects(:ensure_in_catalog).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should immediately evaluate the resources created for found classes when asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate) @class.expects(:ensure_in_catalog).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes that have already been evaluated" do @compiler.catalog.stubs(:tag) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes previously evaluated with different capitalization" do @compiler.catalog.stubs(:tag) @scope.stubs(:find_hostclass).with("MyClass",{:assume_fqname => false}).returns(@class) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{MyClass}, @scope, false) end end describe "when evaluating AST nodes with no AST nodes present" do it "should do nothing" do @compiler.expects(:ast_nodes?).returns(false) @compiler.known_resource_types.expects(:nodes).never Puppet::Parser::Resource.expects(:new).never @compiler.send(:evaluate_ast_node) end end describe "when evaluating AST nodes with AST nodes present" do before do @compiler.known_resource_types.stubs(:nodes?).returns true # Set some names for our test @node.stubs(:names).returns(%w{a b c}) @compiler.known_resource_types.stubs(:node).with("a").returns(nil) @compiler.known_resource_types.stubs(:node).with("b").returns(nil) @compiler.known_resource_types.stubs(:node).with("c").returns(nil) # It should check this last, of course. @compiler.known_resource_types.stubs(:node).with("default").returns(nil) end it "should fail if the named node cannot be found" do proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) end it "should evaluate the first node class matching the node name" do node_class = stub 'node', :name => "c", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) @compiler.compile end it "should match the default node if no matching node can be found" do node_class = stub 'node', :name => "default", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) @compiler.compile end it "should evaluate the node resource immediately rather than using lazy evaluation" do node_class = stub 'node', :name => "c" @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) node_resource.expects(:evaluate) @compiler.send(:evaluate_ast_node) end end describe "when managing resource overrides" do before do @override = stub 'override', :ref => "File[/foo]", :type => "my" @resource = resource(:file, "/foo") end it "should be able to store overrides" do lambda { @compiler.add_override(@override) }.should_not raise_error end it "should apply overrides to the appropriate resources" do @compiler.add_resource(@scope, @resource) @resource.expects(:merge).with(@override) @compiler.add_override(@override) @compiler.compile end it "should accept overrides before the related resource has been created" do @resource.expects(:merge).with(@override) # First store the override @compiler.add_override(@override) # Then the resource @compiler.add_resource(@scope, @resource) # And compile, so they get resolved @compiler.compile end it "should fail if the compile is finished and resource overrides have not been applied" do @compiler.add_override(@override) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Could not find resource(s) File[/foo] for overriding' end end end diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb index 1bf75e623..c3c2792a3 100755 --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -1,203 +1,203 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/files' describe Puppet::Parser::Files do include PuppetSpec::Files before do @basepath = make_absolute("/somepath") end it "should have a method for finding a template" do Puppet::Parser::Files.should respond_to(:find_template) end it "should have a method for finding manifests" do Puppet::Parser::Files.should respond_to(:find_manifests) end describe "when searching for templates" do it "should return fully-qualified templates directly" do Puppet::Parser::Files.expects(:modulepath).never Puppet::Parser::Files.find_template(@basepath + "/my/template").should == @basepath + "/my/template" end it "should return the template from the first found module" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/one/mymod/templates/mytemplate") Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" end it "should return the file in the templatedir if it exists" do Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") Puppet[:modulepath] = "/one:/two" File.stubs(:directory?).returns(true) FileTest.stubs(:exist?).returns(true) Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" end it "should not raise an error if no valid templatedir exists and the template exists in a module" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/one/mymod/templates/mytemplate") Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(nil) Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" end it "should return unqualified templates if they exist in the template dir" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should only return templates if they actually exist" do FileTest.expects(:exist?).with("/my/templates/mytemplate").returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should return nil when asked for a template that doesn't exist" do FileTest.expects(:exist?).with("/my/templates/mytemplate").returns false Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should be_nil end it "should search in the template directories before modules" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).never Puppet::Parser::Files.find_template("mytemplate") end it "should accept relative templatedirs" do FileTest.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" # We expand_path to normalize backslashes and slashes on Windows File.expects(:directory?).with(File.expand_path(File.join(Dir.getwd,"my/templates"))).returns(true) Puppet::Parser::Files.find_template("mytemplate").should == File.expand_path(File.join(Dir.getwd,"my/templates/mytemplate")) end it "should use the environment templatedir if no module is found and an environment is specified" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use first dir from environment templatedir if no module is found and an environment is specified" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(false) FileTest.expects(:exist?).with("/two/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" end it "should use the node environment if specified" do mod = mock 'module' Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") Puppet::Parser::Files.find_template("mymod/envtemplate", "myenv").should == "/my/modules/mymod/templates/envtemplate" end it "should return nil if no template can be found" do Puppet::Parser::Files.find_template("foomod/envtemplate", "myenv").should be_nil end after { Puppet.settings.clear } end describe "when searching for manifests" do it "should ignore invalid modules" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") Puppet.expects(:value).with(:modulepath).never Dir.stubs(:glob).returns %w{foo} Puppet::Parser::Files.find_manifests("mymod/init.pp") end end describe "when searching for manifests when no module is found" do before do File.stubs(:find).returns(nil) end it "should not look for modules when paths are fully qualified" do Puppet.expects(:value).with(:modulepath).never file = @basepath + "/fully/qualified/file.pp" Dir.stubs(:glob).with(file).returns([file]) Puppet::Parser::Files.find_manifests(file) end it "should return nil and an array of fully qualified files" do file = @basepath + "/fully/qualified/file.pp" Dir.stubs(:glob).with(file).returns([file]) Puppet::Parser::Files.find_manifests(file).should == [nil, [file]] end it "should match against provided fully qualified patterns" do pattern = @basepath + "/fully/qualified/pattern/*" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns(%w{my file list}) Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{my file list} end it "should look for files relative to the current directory" do # We expand_path to normalize backslashes and slashes on Windows cwd = File.expand_path(Dir.getwd) Dir.expects(:glob).with("#{cwd}/foobar/init.pp").returns(["#{cwd}/foobar/init.pp"]) Puppet::Parser::Files.find_manifests("foobar/init.pp")[1].should == ["#{cwd}/foobar/init.pp"] end it "should only return files, not directories" do pattern = @basepath + "/fully/qualified/pattern/*" file = @basepath + "/my/file" dir = @basepath + "/my/directory" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns([file, dir]) FileTest.expects(:directory?).with(file).returns(false) FileTest.expects(:directory?).with(dir).returns(true) Puppet::Parser::Files.find_manifests(pattern)[1].should == [file] end it "should return files once only" do pattern = @basepath + "/fully/qualified/pattern/*" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns(%w{one two one}) Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{one two} end end describe "when searching for manifests in a found module" do it "should return the name of the module and the manifests from the first found module" do mod = Puppet::Module.new("mymod") Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) Puppet::Parser::Files.find_manifests("mymod/init.pp").should == ["mymod", ["/one/mymod/manifests/init.pp"]] end it "should use the node environment if specified" do mod = Puppet::Module.new("mymod") Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) Puppet::Parser::Files.find_manifests("mymod/init.pp", :environment => "myenv")[1].should == ["/one/mymod/manifests/init.pp"] end after { Puppet.settings.clear } end end diff --git a/spec/unit/parser/functions/defined_spec.rb b/spec/unit/parser/functions/defined_spec.rb index 0651864fb..67a9ca008 100755 --- a/spec/unit/parser/functions/defined_spec.rb +++ b/spec/unit/parser/functions/defined_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the 'defined' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do Puppet::Node::Environment.stubs(:current).returns(nil) @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should exist" do Puppet::Parser::Functions.function("defined").should == "function_defined" end it "should be true when the name is defined as a class" do @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "yayness") @scope.function_defined("yayness").should be_true end it "should be true when the name is defined as a definition" do @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") @scope.function_defined("yayness").should be_true end it "should be true when the name is defined as a builtin type" do @scope.function_defined("file").should be_true end it "should be true when any of the provided names are defined" do @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "yayness") @scope.function_defined(["meh", "yayness", "booness"]).should be_true end it "should be false when a single given name is not defined" do @scope.function_defined("meh").should be_false end it "should be false when none of the names are defined" do @scope.function_defined(["meh", "yayness", "booness"]).should be_false end it "should be true when a resource reference is provided and the resource is in the catalog" do resource = Puppet::Resource.new("file", "/my/file") @compiler.add_resource(@scope, resource) @scope.function_defined(resource).should be_true end end diff --git a/spec/unit/parser/functions/extlookup_spec.rb b/spec/unit/parser/functions/extlookup_spec.rb index 59ecf39c0..eadf51662 100755 --- a/spec/unit/parser/functions/extlookup_spec.rb +++ b/spec/unit/parser/functions/extlookup_spec.rb @@ -1,98 +1,98 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'tempfile' describe "the extlookup function" do include PuppetSpec::Files before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new @scope.stubs(:environment).returns(Puppet::Node::Environment.new('production')) end it "should exist" do Puppet::Parser::Functions.function("extlookup").should == "function_extlookup" end it "should raise a ParseError if there is less than 1 arguments" do lambda { @scope.function_extlookup([]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 3 arguments" do lambda { @scope.function_extlookup(["foo", "bar", "baz", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should return the default" do result = @scope.function_extlookup([ "key", "default"]) result.should == "default" end it "should lookup the key in a supplied datafile" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value' t.puts 'nonkey,nonvalue' t.close result = @scope.function_extlookup([ "key", "default", t.path]) result.should == "value" end end it "should return an array if the datafile contains more than two columns" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value1,value2' t.puts 'nonkey,nonvalue,nonvalue' t.close result = @scope.function_extlookup([ "key", "default", t.path]) result.should == ["value1", "value2"] end end it "should raise an error if there's no matching key and no default" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value' t.puts 'nonkey,nonvalue' t.close result = @scope.function_extlookup([ "key", nil, t.path]) result.should == "value" end end describe "should look in $extlookup_datadir for data files listed by $extlookup_precedence" do before do dir = tmpdir('extlookup_datadir') @scope.stubs(:[]).with('::extlookup_datadir').returns(dir) File.open(File.join(dir, "one.csv"),"w"){|one| one.puts "key,value1" } File.open(File.join(dir, "two.csv"),"w") do |two| two.puts "key,value2" two.puts "key2,value_two" end end it "when the key is in the first file" do @scope.stubs(:[]).with('::extlookup_precedence').returns(["one","two"]) result = @scope.function_extlookup([ "key" ]) result.should == "value1" end it "when the key is in the second file" do @scope.stubs(:[]).with('::extlookup_precedence').returns(["one","two"]) result = @scope.function_extlookup([ "key2" ]) result.should == "value_two" end it "should not modify extlookup_precedence data" do variable = '%{fqdn}' @scope.stubs(:[]).with('::extlookup_precedence').returns([variable,"one"]) @scope.stubs(:[]).with('::fqdn').returns('myfqdn') result = @scope.function_extlookup([ "key" ]) variable.should == '%{fqdn}' end end end diff --git a/spec/unit/parser/functions/fail_spec.rb b/spec/unit/parser/functions/fail_spec.rb index bd685ae7a..48e0e0bde 100755 --- a/spec/unit/parser/functions/fail_spec.rb +++ b/spec/unit/parser/functions/fail_spec.rb @@ -1,26 +1,26 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the 'fail' parser function" do before :all do Puppet::Parser::Functions.autoloader.loadall end let :scope do scope = Puppet::Parser::Scope.new scope.stubs(:environment).returns(nil) scope end it "should exist" do Puppet::Parser::Functions.function(:fail).should == "function_fail" end it "should raise a parse error if invoked" do expect { scope.function_fail([]) }.to raise_error Puppet::ParseError end it "should join arguments into a string in the error" do expect { scope.function_fail(["hello", "world"]) }.to raise_error /hello world/ end end diff --git a/spec/unit/parser/functions/file_spec.rb b/spec/unit/parser/functions/file_spec.rb index d2ecc2fd4..37e12b194 100755 --- a/spec/unit/parser/functions/file_spec.rb +++ b/spec/unit/parser/functions/file_spec.rb @@ -1,51 +1,51 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/files' describe "the 'file' function" do include PuppetSpec::Files before :all do Puppet::Parser::Functions.autoloader.loadall end let :scope do Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("file").should == "function_file" end def with_file_content(content) path = tmpfile('file-function') file = File.new(path, 'w') file.sync = true file.print content yield path end it "should read a file" do with_file_content('file content') do |name| scope.function_file([name]).should == "file content" end end it "should return the first file if given two files" do with_file_content('one') do |one| with_file_content('two') do |two| scope.function_file([one, two]).should == "one" end end end it "should not fail when some files are absent" do expect { with_file_content('one') do |one| scope.function_file([make_absolute("/should-not-exist"), one]).should == 'one' end }.should_not raise_error end it "should fail when all files are absent" do expect { scope.function_file(['one']) }.to raise_error Puppet::ParseError end end diff --git a/spec/unit/parser/functions/fqdn_rand_spec.rb b/spec/unit/parser/functions/fqdn_rand_spec.rb index 53c498453..a6b0d6b29 100755 --- a/spec/unit/parser/functions/fqdn_rand_spec.rb +++ b/spec/unit/parser/functions/fqdn_rand_spec.rb @@ -1,56 +1,56 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the fqdn_rand function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new @scope[:fqdn] = "127.0.0.1" end it "should exist" do Puppet::Parser::Functions.function("fqdn_rand").should == "function_fqdn_rand" end it "should handle 0 arguments" do lambda { @scope.function_fqdn_rand([]) }.should_not raise_error(Puppet::ParseError) end it "should handle 1 argument'}" do lambda { @scope.function_fqdn_rand([3]) }.should_not raise_error(Puppet::ParseError) end (1..10).each { |n| it "should handle #{n} additional arguments" do lambda { @scope.function_fqdn_rand([3,1,2,3,4,5,6,7,8,9,10][0..n]) }.should_not raise_error(Puppet::ParseError) end it "should handle #{n} additional string arguments" do lambda { @scope.function_fqdn_rand([3,%w{ 1 2 3 4 5 6 7 8 9 10}].flatten[0..n]) }.should_not raise_error(Puppet::ParseError) end } it "should return a value less than max" do @scope.function_fqdn_rand([3]).should satisfy {|n| n.to_i < 3 } end it "should return the same values on subsequent invocations for the same host" do @scope.function_fqdn_rand([3,4]).should eql(@scope.function_fqdn_rand([3, 4])) end it "should return different sequences of value for different hosts" do val1 = @scope.function_fqdn_rand([10000000,4]) @scope.expects(:[]).with("::fqdn").returns("127.0.0.2") val2 = @scope.function_fqdn_rand([10000000,4]) val1.should_not eql(val2) end it "should return different values for the same hosts with different seeds" do val1 = @scope.function_fqdn_rand([10000000,4]) val2 = @scope.function_fqdn_rand([10000000,42]) val1.should_not eql(val2) end end diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb index 8a33954e9..8d3dd01fb 100755 --- a/spec/unit/parser/functions/generate_spec.rb +++ b/spec/unit/parser/functions/generate_spec.rb @@ -1,126 +1,126 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the generate function" do include PuppetSpec::Files before :all do Puppet::Parser::Functions.autoloader.loadall end let(:scope) { Puppet::Parser::Scope.new } it "should exist" do Puppet::Parser::Functions.function("generate").should == "function_generate" end it "accept a fully-qualified path as a command" do command = File.expand_path('/command/foo') Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == "yay" end it "should not accept a relative path as a command" do lambda { scope.function_generate(["command"]) }.should raise_error(Puppet::ParseError) end it "should not accept a command containing illegal characters" do lambda { scope.function_generate([File.expand_path('/##/command')]) }.should raise_error(Puppet::ParseError) end it "should not accept a command containing spaces" do lambda { scope.function_generate([File.expand_path('/com mand')]) }.should raise_error(Puppet::ParseError) end it "should not accept a command containing '..'" do command = File.expand_path("/command/../") lambda { scope.function_generate([command]) }.should raise_error(Puppet::ParseError) end it "should execute the generate script with the correct working directory" do command = File.expand_path("/command") Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end describe "on Windows", :as_platform => :windows do it "should accept the tilde in the path" do command = "C:/DOCUME~1/ADMINI~1/foo.bat" Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should accept lower-case drive letters" do command = 'd:/command/foo' Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should accept upper-case drive letters" do command = 'D:/command/foo' Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should accept forward and backslashes in the path" do command = 'D:\command/foo\bar' Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end it "should reject colons when not part of the drive letter" do lambda { scope.function_generate(['C:/com:mand']) }.should raise_error(Puppet::ParseError) end it "should reject root drives" do lambda { scope.function_generate(['C:/']) }.should raise_error(Puppet::ParseError) end end describe "on non-Windows", :as_platform => :posix do it "should reject backslashes" do lambda { scope.function_generate(['/com\\mand']) }.should raise_error(Puppet::ParseError) end it "should accept plus and dash" do command = "/var/folders/9z/9zXImgchH8CZJh6SgiqS2U+++TM/-Tmp-/foo" Dir.expects(:chdir).with(File.dirname(command)).returns("yay") scope.function_generate([command]).should == 'yay' end end let :command do cmd = tmpfile('function_generate') if Puppet.features.microsoft_windows? cmd += '.bat' text = '@echo off' + "\n" + 'echo a-%1 b-%2' else text = '#!/bin/sh' + "\n" + 'echo a-$1 b-$2' end File.open(cmd, 'w') {|fh| fh.puts text } File.chmod 0700, cmd cmd end it "should call generator with no arguments" do scope.function_generate([command]).should == "a- b-\n" end it "should call generator with one argument" do scope.function_generate([command, 'one']).should == "a-one b-\n" end it "should call generator with wo arguments" do scope.function_generate([command, 'one', 'two']).should == "a-one b-two\n" end it "should fail if generator is not absolute" do expect { scope.function_generate(['boo']) }.to raise_error Puppet::ParseError end it "should fail if generator fails" do expect { scope.function_generate(['/boo']) }.to raise_error Puppet::ParseError end end diff --git a/spec/unit/parser/functions/include_spec.rb b/spec/unit/parser/functions/include_spec.rb index 6802905a0..098f8c792 100755 --- a/spec/unit/parser/functions/include_spec.rb +++ b/spec/unit/parser/functions/include_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the 'include' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do Puppet::Node::Environment.stubs(:current).returns(nil) @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should exist" do Puppet::Parser::Functions.function("include").should == "function_include" end it "should include a single class" do inc = "foo" @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| klasses == [inc]}.returns([inc]) @scope.function_include("foo") end it "should include multiple classes" do inc = ["foo","bar"] @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| klasses == inc}.returns(inc) @scope.function_include(["foo","bar"]) end it "should include multiple classes passed in an array" do inc = ["foo","bar"] @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| klasses == inc}.returns(inc) @scope.function_include([["foo","bar"]]) end it "should flatten nested arrays" do inc = ["foo","bar","baz"] @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| klasses == inc}.returns(inc) @scope.function_include([["foo","bar"],"baz"]) end it "should not lazily evaluate the included class" do @compiler.expects(:evaluate_classes).with {|klasses,parser,lazy| lazy == false}.returns("foo") @scope.function_include("foo") end it "should raise if the class is not found" do @scope.stubs(:source).returns(true) expect { @scope.function_include("nosuchclass") }.to raise_error Puppet::Error end end diff --git a/spec/unit/parser/functions/inline_template_spec.rb b/spec/unit/parser/functions/inline_template_spec.rb index 1b1d1bdcd..d32eb2eab 100755 --- a/spec/unit/parser/functions/inline_template_spec.rb +++ b/spec/unit/parser/functions/inline_template_spec.rb @@ -1,61 +1,61 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the inline_template function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("inline_template").should == "function_inline_template" end it "should create a TemplateWrapper when called" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) @scope.function_inline_template(["test"]) end it "should pass the template string to TemplateWrapper.result" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.expects(:result).with("test") @scope.function_inline_template(["test"]) end it "should return what TemplateWrapper.result returns" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.expects(:result).returns("template contents evaluated") @scope.function_inline_template(["test"]).should == "template contents evaluated" end it "should concatenate template wrapper outputs for multiple templates" do tw1 = stub_everything "template_wrapper1" tw2 = stub_everything "template_wrapper2" Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) tw1.stubs(:result).returns("result1") tw2.stubs(:result).returns("result2") @scope.function_inline_template(["1","2"]).should == "result1result2" end it "should raise an error if the template raises an error" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.stubs(:result).raises lambda { @scope.function_inline_template(["1"]) }.should raise_error(Puppet::ParseError) end end diff --git a/spec/unit/parser/functions/realize_spec.rb b/spec/unit/parser/functions/realize_spec.rb index 159805cbd..efff4ff30 100755 --- a/spec/unit/parser/functions/realize_spec.rb +++ b/spec/unit/parser/functions/realize_spec.rb @@ -1,53 +1,53 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the realize function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @collector = stub_everything 'collector' @scope = Puppet::Parser::Scope.new @compiler = stub 'compiler' @compiler.stubs(:add_collection).with(@collector) @scope.stubs(:compiler).returns(@compiler) end it "should exist" do Puppet::Parser::Functions.function("realize").should == "function_realize" end it "should create a Collector when called" do Puppet::Parser::Collector.expects(:new).returns(@collector) @scope.function_realize("test") end it "should assign the passed-in resources to the collector" do Puppet::Parser::Collector.stubs(:new).returns(@collector) @collector.expects(:resources=).with(["test"]) @scope.function_realize("test") end it "should flatten the resources assigned to the collector" do Puppet::Parser::Collector.stubs(:new).returns(@collector) @collector.expects(:resources=).with(["test"]) @scope.function_realize([["test"]]) end it "should let the compiler know this collector" do Puppet::Parser::Collector.stubs(:new).returns(@collector) @collector.stubs(:resources=).with(["test"]) @compiler.expects(:add_collection).with(@collector) @scope.function_realize("test") end end diff --git a/spec/unit/parser/functions/regsubst_spec.rb b/spec/unit/parser/functions/regsubst_spec.rb index 4ed3bcf68..25101e53a 100755 --- a/spec/unit/parser/functions/regsubst_spec.rb +++ b/spec/unit/parser/functions/regsubst_spec.rb @@ -1,194 +1,194 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the regsubst function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("regsubst").should == "function_regsubst" end it "should raise a ParseError if there is less than 3 arguments" do lambda { @scope.function_regsubst(["foo", "bar"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 5 arguments" do lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "del", "x", "y"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError when given a bad flag" do lambda { @scope.function_regsubst(["foo", "bar", "gazonk", "X"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for non-string and non-array target" do lambda { @scope.function_regsubst([4711, "bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for array target with non-string element" do lambda { @scope.function_regsubst([["x", ["y"], "z"], "bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for a bad regular expression" do lambda { @scope.function_regsubst(["foo", "(bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError for a non-string regular expression" do lambda { @scope.function_regsubst(["foo", ["bar"], "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should handle groups" do result = @scope.function_regsubst( [ '130.236.254.10', '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', '\4-\3-\2-\1' ]) result.should(eql("10-254-236-130")) end it "should handle simple regexps" do result = @scope.function_regsubst( [ "the monkey breaks banana trees", "b[an]*a", "coconut" ]) result.should(eql("the monkey breaks coconut trees")) end it "should handle case-sensitive regexps" do result = @scope.function_regsubst( [ "the monkey breaks baNAna trees", "b[an]+a", "coconut" ]) result.should(eql("the monkey breaks baNAna trees")) end it "should handle case-insensitive regexps" do result = @scope.function_regsubst( [ "the monkey breaks baNAna trees", "b[an]+a", "coconut", "I" ]) result.should(eql("the monkey breaks coconut trees")) end it "should handle global substitutions" do result = @scope.function_regsubst( [ "the monkey breaks\tbanana trees", "[ \t]", "--", "G" ]) result.should(eql("the--monkey--breaks--banana--trees")) end it "should handle global substitutions with groups" do result = @scope.function_regsubst( [ '130.236.254.10', '([0-9]+)', '<\1>', 'G' ]) result.should(eql('<130>.<236>.<254>.<10>')) end it "should apply on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst([ data, '[.]', '-']) result.should(eql( ['130-236.254.10', 'foo-example.com', 'coconut', '10-20.30.40'])) end it "should apply global substitutions on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst([ data, '[.]', '-', 'G']) result.should(eql( ['130-236-254-10', 'foo-example-com', 'coconut', '10-20-30-40'])) end it "should handle groups on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst( [ data, '^([0-9]+)[.]([0-9]+)[.]([0-9]+)[.]([0-9]+)$', '\4-\3-\2-\1' ]) result.should(eql( ['10-254-236-130', 'foo.example.com', 'coconut', '40-30-20-10'])) end it "should handle global substitutions with groups on all elements of an array" do data = ['130.236.254.10', 'foo.example.com', 'coconut', '10.20.30.40'] result = @scope.function_regsubst( [ data, '([^.]+)', '<\1>', 'G' ]) result.should(eql( ['<130>.<236>.<254>.<10>', '..', '', '<10>.<20>.<30>.<40>'])) end it "should return an array (not a string) for a single element array parameter" do data = ['130.236.254.10'] result = @scope.function_regsubst( [ data, '([^.]+)', '<\1>', 'G' ]) result.should(eql(['<130>.<236>.<254>.<10>'])) end it "should return a string (not a one element array) for a simple string parameter" do data = '130.236.254.10' result = @scope.function_regsubst( [ data, '([^.]+)', '<\1>', 'G' ]) result.should(eql('<130>.<236>.<254>.<10>')) end end diff --git a/spec/unit/parser/functions/require_spec.rb b/spec/unit/parser/functions/require_spec.rb index 692b35305..9d1e4710a 100755 --- a/spec/unit/parser/functions/require_spec.rb +++ b/spec/unit/parser/functions/require_spec.rb @@ -1,76 +1,76 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the require function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @catalog = stub 'catalog' @compiler = stub 'compiler', :catalog => @catalog, :environment => nil @scope = Puppet::Parser::Scope.new @scope.stubs(:findresource) @scope.stubs(:compiler).returns(@compiler) @klass = stub 'class', :name => "myclass" @scope.stubs(:find_hostclass).returns(@klass) @resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope, :source => "source") @resource.stubs(:metaparam_compatibility_mode?).returns false @scope.stubs(:resource).returns @resource end it "should exist" do Puppet::Parser::Functions.function("require").should == "function_require" end it "should delegate to the 'include' puppet function" do @scope.expects(:function_include).with("myclass") @scope.function_require("myclass") end it "should set the 'require' prarameter on the resource to a resource reference" do @scope.stubs(:function_include) @scope.function_require("myclass") @resource["require"].should be_instance_of(Array) @resource["require"][0].should be_instance_of(Puppet::Resource) end it "should verify the 'include' function is loaded" do Puppet::Parser::Functions.expects(:function).with(:include).returns(:function_include) @scope.stubs(:function_include) @scope.function_require("myclass") end it "should include the class but not add a dependency if used on a client not at least version 0.25" do @resource.expects(:metaparam_compatibility_mode?).returns true @scope.expects(:warning) @resource.expects(:set_parameter).never @scope.expects(:function_include) @scope.function_require("myclass") end it "should lookup the absolute class path" do @scope.stubs(:function_include) @scope.expects(:find_hostclass).with("myclass").returns(@klass) @klass.expects(:name).returns("myclass") @scope.function_require("myclass") end it "should append the required class to the require parameter" do @scope.stubs(:function_include) one = Puppet::Resource.new(:file, "/one") @resource[:require] = one @scope.function_require("myclass") @resource[:require].should be_include(one) @resource[:require].detect { |r| r.to_s == "Class[Myclass]" }.should be_instance_of(Puppet::Resource) end end diff --git a/spec/unit/parser/functions/search_spec.rb b/spec/unit/parser/functions/search_spec.rb index fefb71425..81b774470 100755 --- a/spec/unit/parser/functions/search_spec.rb +++ b/spec/unit/parser/functions/search_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the 'search' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end let :scope do Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("search").should == "function_search" end it "should invoke #add_namespace on the scope for all inputs" do scope.expects(:add_namespace).with("where") scope.expects(:add_namespace).with("what") scope.expects(:add_namespace).with("who") scope.function_search(["where", "what", "who"]) end end diff --git a/spec/unit/parser/functions/shellquote_spec.rb b/spec/unit/parser/functions/shellquote_spec.rb index a06840b9e..862ac1182 100755 --- a/spec/unit/parser/functions/shellquote_spec.rb +++ b/spec/unit/parser/functions/shellquote_spec.rb @@ -1,69 +1,69 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the shellquote function" do before :all do Puppet::Parser::Functions.autoloader.loadall end let :scope do Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("shellquote").should == "function_shellquote" end it "should handle no arguments" do scope.function_shellquote([]).should == "" end it "should handle several simple arguments" do scope.function_shellquote( ['foo', 'bar@example.com', 'localhost:/dev/null', 'xyzzy+-4711,23'] ).should == 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23' end it "should handle array arguments" do scope.function_shellquote( ['foo', ['bar@example.com', 'localhost:/dev/null'], 'xyzzy+-4711,23'] ).should == 'foo bar@example.com localhost:/dev/null xyzzy+-4711,23' end it "should quote unsafe characters" do scope.function_shellquote(['/etc/passwd ', '(ls)', '*', '[?]', "'&'"]). should == '"/etc/passwd " "(ls)" "*" "[?]" "\'&\'"' end it "should deal with double quotes" do scope.function_shellquote(['"foo"bar"']).should == '\'"foo"bar"\'' end it "should cope with dollar signs" do scope.function_shellquote(['$PATH', 'foo$bar', '"x$"']). should == "'$PATH' 'foo$bar' '\"x$\"'" end it "should deal with apostrophes (single quotes)" do scope.function_shellquote(["'foo'bar'", "`$'EDITOR'`"]). should == '"\'foo\'bar\'" "\\`\\$\'EDITOR\'\\`"' end it "should cope with grave accents (backquotes)" do scope.function_shellquote(['`echo *`', '`ls "$MAILPATH"`']). should == "'`echo *`' '`ls \"$MAILPATH\"`'" end it "should deal with both single and double quotes" do scope.function_shellquote(['\'foo"bar"xyzzy\'', '"foo\'bar\'xyzzy"']). should == '"\'foo\\"bar\\"xyzzy\'" "\\"foo\'bar\'xyzzy\\""' end it "should handle multiple quotes *and* dollars and backquotes" do scope.function_shellquote(['\'foo"$x`bar`"xyzzy\'']). should == '"\'foo\\"\\$x\\`bar\\`\\"xyzzy\'"' end it "should handle linefeeds" do scope.function_shellquote(["foo \n bar"]).should == "\"foo \n bar\"" end end diff --git a/spec/unit/parser/functions/split_spec.rb b/spec/unit/parser/functions/split_spec.rb index 18a21a0cf..8b7052927 100755 --- a/spec/unit/parser/functions/split_spec.rb +++ b/spec/unit/parser/functions/split_spec.rb @@ -1,51 +1,51 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the split function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("split").should == "function_split" end it "should raise a ParseError if there is less than 2 arguments" do lambda { @scope.function_split(["foo"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 2 arguments" do lambda { @scope.function_split(["foo", "bar", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should raise a RegexpError if the regexp is malformed" do lambda { @scope.function_split(["foo", "("]) }.should( raise_error(RegexpError)) end it "should handle simple string without metacharacters" do result = @scope.function_split([ "130;236;254;10", ";"]) result.should(eql(["130", "236", "254", "10"])) end it "should handle simple regexps" do result = @scope.function_split([ "130.236;254.;10", "[.;]+"]) result.should(eql(["130", "236", "254", "10"])) end it "should handle groups" do result = @scope.function_split([ "130.236;254.;10", "([.;]+)"]) result.should(eql(["130", ".", "236", ";", "254", ".;", "10"])) end it "should handle simple string without metacharacters" do result = @scope.function_split([ "130.236.254.10", ";"]) result.should(eql(["130.236.254.10"])) end end diff --git a/spec/unit/parser/functions/sprintf_spec.rb b/spec/unit/parser/functions/sprintf_spec.rb index 3351c7fb3..5cc8d2d55 100755 --- a/spec/unit/parser/functions/sprintf_spec.rb +++ b/spec/unit/parser/functions/sprintf_spec.rb @@ -1,44 +1,44 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the sprintf function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("sprintf").should == "function_sprintf" end it "should raise a ParseError if there is less than 1 argument" do lambda { @scope.function_sprintf([]) }.should( raise_error(Puppet::ParseError)) end it "should format integers" do result = @scope.function_sprintf(["%+05d", "23"]) result.should(eql("+0023")) end it "should format floats" do result = @scope.function_sprintf(["%+.2f", "2.7182818284590451"]) result.should(eql("+2.72")) end it "should format large floats" do result = @scope.function_sprintf(["%+.2e", "27182818284590451"]) str = Puppet.features.microsoft_windows? ? "+2.72e+016" : "+2.72e+16" result.should(eql(str)) end it "should perform more complex formatting" do result = @scope.function_sprintf( [ "<%.8s:%#5o %#8X (%-8s)>", "overlongstring", "23", "48879", "foo" ]) result.should(eql("")) end end diff --git a/spec/unit/parser/functions/tag_spec.rb b/spec/unit/parser/functions/tag_spec.rb index e8a07e1bb..3210ccbc1 100755 --- a/spec/unit/parser/functions/tag_spec.rb +++ b/spec/unit/parser/functions/tag_spec.rb @@ -1,27 +1,27 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the 'tag' function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new @scope.stubs(:environment).returns(nil) end it "should exist" do Puppet::Parser::Functions.function(:tag).should == "function_tag" end it "should tag the resource with any provided tags" do resource = Puppet::Parser::Resource.new(:file, "/file", :scope => @scope) @scope.expects(:resource).returns resource @scope.function_tag ["one", "two"] resource.should be_tagged("one") resource.should be_tagged("two") end end diff --git a/spec/unit/parser/functions/template_spec.rb b/spec/unit/parser/functions/template_spec.rb index 0c8ebbd00..28b039529 100755 --- a/spec/unit/parser/functions/template_spec.rb +++ b/spec/unit/parser/functions/template_spec.rb @@ -1,97 +1,97 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the template function" do before :all do Puppet::Parser::Functions.autoloader.loadall end let :scope do Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("template").should == "function_template" end it "should create a TemplateWrapper when called" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.expects(:new).returns(tw) scope.function_template(["test"]) end it "should give the template filename to the TemplateWrapper" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.expects(:file=).with("test") scope.function_template(["test"]) end it "should return what TemplateWrapper.result returns" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.stubs(:file=).with("test") tw.expects(:result).returns("template contents evaluated") scope.function_template(["test"]).should == "template contents evaluated" end it "should concatenate template wrapper outputs for multiple templates" do tw1 = stub_everything "template_wrapper1" tw2 = stub_everything "template_wrapper2" Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw1,tw2) tw1.stubs(:file=).with("1") tw2.stubs(:file=).with("2") tw1.stubs(:result).returns("result1") tw2.stubs(:result).returns("result2") scope.function_template(["1","2"]).should == "result1result2" end it "should raise an error if the template raises an error" do tw = stub_everything 'template_wrapper' Puppet::Parser::TemplateWrapper.stubs(:new).returns(tw) tw.stubs(:result).raises expect { scope.function_template(["1"]) }.should raise_error(Puppet::ParseError) end def eval_template(content, *rest) File.stubs(:read).with("template").returns(content) Puppet::Parser::Files.stubs(:find_template).returns("template") scope.compiler.stubs(:environment).returns("production") scope.function_template(['template'] + rest) end it "should handle legacy template variable access correctly" do expect { eval_template("template <%= deprecated %>") }. to raise_error Puppet::ParseError end it "should get values from the scope correctly" do scope["deprecated"] = "deprecated value" eval_template("template <%= deprecated %>").should == "template deprecated value" end it "should handle kernel shadows without raising" do expect { eval_template("<%= binding %>") }.should_not raise_error end it "should not see scopes" do scope['myvar'] = 'this is yayness' expect { eval_template("<%= lookupvar('myvar') %>") }. to raise_error Puppet::ParseError end { "" => "", false => "false", true => "true" }.each do |input, output| it "should support defined variables (#{input.inspect} => #{output.inspect})" do scope['yayness'] = input expect { eval_template("<%= @yayness %>").should == output }.should_not raise_error end end end diff --git a/spec/unit/parser/functions/versioncmp_spec.rb b/spec/unit/parser/functions/versioncmp_spec.rb index 6fc724c38..34534570c 100755 --- a/spec/unit/parser/functions/versioncmp_spec.rb +++ b/spec/unit/parser/functions/versioncmp_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "the versioncmp function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("versioncmp").should == "function_versioncmp" end it "should raise a ParseError if there is less than 2 arguments" do lambda { @scope.function_versioncmp(["1.2"]) }.should raise_error(Puppet::ParseError) end it "should raise a ParseError if there is more than 2 arguments" do lambda { @scope.function_versioncmp(["1.2", "2.4.5", "3.5.6"]) }.should raise_error(Puppet::ParseError) end it "should call Puppet::Util::Package.versioncmp (included in scope)" do Puppet::Util::Package.expects(:versioncmp).with("1.2", "1.3").returns(-1) @scope.function_versioncmp(["1.2", "1.3"]) end end diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb index bd453402d..27ea24053 100755 --- a/spec/unit/parser/functions_spec.rb +++ b/spec/unit/parser/functions_spec.rb @@ -1,106 +1,106 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser::Functions do after(:each) do # Rationale: # our various tests will almost all register to Pupet::Parser::Functions # a new function called "name". All tests are required to stub Puppet::Parser::Scope # so that +no+ new real ruby method are defined. # After each test, we want to leave the whole Puppet::Parser::Functions environment # as it was before we were called, hence we call rmfunction (which might not succeed # if the function hasn't been registered in the test). It is also important in this # section to stub +remove_method+ here so that we don't pollute the scope. Puppet::Parser::Scope.stubs(:remove_method) begin Puppet::Parser::Functions.rmfunction("name") rescue end end it "should have a method for returning an environment-specific module" do Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.new("myenv")).should be_instance_of(Module) end it "should use the current default environment if no environment is provided" do Puppet::Parser::Functions.environment_module.should be_instance_of(Module) end it "should be able to retrieve environment modules asked for by name rather than instance" do Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.new("myenv")).should equal(Puppet::Parser::Functions.environment_module("myenv")) end describe "when calling newfunction" do before do @module = Module.new Puppet::Parser::Functions.stubs(:environment_module).returns @module end it "should create the function in the environment module" do @module.expects(:define_method).with { |name,block| name == "function_name" } Puppet::Parser::Functions.newfunction("name", :type => :rvalue) end it "should warn if the function already exists" do @module.expects(:define_method).with { |name,block| name == "function_name" }.twice Puppet::Parser::Functions.newfunction("name", :type => :rvalue) Puppet.expects(:warning) Puppet::Parser::Functions.newfunction("name", :type => :rvalue) end it "should raise an error if the function type is not correct" do @module.expects(:define_method).with { |name,block| name == "function_name" }.never lambda { Puppet::Parser::Functions.newfunction("name", :type => :unknown) }.should raise_error end end describe "when calling rmfunction" do before do @module = Module.new Puppet::Parser::Functions.stubs(:environment_module).returns @module end it "should remove the function in the scope class" do @module.expects(:define_method).with { |name,block| name == "function_name" } Puppet::Parser::Functions.newfunction("name", :type => :rvalue) @module.expects(:remove_method).with("function_name").once Puppet::Parser::Functions.rmfunction("name") end it "should raise an error if the function doesn't exists" do lambda { Puppet::Parser::Functions.rmfunction("name") }.should raise_error end end describe "when calling function to test function existance" do before do @module = Module.new Puppet::Parser::Functions.stubs(:environment_module).returns @module end it "should return false if the function doesn't exist" do Puppet::Parser::Functions.autoloader.stubs(:load) Puppet::Parser::Functions.function("name").should be_false end it "should return its name if the function exists" do @module.expects(:define_method).with { |name,block| name == "function_name" } Puppet::Parser::Functions.newfunction("name", :type => :rvalue) Puppet::Parser::Functions.function("name").should == "function_name" end it "should try to autoload the function if it doesn't exist yet" do Puppet::Parser::Functions.autoloader.expects(:load) Puppet::Parser::Functions.function("name") end end end diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb index d1a2938ba..f4733e7d5 100755 --- a/spec/unit/parser/lexer_spec.rb +++ b/spec/unit/parser/lexer_spec.rb @@ -1,743 +1,743 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/lexer' # This is a special matcher to match easily lexer output RSpec::Matchers.define :be_like do |*expected| match do |actual| expected.zip(actual).all? { |e,a| !e or a[0] == e or (e.is_a? Array and a[0] == e[0] and (a[1] == e[1] or (a[1].is_a?(Hash) and a[1][:value] == e[1]))) } end end __ = nil describe Puppet::Parser::Lexer do describe "when reading strings" do before { @lexer = Puppet::Parser::Lexer.new } it "should increment the line count for every carriage return in the string" do @lexer.line = 10 @lexer.string = "this\nis\natest'" @lexer.slurpstring("'") @lexer.line.should == 12 end it "should not increment the line count for escapes in the string" do @lexer.line = 10 @lexer.string = "this\\nis\\natest'" @lexer.slurpstring("'") @lexer.line.should == 10 end it "should not think the terminator is escaped, when preceeded by an even number of backslashes" do @lexer.line = 10 @lexer.string = "here\nis\nthe\nstring\\\\'with\nextra\njunk" @lexer.slurpstring("'") @lexer.line.should == 13 end end end describe Puppet::Parser::Lexer::Token do before do @token = Puppet::Parser::Lexer::Token.new(%r{something}, :NAME) end [:regex, :name, :string, :skip, :incr_line, :skip_text, :accumulate].each do |param| it "should have a #{param.to_s} reader" do @token.should be_respond_to(param) end it "should have a #{param.to_s} writer" do @token.should be_respond_to(param.to_s + "=") end end end describe Puppet::Parser::Lexer::Token, "when initializing" do it "should create a regex if the first argument is a string" do Puppet::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something} end it "should set the string if the first argument is one" do Puppet::Parser::Lexer::Token.new("something", :NAME).string.should == "something" end it "should set the regex if the first argument is one" do Puppet::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something} end end describe Puppet::Parser::Lexer::TokenList do before do @list = Puppet::Parser::Lexer::TokenList.new end it "should have a method for retrieving tokens by the name" do token = @list.add_token :name, "whatever" @list[:name].should equal(token) end it "should have a method for retrieving string tokens by the string" do token = @list.add_token :name, "whatever" @list.lookup("whatever").should equal(token) end it "should add tokens to the list when directed" do token = @list.add_token :name, "whatever" @list[:name].should equal(token) end it "should have a method for adding multiple tokens at once" do @list.add_tokens "whatever" => :name, "foo" => :bar @list[:name].should_not be_nil @list[:bar].should_not be_nil end it "should fail to add tokens sharing a name with an existing token" do @list.add_token :name, "whatever" lambda { @list.add_token :name, "whatever" }.should raise_error(ArgumentError) end it "should set provided options on tokens being added" do token = @list.add_token :name, "whatever", :skip_text => true token.skip_text.should == true end it "should define any provided blocks as a :convert method" do token = @list.add_token(:name, "whatever") do "foo" end token.convert.should == "foo" end it "should store all string tokens in the :string_tokens list" do one = @list.add_token(:name, "1") @list.string_tokens.should be_include(one) end it "should store all regex tokens in the :regex_tokens list" do one = @list.add_token(:name, %r{one}) @list.regex_tokens.should be_include(one) end it "should not store string tokens in the :regex_tokens list" do one = @list.add_token(:name, "1") @list.regex_tokens.should_not be_include(one) end it "should not store regex tokens in the :string_tokens list" do one = @list.add_token(:name, %r{one}) @list.string_tokens.should_not be_include(one) end it "should sort the string tokens inversely by length when asked" do one = @list.add_token(:name, "1") two = @list.add_token(:other, "12") @list.sort_tokens @list.string_tokens.should == [two, one] end end describe Puppet::Parser::Lexer::TOKENS do before do @lexer = Puppet::Parser::Lexer.new end { :LBRACK => '[', :RBRACK => ']', :LBRACE => '{', :RBRACE => '}', :LPAREN => '(', :RPAREN => ')', :EQUALS => '=', :ISEQUAL => '==', :GREATEREQUAL => '>=', :GREATERTHAN => '>', :LESSTHAN => '<', :LESSEQUAL => '<=', :NOTEQUAL => '!=', :NOT => '!', :COMMA => ',', :DOT => '.', :COLON => ':', :AT => '@', :LLCOLLECT => '<<|', :RRCOLLECT => '|>>', :LCOLLECT => '<|', :RCOLLECT => '|>', :SEMIC => ';', :QMARK => '?', :BACKSLASH => '\\', :FARROW => '=>', :PARROW => '+>', :APPENDS => '+=', :PLUS => '+', :MINUS => '-', :DIV => '/', :TIMES => '*', :LSHIFT => '<<', :RSHIFT => '>>', :MATCH => '=~', :NOMATCH => '!~', :IN_EDGE => '->', :OUT_EDGE => '<-', :IN_EDGE_SUB => '~>', :OUT_EDGE_SUB => '<~', }.each do |name, string| it "should have a token named #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].should_not be_nil end it "should match '#{string}' for the token #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].string.should == string end end { "case" => :CASE, "class" => :CLASS, "default" => :DEFAULT, "define" => :DEFINE, "import" => :IMPORT, "if" => :IF, "elsif" => :ELSIF, "else" => :ELSE, "inherits" => :INHERITS, "node" => :NODE, "and" => :AND, "or" => :OR, "undef" => :UNDEF, "false" => :FALSE, "true" => :TRUE, "in" => :IN, "unless" => :UNLESS, }.each do |string, name| it "should have a keyword named #{name.to_s}" do Puppet::Parser::Lexer::KEYWORDS[name].should_not be_nil end it "should have the keyword for #{name.to_s} set to #{string}" do Puppet::Parser::Lexer::KEYWORDS[name].string.should == string end end # These tokens' strings don't matter, just that the tokens exist. [:STRING, :DQPRE, :DQMID, :DQPOST, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :MLCOMMENT, :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name| it "should have a token named #{name.to_s}" do Puppet::Parser::Lexer::TOKENS[name].should_not be_nil end end end describe Puppet::Parser::Lexer::TOKENS[:CLASSREF] do before { @token = Puppet::Parser::Lexer::TOKENS[:CLASSREF] } it "should match against single upper-case alpha-numeric terms" do @token.regex.should =~ "One" end it "should match against upper-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "One::Two" end it "should match against many upper-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "One::Two::Three::Four::Five" end it "should match against upper-case alpha-numeric terms prefixed by double colons" do @token.regex.should =~ "::One" end end describe Puppet::Parser::Lexer::TOKENS[:NAME] do before { @token = Puppet::Parser::Lexer::TOKENS[:NAME] } it "should match against lower-case alpha-numeric terms" do @token.regex.should =~ "one-two" end it "should return itself and the value if the matched term is not a keyword" do Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil) @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] end it "should return the keyword token and the value if the matched term is a keyword" do keyword = stub 'keyword', :name => :testing Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) @token.convert(stub("lexer"), "myval").should == [keyword, "myval"] end it "should return the BOOLEAN token and 'true' if the matched term is the string 'true'" do keyword = stub 'keyword', :name => :TRUE Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) @token.convert(stub('lexer'), "true").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], true] end it "should return the BOOLEAN token and 'false' if the matched term is the string 'false'" do keyword = stub 'keyword', :name => :FALSE Puppet::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword) @token.convert(stub('lexer'), "false").should == [Puppet::Parser::Lexer::TOKENS[:BOOLEAN], false] end it "should match against lower-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "one::two" end it "should match against many lower-case alpha-numeric terms separated by double colons" do @token.regex.should =~ "one::two::three::four::five" end it "should match against lower-case alpha-numeric terms prefixed by double colons" do @token.regex.should =~ "::one" end it "should match against nested terms starting with numbers" do @token.regex.should =~ "::1one::2two::3three" end end describe Puppet::Parser::Lexer::TOKENS[:NUMBER] do before do @token = Puppet::Parser::Lexer::TOKENS[:NUMBER] @regex = @token.regex end it "should match against numeric terms" do @regex.should =~ "2982383139" end it "should match against float terms" do @regex.should =~ "29823.235" end it "should match against hexadecimal terms" do @regex.should =~ "0xBEEF0023" end it "should match against float with exponent terms" do @regex.should =~ "10e23" end it "should match against float terms with negative exponents" do @regex.should =~ "10e-23" end it "should match against float terms with fractional parts and exponent" do @regex.should =~ "1.234e23" end it "should return the NAME token and the value" do @token.convert(stub("lexer"), "myval").should == [Puppet::Parser::Lexer::TOKENS[:NAME], "myval"] end end describe Puppet::Parser::Lexer::TOKENS[:COMMENT] do before { @token = Puppet::Parser::Lexer::TOKENS[:COMMENT] } it "should match against lines starting with '#'" do @token.regex.should =~ "# this is a comment" end it "should be marked to get skipped" do @token.skip?.should be_true end it "should be marked to accumulate" do @token.accumulate?.should be_true end it "'s block should return the comment without the #" do @token.convert(@lexer,"# this is a comment")[1].should == "this is a comment" end end describe Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] do before do @token = Puppet::Parser::Lexer::TOKENS[:MLCOMMENT] @lexer = stub 'lexer', :line => 0 end it "should match against lines enclosed with '/*' and '*/'" do @token.regex.should =~ "/* this is a comment */" end it "should match multiple lines enclosed with '/*' and '*/'" do @token.regex.should =~ """/* this is a comment */""" end it "should increase the lexer current line number by the amount of lines spanned by the comment" do @lexer.expects(:line=).with(2) @token.convert(@lexer, "1\n2\n3") end it "should not greedily match comments" do match = @token.regex.match("/* first */ word /* second */") match[1].should == " first " end it "should be marked to accumulate" do @token.accumulate?.should be_true end it "'s block should return the comment without the comment marks" do @lexer.stubs(:line=).with(0) @token.convert(@lexer,"/* this is a comment */")[1].should == "this is a comment" end end describe Puppet::Parser::Lexer::TOKENS[:RETURN] do before { @token = Puppet::Parser::Lexer::TOKENS[:RETURN] } it "should match against carriage returns" do @token.regex.should =~ "\n" end it "should be marked to initiate text skipping" do @token.skip_text.should be_true end it "should be marked to increment the line" do @token.incr_line.should be_true end end shared_examples_for "variable names in the lexer" do |prefix| # Watch out - a regex might match a *prefix* on these, not just the whole # word, so make sure you don't have false positive or negative results based # on that. legal = %w{f foo f::b foo::b f::bar foo::bar 3 foo3 3foo} illegal = %w{f- f-o -f f::-o f::o- f::o-o} ["", "::"].each do |global_scope| legal.each do |name| var = prefix + global_scope + name it "should accept #{var.inspect} as a valid variable name" do (subject.regex.match(var) || [])[0].should == var end end illegal.each do |name| var = prefix + global_scope + name it "should NOT accept #{var.inspect} as a valid variable name" do (subject.regex.match(var) || [])[0].should_not == var end end end end describe Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] do its(:skip_text) { should be_false } its(:incr_line) { should be_false } it_should_behave_like "variable names in the lexer", '$' end describe Puppet::Parser::Lexer::TOKENS[:VARIABLE] do its(:skip_text) { should be_false } its(:incr_line) { should be_false } it_should_behave_like "variable names in the lexer", '' end def tokens_scanned_from(s) lexer = Puppet::Parser::Lexer.new lexer.string = s lexer.fullscan[0..-2] end describe Puppet::Parser::Lexer,"when lexing strings" do { %q{'single quoted string')} => [[:STRING,'single quoted string']], %q{"double quoted string"} => [[:STRING,'double quoted string']], %q{'single quoted string with an escaped "\\'"'} => [[:STRING,'single quoted string with an escaped "\'"']], %q{'single quoted string with an escaped "\$"'} => [[:STRING,'single quoted string with an escaped "\$"']], %q{'single quoted string with an escaped "\."'} => [[:STRING,'single quoted string with an escaped "\."']], %q{'single quoted string with an escaped "\n"'} => [[:STRING,'single quoted string with an escaped "\n"']], %q{'single quoted string with an escaped "\\\\"'} => [[:STRING,'single quoted string with an escaped "\\\\"']], %q{"string with an escaped '\\"'"} => [[:STRING,"string with an escaped '\"'"]], %q{"string with an escaped '\\$'"} => [[:STRING,"string with an escaped '$'"]], %Q{"string with a line ending with a backslash: \\\nfoo"} => [[:STRING,"string with a line ending with a backslash: foo"]], %q{"string with $v (but no braces)"} => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' (but no braces)']], %q["string with ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'v'],[:DQPOST,' in braces']], %q["string with ${qualified::var} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,'qualified::var'],[:DQPOST,' in braces']], %q{"string with $v and $v (but no braces)"} => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," (but no braces)"]], %q["string with ${v} and ${v} in braces"] => [[:DQPRE,"string with "],[:VARIABLE,"v"],[:DQMID," and "],[:VARIABLE,"v"],[:DQPOST," in braces"]], %q["string with ${'a nested single quoted string'} inside it."] => [[:DQPRE,"string with "],[:STRING,'a nested single quoted string'],[:DQPOST,' inside it.']], %q["string with ${['an array ',$v2]} in it."] => [[:DQPRE,"string with "],:LBRACK,[:STRING,"an array "],:COMMA,[:VARIABLE,"v2"],:RBRACK,[:DQPOST," in it."]], %q{a simple "scanner" test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"scanner"],[:NAME,"test"]], %q{a simple 'single quote scanner' test} => [[:NAME,"a"],[:NAME,"simple"], [:STRING,"single quote scanner"],[:NAME,"test"]], %q{a harder 'a $b \c"'} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,'a $b \c"']], %q{a harder "scanner test"} => [[:NAME,"a"],[:NAME,"harder"], [:STRING,"scanner test"]], %q{a hardest "scanner \"test\""} => [[:NAME,"a"],[:NAME,"hardest"],[:STRING,'scanner "test"']], %Q{a hardestest "scanner \\"test\\"\n"} => [[:NAME,"a"],[:NAME,"hardestest"],[:STRING,%Q{scanner "test"\n}]], %q{function("call")} => [[:NAME,"function"],[:LPAREN,"("],[:STRING,'call'],[:RPAREN,")"]], %q["string with ${(3+5)/4} nested math."] => [[:DQPRE,"string with "],:LPAREN,[:NAME,"3"],:PLUS,[:NAME,"5"],:RPAREN,:DIV,[:NAME,"4"],[:DQPOST," nested math."]], %q["$$$$"] => [[:STRING,"$$$$"]], %q["$variable"] => [[:DQPRE,""],[:VARIABLE,"variable"],[:DQPOST,""]], %q["$var$other"] => [[:DQPRE,""],[:VARIABLE,"var"],[:DQMID,""],[:VARIABLE,"other"],[:DQPOST,""]], %q["foo$bar$"] => [[:DQPRE,"foo"],[:VARIABLE,"bar"],[:DQPOST,"$"]], %q["foo$$bar"] => [[:DQPRE,"foo$"],[:VARIABLE,"bar"],[:DQPOST,""]], %q[""] => [[:STRING,""]], %q["123 456 789 0"] => [[:STRING,"123 456 789 0"]], %q["${123} 456 $0"] => [[:DQPRE,""],[:VARIABLE,"123"],[:DQMID," 456 "],[:VARIABLE,"0"],[:DQPOST,""]], %q["$foo::::bar"] => [[:DQPRE,""],[:VARIABLE,"foo"],[:DQPOST,"::::bar"]] }.each { |src,expected_result| it "should handle #{src} correctly" do tokens_scanned_from(src).should be_like(*expected_result) end } end describe Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] do before { @token = Puppet::Parser::Lexer::TOKENS[:DOLLAR_VAR] } it "should match against alpha words prefixed with '$'" do @token.regex.should =~ '$this_var' end it "should return the VARIABLE token and the variable name stripped of the '$'" do @token.convert(stub("lexer"), "$myval").should == [Puppet::Parser::Lexer::TOKENS[:VARIABLE], "myval"] end end describe Puppet::Parser::Lexer::TOKENS[:REGEX] do before { @token = Puppet::Parser::Lexer::TOKENS[:REGEX] } it "should match against any expression enclosed in //" do @token.regex.should =~ '/this is a regex/' end it 'should not match if there is \n in the regex' do @token.regex.should_not =~ "/this is \n a regex/" end describe "when scanning" do it "should not consider escaped slashes to be the end of a regex" do tokens_scanned_from("$x =~ /this \\/ foo/").should be_like(__,__,[:REGEX,%r{this / foo}]) end it "should not lex chained division as a regex" do tokens_scanned_from("$x = $a/$b/$c").collect { |name, data| name }.should_not be_include( :REGEX ) end it "should accept a regular expression after NODE" do tokens_scanned_from("node /www.*\.mysite\.org/").should be_like(__,[:REGEX,Regexp.new("www.*\.mysite\.org")]) end it "should accept regular expressions in a CASE" do s = %q{case $variable { "something": {$othervar = 4096 / 2} /regex/: {notice("this notably sucks")} } } tokens_scanned_from(s).should be_like( :CASE,:VARIABLE,:LBRACE,:STRING,:COLON,:LBRACE,:VARIABLE,:EQUALS,:NAME,:DIV,:NAME,:RBRACE,[:REGEX,/regex/],:COLON,:LBRACE,:NAME,:LPAREN,:STRING,:RPAREN,:RBRACE,:RBRACE ) end end it "should return the REGEX token and a Regexp" do @token.convert(stub("lexer"), "/myregex/").should == [Puppet::Parser::Lexer::TOKENS[:REGEX], Regexp.new(/myregex/)] end end describe Puppet::Parser::Lexer, "when lexing comments" do before { @lexer = Puppet::Parser::Lexer.new } it "should accumulate token in munge_token" do token = stub 'token', :skip => true, :accumulate? => true, :incr_line => nil, :skip_text => false token.stubs(:convert).with(@lexer, "# this is a comment").returns([token, " this is a comment"]) @lexer.munge_token(token, "# this is a comment") @lexer.munge_token(token, "# this is a comment") @lexer.getcomment.should == " this is a comment\n this is a comment\n" end it "should add a new comment stack level on LBRACE" do @lexer.string = "{" @lexer.expects(:commentpush) @lexer.fullscan end it "should add a new comment stack level on LPAREN" do @lexer.string = "(" @lexer.expects(:commentpush) @lexer.fullscan end it "should pop the current comment on RPAREN" do @lexer.string = ")" @lexer.expects(:commentpop) @lexer.fullscan end it "should return the current comments on getcomment" do @lexer.string = "# comment" @lexer.fullscan @lexer.getcomment.should == "comment\n" end it "should discard the previous comments on blank line" do @lexer.string = "# 1\n\n# 2" @lexer.fullscan @lexer.getcomment.should == "2\n" end it "should skip whitespace before lexing the next token after a non-token" do tokens_scanned_from("/* 1\n\n */ \ntest").should be_like([:NAME, "test"]) end it "should not return comments seen after the current line" do @lexer.string = "# 1\n\n# 2" @lexer.fullscan @lexer.getcomment(1).should == "" end it "should return a comment seen before the current line" do @lexer.string = "# 1\n# 2" @lexer.fullscan @lexer.getcomment(2).should == "1\n2\n" end end # FIXME: We need to rewrite all of these tests, but I just don't want to take the time right now. describe "Puppet::Parser::Lexer in the old tests" do before { @lexer = Puppet::Parser::Lexer.new } it "should do simple lexing" do { %q{\\} => [[:BACKSLASH,"\\"]], %q{simplest scanner test} => [[:NAME,"simplest"],[:NAME,"scanner"],[:NAME,"test"]], %Q{returned scanner test\n} => [[:NAME,"returned"],[:NAME,"scanner"],[:NAME,"test"]] }.each { |source,expected| tokens_scanned_from(source).should be_like(*expected) } end it "should fail usefully" do lambda { tokens_scanned_from('^') }.should raise_error(RuntimeError) end it "should fail if the string is not set" do lambda { @lexer.fullscan }.should raise_error(Puppet::LexError) end it "should correctly identify keywords" do tokens_scanned_from("case").should be_like([:CASE, "case"]) end it "should correctly parse class references" do %w{Many Different Words A Word}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF,t])} end # #774 it "should correctly parse namespaced class refernces token" do %w{Foo ::Foo Foo::Bar ::Foo::Bar}.each { |t| tokens_scanned_from(t).should be_like([:CLASSREF, t]) } end it "should correctly parse names" do %w{this is a bunch of names}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } end it "should correctly parse names with numerals" do %w{1name name1 11names names11}.each { |t| tokens_scanned_from(t).should be_like([:NAME,t]) } end it "should correctly parse empty strings" do lambda { tokens_scanned_from('$var = ""') }.should_not raise_error end it "should correctly parse virtual resources" do tokens_scanned_from("@type {").should be_like([:AT, "@"], [:NAME, "type"], [:LBRACE, "{"]) end it "should correctly deal with namespaces" do @lexer.string = %{class myclass} @lexer.fullscan @lexer.namespace.should == "myclass" @lexer.namepop @lexer.namespace.should == "" @lexer.string = "class base { class sub { class more" @lexer.fullscan @lexer.namespace.should == "base::sub::more" @lexer.namepop @lexer.namespace.should == "base::sub" end it "should not put class instantiation on the namespace" do @lexer.string = "class base { class sub { class { mode" @lexer.fullscan @lexer.namespace.should == "base::sub" end it "should correctly handle fully qualified names" do @lexer.string = "class base { class sub::more {" @lexer.fullscan @lexer.namespace.should == "base::sub::more" @lexer.namepop @lexer.namespace.should == "base" end it "should correctly lex variables" do ["$variable", "$::variable", "$qualified::variable", "$further::qualified::variable"].each do |string| tokens_scanned_from(string).should be_like([:VARIABLE,string.sub(/^\$/,'')]) end end it "should end variables at `-`" do tokens_scanned_from('$hyphenated-variable'). should be_like [:VARIABLE, "hyphenated"], [:MINUS, '-'], [:NAME, 'variable'] end it "should not include whitespace in a variable" do tokens_scanned_from("$foo bar").should_not be_like([:VARIABLE, "foo bar"]) end it "should not include excess colons in a variable" do tokens_scanned_from("$foo::::bar").should_not be_like([:VARIABLE, "foo::::bar"]) end end describe "Puppet::Parser::Lexer in the old tests when lexing example files" do my_fixtures('*.pp') do |file| it "should correctly lex #{file}" do lexer = Puppet::Parser::Lexer.new lexer.file = file expect { lexer.fullscan }.should_not raise_error end end end describe "when trying to lex an non-existent file" do include PuppetSpec::Files it "should return an empty list of tokens" do lexer = Puppet::Parser::Lexer.new lexer.file = nofile = tmpfile('lexer') File.exists?(nofile).should == false lexer.fullscan.should == [[false,false]] end end diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb index d25d88066..08923a378 100755 --- a/spec/unit/parser/parser_spec.rb +++ b/spec/unit/parser/parser_spec.rb @@ -1,520 +1,520 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Parser do Puppet::Parser::AST before :each do @known_resource_types = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @known_resource_types @true_ast = Puppet::Parser::AST::Boolean.new :value => true end it "should require an environment at initialization" do expect { Puppet::Parser::Parser.new }.should raise_error(ArgumentError) end it "should set the environment" do env = Puppet::Node::Environment.new Puppet::Parser::Parser.new(env).environment.should == env end it "should convert the environment into an environment instance if a string is provided" do env = Puppet::Node::Environment.new("testing") Puppet::Parser::Parser.new("testing").environment.should == env end it "should be able to look up the environment-specific resource type collection" do rtc = Puppet::Node::Environment.new("development").known_resource_types parser = Puppet::Parser::Parser.new "development" parser.known_resource_types.should equal(rtc) end context "when importing" do it "should delegate importing to the known resource type loader" do parser = Puppet::Parser::Parser.new "development" parser.known_resource_types.loader.expects(:import).with("newfile", "current_file") parser.lexer.expects(:file).returns "current_file" parser.import("newfile") end it "should import multiple files on one line" do @parser.known_resource_types.loader.expects(:import).with('one', nil) @parser.known_resource_types.loader.expects(:import).with('two', nil) @parser.parse("import 'one', 'two'") end end describe "when parsing files" do before do FileTest.stubs(:exist?).returns true File.stubs(:read).returns "" @parser.stubs(:watch_file) end it "should treat files ending in 'rb' as ruby files" do @parser.expects(:parse_ruby_file) @parser.file = "/my/file.rb" @parser.parse end end describe "when parsing append operator" do it "should not raise syntax errors" do expect { @parser.parse("$var += something") }.should_not raise_error end it "shouldraise syntax error on incomplete syntax " do expect { @parser.parse("$var += ") }.should raise_error end it "should create ast::VarDef with append=true" do vardef = @parser.parse("$var += 2").code[0] vardef.should be_a(Puppet::Parser::AST::VarDef) vardef.append.should == true end it "should work with arrays too" do vardef = @parser.parse("$var += ['test']").code[0] vardef.should be_a(Puppet::Parser::AST::VarDef) vardef.append.should == true end end describe "when parsing selector" do it "should support hash access on the left hand side" do expect { @parser.parse("$h = { 'a' => 'b' } $a = $h['a'] ? { 'b' => 'd', default => undef }") }.should_not raise_error end end describe "parsing 'unless'" do it "should create the correct ast objects" do Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } @parser.parse("unless false { $var = 1 }") end it "should not raise an error with empty statements" do expect { @parser.parse("unless false { }") }.should_not raise_error end #test for bug #13296 it "should not override 'unless' as a parameter inside resources" do lambda { @parser.parse("exec {'/bin/echo foo': unless => '/usr/bin/false',}") }.should_not raise_error end end describe "when parsing parameter names" do Puppet::Parser::Lexer::KEYWORDS.sort_tokens.each do |keyword| it "should allow #{keyword} as a keyword" do lambda { @parser.parse("exec {'/bin/echo foo': #{keyword} => '/usr/bin/false',}") }.should_not raise_error end end end describe "when parsing 'if'" do it "not, it should create the correct ast objects" do Puppet::Parser::AST::Not.expects(:new).with { |h| h[:value].is_a?(Puppet::Parser::AST::Boolean) } @parser.parse("if ! true { $var = 1 }") end it "boolean operation, it should create the correct ast objects" do Puppet::Parser::AST::BooleanOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Boolean) and h[:lval].is_a?(Puppet::Parser::AST::Boolean) and h[:operator]=="or" } @parser.parse("if true or true { $var = 1 }") end it "comparison operation, it should create the correct ast objects" do Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="<" } @parser.parse("if 1 < 2 { $var = 1 }") end end describe "when parsing if complex expressions" do it "should create a correct ast tree" do aststub = stub_everything 'ast' Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]==">" }.returns(aststub) Puppet::Parser::AST::ComparisonOperator.expects(:new).with { |h| h[:rval].is_a?(Puppet::Parser::AST::Name) and h[:lval].is_a?(Puppet::Parser::AST::Name) and h[:operator]=="==" }.returns(aststub) Puppet::Parser::AST::BooleanOperator.expects(:new).with { |h| h[:rval]==aststub and h[:lval]==aststub and h[:operator]=="and" } @parser.parse("if (1 > 2) and (1 == 2) { $var = 1 }") end it "should raise an error on incorrect expression" do expect { @parser.parse("if (1 > 2 > ) or (1 == 2) { $var = 1 }") }.should raise_error end end describe "when parsing resource references" do it "should not raise syntax errors" do expect { @parser.parse('exec { test: param => File["a"] }') }.should_not raise_error end it "should not raise syntax errors with multiple references" do expect { @parser.parse('exec { test: param => File["a","b"] }') }.should_not raise_error end it "should create an ast::ResourceReference" do Puppet::Parser::AST::ResourceReference.expects(:new).with { |arg| arg[:line]==1 and arg[:type]=="File" and arg[:title].is_a?(Puppet::Parser::AST::ASTArray) } @parser.parse('exec { test: command => File["a","b"] }') end end describe "when parsing resource overrides" do it "should not raise syntax errors" do expect { @parser.parse('Resource["title"] { param => value }') }.should_not raise_error end it "should not raise syntax errors with multiple overrides" do expect { @parser.parse('Resource["title1","title2"] { param => value }') }.should_not raise_error end it "should create an ast::ResourceOverride" do #Puppet::Parser::AST::ResourceOverride.expects(:new).with { |arg| # arg[:line]==1 and arg[:object].is_a?(Puppet::Parser::AST::ResourceReference) and arg[:parameters].is_a?(Puppet::Parser::AST::ResourceParam) #} ro = @parser.parse('Resource["title1","title2"] { param => value }').code[0] ro.should be_a(Puppet::Parser::AST::ResourceOverride) ro.line.should == 1 ro.object.should be_a(Puppet::Parser::AST::ResourceReference) ro.parameters[0].should be_a(Puppet::Parser::AST::ResourceParam) end end describe "when parsing if statements" do it "should not raise errors with empty if" do expect { @parser.parse("if true { }") }.should_not raise_error end it "should not raise errors with empty else" do expect { @parser.parse("if false { notice('if') } else { }") }.should_not raise_error end it "should not raise errors with empty if and else" do expect { @parser.parse("if false { } else { }") }.should_not raise_error end it "should create a nop node for empty branch" do Puppet::Parser::AST::Nop.expects(:new) @parser.parse("if true { }") end it "should create a nop node for empty else branch" do Puppet::Parser::AST::Nop.expects(:new) @parser.parse("if true { notice('test') } else { }") end it "should build a chain of 'ifs' if there's an 'elsif'" do expect { @parser.parse(<<-PP) }.should_not raise_error if true { notice('test') } elsif true {} else { } PP end end describe "when parsing function calls" do it "should not raise errors with no arguments" do expect { @parser.parse("tag()") }.should_not raise_error end it "should not raise errors with rvalue function with no args" do expect { @parser.parse("$a = template()") }.should_not raise_error end it "should not raise errors with arguments" do expect { @parser.parse("notice(1)") }.should_not raise_error end it "should not raise errors with multiple arguments" do expect { @parser.parse("notice(1,2)") }.should_not raise_error end it "should not raise errors with multiple arguments and a trailing comma" do expect { @parser.parse("notice(1,2,)") }.should_not raise_error end end describe "when parsing arrays" do it "should parse an array" do expect { @parser.parse("$a = [1,2]") }.should_not raise_error end it "should not raise errors with a trailing comma" do expect { @parser.parse("$a = [1,2,]") }.should_not raise_error end it "should accept an empty array" do expect { @parser.parse("$var = []\n") }.should_not raise_error end end describe "when providing AST context" do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer end it "should include the lexer's line" do @parser.ast_context[:line].should == 50 end it "should include the lexer's file" do @parser.ast_context[:file].should == "/foo/bar" end it "should include the docs if directed to do so" do @parser.ast_context(true)[:doc].should == "whev" end it "should not include the docs when told not to" do @parser.ast_context(false)[:doc].should be_nil end it "should not include the docs by default" do @parser.ast_context[:doc].should be_nil end end describe "when building ast nodes" do before do @lexer = stub 'lexer', :line => 50, :file => "/foo/bar", :getcomment => "whev" @parser.stubs(:lexer).returns @lexer @class = Puppet::Resource::Type.new(:hostclass, "myclass", :use_docs => false) end it "should return a new instance of the provided class created with the provided options" do @class.expects(:new).with { |opts| opts[:foo] == "bar" } @parser.ast(@class, :foo => "bar") end it "should merge the ast context into the provided options" do @class.expects(:new).with { |opts| opts[:file] == "/foo" } @parser.expects(:ast_context).returns :file => "/foo" @parser.ast(@class, :foo => "bar") end it "should prefer provided options over AST context" do @class.expects(:new).with { |opts| opts[:file] == "/bar" } @lexer.expects(:file).returns "/foo" @parser.ast(@class, :file => "/bar") end it "should include docs when the AST class uses them" do @class.expects(:use_docs).returns true @class.stubs(:new) @parser.expects(:ast_context).with{ |docs, line| docs == true }.returns({}) @parser.ast(@class, :file => "/bar") end it "should get docs from lexer using the correct AST line number" do @class.expects(:use_docs).returns true @class.stubs(:new).with{ |a| a[:doc] == "doc" } @lexer.expects(:getcomment).with(12).returns "doc" @parser.ast(@class, :file => "/bar", :line => 12) end end describe "when retrieving a specific node" do it "should delegate to the known_resource_types node" do @known_resource_types.expects(:node).with("node") @parser.node("node") end end describe "when retrieving a specific class" do it "should delegate to the loaded code" do @known_resource_types.expects(:hostclass).with("class") @parser.hostclass("class") end end describe "when retrieving a specific definitions" do it "should delegate to the loaded code" do @known_resource_types.expects(:definition).with("define") @parser.definition("define") end end describe "when determining the configuration version" do it "should determine it from the resource type collection" do @parser.known_resource_types.expects(:version).returns "foo" @parser.version.should == "foo" end end describe "when looking up definitions" do it "should use the known resource types to check for them by name" do @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:definition).returns(:this_value) @parser.find_definition("namespace","name").should == :this_value end end describe "when looking up hostclasses" do it "should use the known resource types to check for them by name" do @parser.known_resource_types.stubs(:find_or_load).with("namespace","name",:hostclass,{}).returns(:this_value) @parser.find_hostclass("namespace","name").should == :this_value end end describe "when parsing classes" do before :each do @krt = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @krt end it "should not create new classes" do @parser.parse("class foobar {}").code[0].should be_a(Puppet::Parser::AST::Hostclass) @krt.hostclass("foobar").should be_nil end it "should correctly set the parent class when one is provided" do @parser.parse("class foobar inherits yayness {}").code[0].instantiate('')[0].parent.should == "yayness" end it "should correctly set the parent class for multiple classes at a time" do statements = @parser.parse("class foobar inherits yayness {}\nclass boo inherits bar {}").code statements[0].instantiate('')[0].parent.should == "yayness" statements[1].instantiate('')[0].parent.should == "bar" end it "should define the code when some is provided" do @parser.parse("class foobar { $var = val }").code[0].code.should_not be_nil end it "should accept parameters with trailing comma" do @parser.parse("file { '/example': ensure => file, }").should be end it "should accept parametrized classes with trailing comma" do @parser.parse("class foobar ($var1 = 0,) { $var = val }").code[0].code.should_not be_nil end it "should define parameters when provided" do foobar = @parser.parse("class foobar($biz,$baz) {}").code[0].instantiate('')[0] foobar.arguments.should == {"biz" => nil, "baz" => nil} end end describe "when parsing resources" do before :each do @krt = Puppet::Resource::TypeCollection.new("development") @parser = Puppet::Parser::Parser.new "development" @parser.stubs(:known_resource_types).returns @krt end it "should be able to parse class resources" do @krt.add(Puppet::Resource::Type.new(:hostclass, "foobar", :arguments => {"biz" => nil})) expect { @parser.parse("class { foobar: biz => stuff }") }.should_not raise_error end it "should correctly mark exported resources as exported" do @parser.parse("@@file { '/file': }").code[0].exported.should be_true end it "should correctly mark virtual resources as virtual" do @parser.parse("@file { '/file': }").code[0].virtual.should be_true end end describe "when parsing nodes" do it "should be able to parse a node with a single name" do node = @parser.parse("node foo { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 1 node.names[0].value.should == "foo" end it "should be able to parse a node with two names" do node = @parser.parse("node foo, bar { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 2 node.names[0].value.should == "foo" node.names[1].value.should == "bar" end it "should be able to parse a node with three names" do node = @parser.parse("node foo, bar, baz { }").code[0] node.should be_a Puppet::Parser::AST::Node node.names.length.should == 3 node.names[0].value.should == "foo" node.names[1].value.should == "bar" node.names[2].value.should == "baz" end end it "should fail if trying to collect defaults" do expect { @parser.parse("@Port { protocols => tcp }") }. to raise_error Puppet::ParseError end context "when parsing collections" do it "should parse basic collections" do @parser.parse("Port <| |>").code. should be_all {|x| x.is_a? Puppet::Parser::AST::Collection } end it "should parse fully qualified collections" do @parser.parse("Port::Range <| |>").code. should be_all {|x| x.is_a? Puppet::Parser::AST::Collection } end end it "should not assign to a fully qualified variable" do expect { @parser.parse("$one::two = yay") }.to raise_error Puppet::ParseError end it "should parse assignment of undef" do tree = @parser.parse("$var = undef") tree.code.children[0].should be_an_instance_of Puppet::Parser::AST::VarDef tree.code.children[0].value.should be_an_instance_of Puppet::Parser::AST::Undef end context "#namesplit" do { "base::sub" => %w{base sub}, "main" => ["", "main"], "one::two::three::four" => ["one::two::three", "four"], }.each do |input, output| it "should split #{input.inspect} to #{output.inspect}" do @parser.namesplit(input).should == output end end end it "should treat classes as case insensitive" do @parser.known_resource_types.import_ast(@parser.parse("class yayness {}"), '') @parser.known_resource_types.hostclass('yayness'). should == @parser.find_hostclass("", "YayNess") end it "should treat defines as case insensitive" do @parser.known_resource_types.import_ast(@parser.parse("define funtest {}"), '') @parser.known_resource_types.hostclass('funtest'). should == @parser.find_hostclass("", "fUntEst") end end diff --git a/spec/unit/parser/relationship_spec.rb b/spec/unit/parser/relationship_spec.rb index 5a49831e1..0d1516f07 100755 --- a/spec/unit/parser/relationship_spec.rb +++ b/spec/unit/parser/relationship_spec.rb @@ -1,69 +1,69 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/relationship' describe Puppet::Parser::Relationship do before do @source = Puppet::Resource.new(:mytype, "source") @target = Puppet::Resource.new(:mytype, "target") @dep = Puppet::Parser::Relationship.new(@source, @target, :relationship) end describe "when evaluating" do before do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@source) @catalog.add_resource(@target) end it "should fail if the source resource cannot be found" do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @target lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @source lambda { @dep.evaluate(@catalog) }.should raise_error(ArgumentError) end it "should add the target as a 'before' value if the type is 'relationship'" do @dep.type = :relationship @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") end it "should add the target as a 'notify' value if the type is 'subscription'" do @dep.type = :subscription @dep.evaluate(@catalog) @source[:notify].should be_include("Mytype[target]") end it "should supplement rather than clobber existing relationship values" do @source[:before] = "File[/bar]" @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") @source[:before].should be_include("File[/bar]") end it "should use the collected retargets if the target is a Collector" do orig_target = @target @target = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) @target.collected[:foo] = @target @dep.evaluate(@catalog) @source[:before].should be_include("Mytype[target]") end it "should use the collected resources if the source is a Collector" do orig_source = @source @source = Puppet::Parser::Collector.new(stub("scope"), :file, "equery", "vquery", :virtual) @source.collected[:foo] = @source @dep.evaluate(@catalog) orig_source[:before].should be_include("Mytype[target]") end end end diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb index 59513a103..a089af853 100755 --- a/spec/unit/parser/resource_spec.rb +++ b/spec/unit/parser/resource_spec.rb @@ -1,640 +1,640 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' # LAK: FIXME This is just new tests for resources; I have # not moved all tests over yet. describe Puppet::Parser::Resource do before do @node = Puppet::Node.new("yaynode") @known_resource_types = Puppet::Resource::TypeCollection.new("env") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @source = newclass "" @scope = @compiler.topscope end def mkresource(args = {}) args[:source] ||= @source args[:scope] ||= @scope params = args[:parameters] || {:one => "yay", :three => "rah"} if args[:parameters] == :none args.delete(:parameters) elsif not args[:parameters].is_a? Array args[:parameters] = paramify(args[:source], params) end Puppet::Parser::Resource.new("resource", "testing", args) end def param(name, value, source) Puppet::Parser::Resource::Param.new(:name => name, :value => value, :source => source) end def paramify(source, hash) hash.collect do |name, value| Puppet::Parser::Resource::Param.new( :name => name, :value => value, :source => source ) end end def newclass(name) @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def newdefine(name) @known_resource_types.add Puppet::Resource::Type.new(:definition, name) end def newnode(name) @known_resource_types.add Puppet::Resource::Type.new(:node, name) end it "should use the file lookup module" do Puppet::Parser::Resource.ancestors.should be_include(Puppet::FileCollection::Lookup) end it "should get its environment from its scope" do scope = stub 'scope', :source => stub("source"), :namespaces => nil scope.expects(:environment).returns("foo").at_least_once Puppet::Parser::Resource.new("file", "whatever", :scope => scope).environment.should == "foo" end it "should use the resource type collection helper module" do Puppet::Parser::Resource.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should use the scope's environment as its environment" do @scope.expects(:environment).returns("myenv").at_least_once Puppet::Parser::Resource.new("file", "whatever", :scope => @scope).environment.should == "myenv" end it "should be isomorphic if it is builtin and models an isomorphic type" do Puppet::Type.type(:file).expects(:isomorphic?).returns(true) @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should not be isomorphic if it is builtin and models a non-isomorphic type" do Puppet::Type.type(:file).expects(:isomorphic?).returns(false) @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_false end it "should be isomorphic if it is not builtin" do newdefine "whatever" @resource = Puppet::Parser::Resource.new("whatever", "whatever", :scope => @scope, :source => @source).isomorphic?.should be_true end it "should have a array-indexing method for retrieving parameter values" do @resource = mkresource @resource[:one].should == "yay" end it "should use a Puppet::Resource for converting to a ral resource" do trans = mock 'resource', :to_ral => "yay" @resource = mkresource @resource.expects(:to_resource).returns trans @resource.to_ral.should == "yay" end it "should be able to use the indexing operator to access parameters" do resource = Puppet::Parser::Resource.new("resource", "testing", :source => "source", :scope => @scope) resource["foo"] = "bar" resource["foo"].should == "bar" end it "should return the title when asked for a parameter named 'title'" do Puppet::Parser::Resource.new("resource", "testing", :source => @source, :scope => @scope)[:title].should == "testing" end describe "when initializing" do before do @arguments = {:scope => @scope} end it "should fail unless #{name.to_s} is specified" do expect { Puppet::Parser::Resource.new('file', '/my/file') }. should raise_error(ArgumentError) end it "should set the reference correctly" do res = Puppet::Parser::Resource.new("resource", "testing", @arguments) res.ref.should == "Resource[testing]" end it "should be tagged with user tags" do tags = [ "tag1", "tag2" ] @arguments[:parameters] = [ param(:tag, tags , :source) ] res = Puppet::Parser::Resource.new("resource", "testing", @arguments) (res.tags & tags).should == tags end end describe "when evaluating" do before do @node = Puppet::Node.new "test-node" @compiler = Puppet::Parser::Compiler.new @node @catalog = Puppet::Resource::Catalog.new source = stub('source') source.stubs(:module_name) @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => source) @catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => @scope)) end it "should evaluate the associated AST definition" do definition = newdefine "mydefine" res = Puppet::Parser::Resource.new("mydefine", "whatever", :scope => @scope, :source => @source, :catalog => @catalog) definition.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST class" do @class = newclass "myclass" res = Puppet::Parser::Resource.new("class", "myclass", :scope => @scope, :source => @source, :catalog => @catalog) @class.expects(:evaluate_code).with(res) res.evaluate end it "should evaluate the associated AST node" do nodedef = newnode("mynode") res = Puppet::Parser::Resource.new("node", "mynode", :scope => @scope, :source => @source, :catalog => @catalog) nodedef.expects(:evaluate_code).with(res) res.evaluate end it "should add an edge to any specified stage for class resources" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) other_stage = Puppet::Parser::Resource.new(:stage, "other", :scope => @scope, :catalog => @catalog) @compiler.add_resource(@scope, other_stage) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) resource[:stage] = 'other' @compiler.add_resource(@scope, resource) resource.evaluate @compiler.catalog.edge?(other_stage, resource).should be_true end it "should fail if an unknown stage is specified" do @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) resource[:stage] = 'other' lambda { resource.evaluate }.should raise_error(ArgumentError, /Could not find stage other specified by/) end it "should add edges from the class resources to the parent's stage if no stage is specified" do main = @compiler.catalog.resource(:stage, :main) foo_stage = Puppet::Parser::Resource.new(:stage, :foo_stage, :scope => @scope, :catalog => @catalog) @compiler.add_resource(@scope, foo_stage) @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) resource[:stage] = 'foo_stage' @compiler.add_resource(@scope, resource) resource.evaluate @compiler.catalog.should be_edge(foo_stage, resource) end it "should allow edges to propagate multiple levels down the scope hierarchy" do Puppet[:code] = <<-MANIFEST stage { before: before => Stage[main] } class alpha { include beta } class beta { include gamma } class gamma { } class { alpha: stage => before } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone') # Stringify them to make for easier lookup edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]} edges.should include(["Stage[before]", "Class[Alpha]"]) edges.should include(["Stage[before]", "Class[Beta]"]) edges.should include(["Stage[before]", "Class[Gamma]"]) end it "should use the specified stage even if the parent scope specifies one" do Puppet[:code] = <<-MANIFEST stage { before: before => Stage[main], } stage { after: require => Stage[main], } class alpha { class { beta: stage => after } } class beta { } class { alpha: stage => before } MANIFEST catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new 'anyone') edges = catalog.edges.map {|e| [e.source.ref, e.target.ref]} edges.should include(["Stage[before]", "Class[Alpha]"]) edges.should include(["Stage[after]", "Class[Beta]"]) end it "should add edges from top-level class resources to the main stage if no stage is specified" do main = @compiler.catalog.resource(:stage, :main) @compiler.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "foo", {}) resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope, :catalog => @catalog) @compiler.add_resource(@scope, resource) resource.evaluate @compiler.catalog.should be_edge(main, resource) end end describe "when finishing" do before do @class = newclass "myclass" @nodedef = newnode("mynode") @resource = Puppet::Parser::Resource.new("file", "whatever", :scope => @scope, :source => @source) end it "should do nothing if it has already been finished" do @resource.finish @resource.expects(:add_metaparams).never @resource.finish end it "should add all defaults available from the scope" do @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => param(:owner, "default", @resource.source)) @resource.finish @resource[:owner].should == "default" end it "should not replace existing parameters with defaults" do @resource.set_parameter :owner, "oldvalue" @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => :replaced) @resource.finish @resource[:owner].should == "oldvalue" end it "should add a copy of each default, rather than the actual default parameter instance" do newparam = param(:owner, "default", @resource.source) other = newparam.dup other.value = "other" newparam.expects(:dup).returns(other) @resource.scope.expects(:lookupdefaults).with(@resource.type).returns(:owner => newparam) @resource.finish @resource[:owner].should == "other" end it "should be running in metaparam compatibility mode if running a version below 0.25" do catalog = stub 'catalog', :client_version => "0.24.8" @resource.stubs(:catalog).returns catalog @resource.should be_metaparam_compatibility_mode end it "should be running in metaparam compatibility mode if running no client version is available" do catalog = stub 'catalog', :client_version => nil @resource.stubs(:catalog).returns catalog @resource.should be_metaparam_compatibility_mode end it "should not be running in metaparam compatibility mode if running a version at or above 0.25" do catalog = stub 'catalog', :client_version => "0.25.0" @resource.stubs(:catalog).returns catalog @resource.should_not be_metaparam_compatibility_mode end it "should not copy relationship metaparams when not in metaparam compatibility mode" do @scope['require'] = "bar" @resource.stubs(:metaparam_compatibility_mode?).returns false @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should be_nil end it "should copy relationship metaparams when in metaparam compatibility mode" do @scope['require'] = "bar" @resource.stubs(:metaparam_compatibility_mode?).returns true @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should == "bar" end it "should stack relationship metaparams when in metaparam compatibility mode" do @resource.set_parameter("require", "foo") @scope['require'] = "bar" @resource.stubs(:metaparam_compatibility_mode?).returns true @resource.class.publicize_methods(:add_metaparams) { @resource.add_metaparams } @resource["require"].should == ["foo", "bar"] end end describe "when being tagged" do before do @scope_resource = stub 'scope_resource', :tags => %w{srone srtwo} @scope.stubs(:resource).returns @scope_resource @resource = Puppet::Parser::Resource.new("file", "yay", :scope => @scope, :source => mock('source')) end it "should get tagged with the resource type" do @resource.tags.should be_include("file") end it "should get tagged with the title" do @resource.tags.should be_include("yay") end it "should get tagged with each name in the title if the title is a qualified class name" do resource = Puppet::Parser::Resource.new("file", "one::two", :scope => @scope, :source => mock('source')) resource.tags.should be_include("one") resource.tags.should be_include("two") end it "should get tagged with each name in the type if the type is a qualified class name" do resource = Puppet::Parser::Resource.new("one::two", "whatever", :scope => @scope, :source => mock('source')) resource.tags.should be_include("one") resource.tags.should be_include("two") end it "should not get tagged with non-alphanumeric titles" do resource = Puppet::Parser::Resource.new("file", "this is a test", :scope => @scope, :source => mock('source')) resource.tags.should_not be_include("this is a test") end it "should fail on tags containing '*' characters" do lambda { @resource.tag("bad*tag") }.should raise_error(Puppet::ParseError) end it "should fail on tags starting with '-' characters" do lambda { @resource.tag("-badtag") }.should raise_error(Puppet::ParseError) end it "should fail on tags containing ' ' characters" do lambda { @resource.tag("bad tag") }.should raise_error(Puppet::ParseError) end it "should allow alpha tags" do lambda { @resource.tag("good_tag") }.should_not raise_error(Puppet::ParseError) end end describe "when merging overrides" do before do @source = "source1" @resource = mkresource :source => @source @override = mkresource :source => @source end it "should fail when the override was not created by a parent class" do @override.source = "source2" @override.source.expects(:child_of?).with("source1").returns(false) lambda { @resource.merge(@override) }.should raise_error(Puppet::ParseError) end it "should succeed when the override was created in the current scope" do @resource.source = "source3" @override.source = @resource.source @override.source.expects(:child_of?).with("source3").never params = {:a => :b, :c => :d} @override.expects(:parameters).returns(params) @resource.expects(:override_parameter).with(:b) @resource.expects(:override_parameter).with(:d) @resource.merge(@override) end it "should succeed when a parent class created the override" do @resource.source = "source3" @override.source = "source4" @override.source.expects(:child_of?).with("source3").returns(true) params = {:a => :b, :c => :d} @override.expects(:parameters).returns(params) @resource.expects(:override_parameter).with(:b) @resource.expects(:override_parameter).with(:d) @resource.merge(@override) end it "should add new parameters when the parameter is not set" do @source.stubs(:child_of?).returns true @override.set_parameter(:testing, "value") @resource.merge(@override) @resource[:testing].should == "value" end it "should replace existing parameter values" do @source.stubs(:child_of?).returns true @resource.set_parameter(:testing, "old") @override.set_parameter(:testing, "value") @resource.merge(@override) @resource[:testing].should == "value" end it "should add values to the parameter when the override was created with the '+>' syntax" do @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "testing", :source => @resource.source) param.add = true @override.set_parameter(param) @resource.set_parameter(:testing, "other") @resource.merge(@override) @resource[:testing].should == %w{other testing} end it "should not merge parameter values when multiple resources are overriden with '+>' at once " do @resource_2 = mkresource :source => @source @resource. set_parameter(:testing, "old_val_1") @resource_2.set_parameter(:testing, "old_val_2") @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :testing, :value => "new_val", :source => @resource.source) param.add = true @override.set_parameter(param) @resource. merge(@override) @resource_2.merge(@override) @resource [:testing].should == %w{old_val_1 new_val} @resource_2[:testing].should == %w{old_val_2 new_val} end it "should promote tag overrides to real tags" do @source.stubs(:child_of?).returns true param = Puppet::Parser::Resource::Param.new(:name => :tag, :value => "testing", :source => @resource.source) @override.set_parameter(param) @resource.merge(@override) @resource.tagged?("testing").should be_true end end it "should be able to be converted to a normal resource" do @source = stub 'scope', :name => "myscope" @resource = mkresource :source => @source @resource.should respond_to(:to_resource) end describe "when being converted to a resource" do before do @parser_resource = mkresource :scope => @scope, :parameters => {:foo => "bar", :fee => "fum"} end it "should create an instance of Puppet::Resource" do @parser_resource.to_resource.should be_instance_of(Puppet::Resource) end it "should set the type correctly on the Puppet::Resource" do @parser_resource.to_resource.type.should == @parser_resource.type end it "should set the title correctly on the Puppet::Resource" do @parser_resource.to_resource.title.should == @parser_resource.title end it "should copy over all of the parameters" do result = @parser_resource.to_resource.to_hash # The name will be in here, also. result[:foo].should == "bar" result[:fee].should == "fum" end it "should copy over the tags" do @parser_resource.tag "foo" @parser_resource.tag "bar" @parser_resource.to_resource.tags.should == @parser_resource.tags end it "should copy over the line" do @parser_resource.line = 40 @parser_resource.to_resource.line.should == 40 end it "should copy over the file" do @parser_resource.file = "/my/file" @parser_resource.to_resource.file.should == "/my/file" end it "should copy over the 'exported' value" do @parser_resource.exported = true @parser_resource.to_resource.exported.should be_true end it "should copy over the 'virtual' value" do @parser_resource.virtual = true @parser_resource.to_resource.virtual.should be_true end it "should convert any parser resource references to Puppet::Resource instances" do ref = Puppet::Resource.new("file", "/my/file") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ref} result = @parser_resource.to_resource result[:fee].should == Puppet::Resource.new(:file, "/my/file") end it "should convert any parser resource references to Puppet::Resource instances even if they are in an array" do ref = Puppet::Resource.new("file", "/my/file") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", ref]} result = @parser_resource.to_resource result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file")] end it "should convert any parser resource references to Puppet::Resource instances even if they are in an array of array, and even deeper" do ref1 = Puppet::Resource.new("file", "/my/file1") ref2 = Puppet::Resource.new("file", "/my/file2") @parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", [ref1,ref2]]} result = @parser_resource.to_resource result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file1"), Puppet::Resource.new(:file, "/my/file2")] end it "should fail if the same param is declared twice" do lambda do @parser_resource = mkresource :source => @source, :parameters => [ Puppet::Parser::Resource::Param.new( :name => :foo, :value => "bar", :source => @source ), Puppet::Parser::Resource::Param.new( :name => :foo, :value => "baz", :source => @source ) ] end.should raise_error(Puppet::ParseError) end end describe "when validating" do it "should check each parameter" do resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") resource[:one] = :two resource[:three] = :four resource.expects(:validate_parameter).with(:one) resource.expects(:validate_parameter).with(:three) resource.send(:validate) end it "should raise a parse error when there's a failure" do resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => stub("source") resource[:one] = :two resource.expects(:validate_parameter).with(:one).raises ArgumentError lambda { resource.send(:validate) }.should raise_error(Puppet::ParseError) end end describe "when setting parameters" do before do @source = newclass "foobar" @resource = Puppet::Parser::Resource.new :foo, "bar", :scope => @scope, :source => @source end it "should accept Param instances and add them to the parameter list" do param = Puppet::Parser::Resource::Param.new :name => "foo", :value => "bar", :source => @source @resource.set_parameter(param) @resource["foo"].should == "bar" end it "should fail when provided a parameter name but no value" do lambda { @resource.set_parameter("myparam") }.should raise_error(ArgumentError) end it "should allow parameters to be set to 'false'" do @resource.set_parameter("myparam", false) @resource["myparam"].should be_false end it "should use its source when provided a parameter name and value" do @resource.set_parameter("myparam", "myvalue") @resource["myparam"].should == "myvalue" end end # part of #629 -- the undef keyword. Make sure 'undef' params get skipped. it "should not include 'undef' parameters when converting itself to a hash" do resource = Puppet::Parser::Resource.new "file", "/tmp/testing", :source => mock("source"), :scope => mock("scope") resource[:owner] = :undef resource[:mode] = "755" resource.to_hash[:owner].should be_nil end end diff --git a/spec/unit/parser/scope_spec.rb b/spec/unit/parser/scope_spec.rb index 412b7f50e..cce894be7 100755 --- a/spec/unit/parser/scope_spec.rb +++ b/spec/unit/parser/scope_spec.rb @@ -1,521 +1,521 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet_spec/compiler' describe Puppet::Parser::Scope do before :each do @scope = Puppet::Parser::Scope.new @scope.compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo")) @scope.source = Puppet::Resource::Type.new(:node, :foo) @topscope = @scope.compiler.topscope @scope.parent = @topscope end it "should be able to retrieve class scopes by name" do @scope.class_set "myname", "myscope" @scope.class_scope("myname").should == "myscope" end it "should be able to retrieve class scopes by object" do klass = mock 'ast_class' klass.expects(:name).returns("myname") @scope.class_set "myname", "myscope" @scope.class_scope(klass).should == "myscope" end it "should be able to retrieve its parent module name from the source of its parent type" do @topscope.source = Puppet::Resource::Type.new(:hostclass, :foo, :module_name => "foo") @scope.parent_module_name.should == "foo" end it "should return a nil parent module name if it has no parent" do @topscope.parent_module_name.should be_nil end it "should return a nil parent module name if its parent has no source" do @scope.parent_module_name.should be_nil end it "should get its environment from its compiler" do env = Puppet::Node::Environment.new compiler = stub 'compiler', :environment => env scope = Puppet::Parser::Scope.new :compiler => compiler scope.environment.should equal(env) end it "should use the default environment if none is available" do Puppet::Parser::Scope.new.environment.should equal(Puppet::Node::Environment.new) end it "should use the resource type collection helper to find its known resource types" do Puppet::Parser::Scope.ancestors.should include(Puppet::Resource::TypeCollectionHelper) end describe "when missing methods are called" do before :each do @env = Puppet::Node::Environment.new('testing') @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new('foo', :environment => @env)) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) end it "should load and call the method if it looks like a function and it exists" do @scope.function_sprintf(["%b", 123]).should == "1111011" end it "should raise NoMethodError if the method doesn't look like a function" do expect { @scope.sprintf(["%b", 123]) }.should raise_error(NoMethodError) end it "should raise NoMethodError if the method looks like a function but doesn't exist" do expect { @scope.function_fake_bs(['cows']) }.should raise_error(NoMethodError) end end describe "when initializing" do it "should extend itself with its environment's Functions module as well as the default" do env = Puppet::Node::Environment.new("myenv") root = Puppet::Node::Environment.root compiler = stub 'compiler', :environment => env scope = Puppet::Parser::Scope.new(:compiler => compiler) scope.singleton_class.ancestors.should be_include(Puppet::Parser::Functions.environment_module(env)) scope.singleton_class.ancestors.should be_include(Puppet::Parser::Functions.environment_module(root)) end it "should extend itself with the default Functions module if its environment is the default" do root = Puppet::Node::Environment.root scope = Puppet::Parser::Scope.new scope.singleton_class.ancestors.should be_include(Puppet::Parser::Functions.environment_module(root)) end end describe "when looking up a variable" do it "should support :lookupvar and :setvar for backward compatibility" do @scope.setvar("var", "yep") @scope.lookupvar("var").should == "yep" end it "should return nil for unset variables" do @scope["var"].should be_nil end it "should be able to look up values" do @scope["var"] = "yep" @scope["var"].should == "yep" end it "should be able to look up hashes" do @scope["var"] = {"a" => "b"} @scope["var"].should == {"a" => "b"} end it "should be able to look up variables in parent scopes" do @topscope["var"] = "parentval" @scope["var"].should == "parentval" end it "should prefer its own values to parent values" do @topscope["var"] = "parentval" @scope["var"] = "childval" @scope["var"].should == "childval" end it "should be able to detect when variables are set" do @scope["var"] = "childval" @scope.should be_include("var") end it "does not allow changing a set value" do @scope["var"] = "childval" expect { @scope["var"] = "change" }.should raise_error(Puppet::Error, "Cannot reassign variable var") end it "should be able to detect when variables are not set" do @scope.should_not be_include("var") end it "should support iteration over its variables" do @scope["one"] = "two" @scope["three"] = "four" hash = {} @scope.each { |name, value| hash[name] = value } hash.should == {"one" => "two", "three" => "four" } end it "should include Enumerable" do @scope.singleton_class.ancestors.should be_include(Enumerable) end describe "and the variable is qualified" do before :each do @known_resource_types = @scope.known_resource_types end def newclass(name) @known_resource_types.add Puppet::Resource::Type.new(:hostclass, name) end def create_class_scope(name) klass = newclass(name) catalog = Puppet::Resource::Catalog.new catalog.add_resource(Puppet::Parser::Resource.new("stage", :main, :scope => Puppet::Parser::Scope.new)) Puppet::Parser::Resource.new("class", name, :scope => @scope, :source => mock('source'), :catalog => catalog).evaluate @scope.class_scope(klass) end it "should be able to look up explicitly fully qualified variables from main" do Puppet.expects(:deprecation_warning).never other_scope = create_class_scope("") other_scope["othervar"] = "otherval" @scope["::othervar"].should == "otherval" end it "should be able to look up explicitly fully qualified variables from other scopes" do Puppet.expects(:deprecation_warning).never other_scope = create_class_scope("other") other_scope["var"] = "otherval" @scope["::other::var"].should == "otherval" end it "should be able to look up deeply qualified variables" do Puppet.expects(:deprecation_warning).never other_scope = create_class_scope("other::deep::klass") other_scope["var"] = "otherval" @scope["other::deep::klass::var"].should == "otherval" end it "should return nil for qualified variables that cannot be found in other classes" do other_scope = create_class_scope("other::deep::klass") @scope["other::deep::klass::var"].should be_nil end it "should warn and return nil for qualified variables whose classes have not been evaluated" do klass = newclass("other::deep::klass") @scope.expects(:warning) @scope["other::deep::klass::var"].should be_nil end it "should warn and return nil for qualified variables whose classes do not exist" do @scope.expects(:warning) @scope["other::deep::klass::var"].should be_nil end it "should return nil when asked for a non-string qualified variable from a class that does not exist" do @scope.stubs(:warning) @scope["other::deep::klass::var"].should be_nil end it "should return nil when asked for a non-string qualified variable from a class that has not been evaluated" do @scope.stubs(:warning) klass = newclass("other::deep::klass") @scope["other::deep::klass::var"].should be_nil end end end describe "when variables are set with append=true" do it "should raise error if the variable is already defined in this scope" do @scope.setvar("var", "1", :append => false) expect { @scope.setvar("var", "1", :append => true) }.should raise_error(Puppet::ParseError, "Cannot append, variable var is defined in this scope") end it "should lookup current variable value" do @scope.expects(:[]).with("var").returns("2") @scope.setvar("var", "1", :append => true) end it "should store the concatenated string '42'" do @topscope.setvar("var", "4", :append => false) @scope.setvar("var", "2", :append => true) @scope["var"].should == "42" end it "should store the concatenated array [4,2]" do @topscope.setvar("var", [4], :append => false) @scope.setvar("var", [2], :append => true) @scope["var"].should == [4,2] end it "should store the merged hash {a => b, c => d}" do @topscope.setvar("var", {"a" => "b"}, :append => false) @scope.setvar("var", {"c" => "d"}, :append => true) @scope["var"].should == {"a" => "b", "c" => "d"} end it "should raise an error when appending a hash with something other than another hash" do @topscope.setvar("var", {"a" => "b"}, :append => false) expect { @scope.setvar("var", "not a hash", :append => true) }.should raise_error(ArgumentError, "Trying to append to a hash with something which is not a hash is unsupported") end end describe "when calling number?" do it "should return nil if called with anything not a number" do Puppet::Parser::Scope.number?([2]).should be_nil end it "should return a Fixnum for a Fixnum" do Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) end it "should return a Float for a Float" do Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) end it "should return 234 for '234'" do Puppet::Parser::Scope.number?("234").should == 234 end it "should return nil for 'not a number'" do Puppet::Parser::Scope.number?("not a number").should be_nil end it "should return 23.4 for '23.4'" do Puppet::Parser::Scope.number?("23.4").should == 23.4 end it "should return 23.4e13 for '23.4e13'" do Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 end it "should understand negative numbers" do Puppet::Parser::Scope.number?("-234").should == -234 end it "should know how to convert exponential float numbers ala '23e13'" do Puppet::Parser::Scope.number?("23e13").should == 23e13 end it "should understand hexadecimal numbers" do Puppet::Parser::Scope.number?("0x234").should == 0x234 end it "should understand octal numbers" do Puppet::Parser::Scope.number?("0755").should == 0755 end it "should return nil on malformed integers" do Puppet::Parser::Scope.number?("0.24.5").should be_nil end it "should convert strings with leading 0 to integer if they are not octal" do Puppet::Parser::Scope.number?("0788").should == 788 end it "should convert strings of negative integers" do Puppet::Parser::Scope.number?("-0788").should == -788 end it "should return nil on malformed hexadecimal numbers" do Puppet::Parser::Scope.number?("0x89g").should be_nil end end describe "when using ephemeral variables" do it "should store the variable value" do @scope.setvar("1", :value, :ephemeral => true) @scope["1"].should == :value end it "should remove the variable value when unset_ephemeral_var is called" do @scope.setvar("1", :value, :ephemeral => true) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var @scope["1"].should be_nil end it "should not remove classic variables when unset_ephemeral_var is called" do @scope['myvar'] = :value1 @scope.setvar("1", :value2, :ephemeral => true) @scope.stubs(:parent).returns(nil) @scope.unset_ephemeral_var @scope["myvar"].should == :value1 end it "should raise an error when setting it again" do @scope.setvar("1", :value2, :ephemeral => true) expect { @scope.setvar("1", :value3, :ephemeral => true) }.should raise_error end it "should declare ephemeral number only variable names" do @scope.ephemeral?("0").should be_true end it "should not declare ephemeral other variable names" do @scope.ephemeral?("abc0").should be_nil end describe "with more than one level" do it "should prefer latest ephemeral scopes" do @scope.setvar("0", :earliest, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :latest, :ephemeral => true) @scope["0"].should == :latest end it "should be able to report the current level" do @scope.ephemeral_level.should == 1 @scope.new_ephemeral @scope.ephemeral_level.should == 2 end it "should check presence of an ephemeral variable accross multiple levels" do @scope.new_ephemeral @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :value2, :ephemeral => true) @scope.new_ephemeral @scope.ephemeral_include?("1").should be_true end it "should return false when an ephemeral variable doesn't exist in any ephemeral scope" do @scope.new_ephemeral @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :value2, :ephemeral => true) @scope.new_ephemeral @scope.ephemeral_include?("2").should be_false end it "should get ephemeral values from earlier scope when not in later" do @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("0", :value2, :ephemeral => true) @scope["1"].should == :value1 end describe "when calling unset_ephemeral_var without a level" do it "should remove all the variables values" do @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("1", :value2, :ephemeral => true) @scope.unset_ephemeral_var @scope["1"].should be_nil end end describe "when calling unset_ephemeral_var with a level" do it "should remove ephemeral scopes up to this level" do @scope.setvar("1", :value1, :ephemeral => true) @scope.new_ephemeral @scope.setvar("1", :value2, :ephemeral => true) @scope.new_ephemeral @scope.setvar("1", :value3, :ephemeral => true) @scope.unset_ephemeral_var(2) @scope["1"].should == :value2 end end end end describe "when setting ephemeral vars from matches" do before :each do @match = stub 'match', :is_a? => true @match.stubs(:[]).with(0).returns("this is a string") @match.stubs(:captures).returns([]) @scope.stubs(:setvar) end it "should accept only MatchData" do expect { @scope.ephemeral_from("match") }.should raise_error end it "should set $0 with the full match" do @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] } @scope.ephemeral_from(@match) end it "should set every capture as ephemeral var" do @match.stubs(:captures).returns([:capture1,:capture2]) @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] } @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] } @scope.ephemeral_from(@match) end it "should create a new ephemeral level" do @scope.expects(:new_ephemeral) @scope.ephemeral_from(@match) end end it "should use its namespaces to find hostclasses" do klass = @scope.known_resource_types.add Puppet::Resource::Type.new(:hostclass, "a::b::c") @scope.add_namespace "a::b" @scope.find_hostclass("c").should equal(klass) end it "should use its namespaces to find definitions" do define = @scope.known_resource_types.add Puppet::Resource::Type.new(:definition, "a::b::c") @scope.add_namespace "a::b" @scope.find_definition("c").should equal(define) end describe "when managing defaults" do it "should be able to set and lookup defaults" do param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param) @scope.lookupdefaults(:mytype).should == {:myparam => param} end it "should fail if a default is already defined and a new default is being defined" do param = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param) expect { @scope.define_settings(:mytype, param) }.should raise_error(Puppet::ParseError) end it "should return multiple defaults at once" do param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param1) param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param2) @scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} end it "should look up defaults defined in parent scopes" do param1 = Puppet::Parser::Resource::Param.new(:name => :myparam, :value => "myvalue", :source => stub("source")) @scope.define_settings(:mytype, param1) child_scope = @scope.newscope param2 = Puppet::Parser::Resource::Param.new(:name => :other, :value => "myvalue", :source => stub("source")) child_scope.define_settings(:mytype, param2) child_scope.lookupdefaults(:mytype).should == {:myparam => param1, :other => param2} end end context "#true?" do { "a string" => true, "true" => true, "false" => true, true => true, "" => false, :undef => false }.each do |input, output| it "should treat #{input.inspect} as #{output}" do Puppet::Parser::Scope.true?(input).should == output end end end end diff --git a/spec/unit/parser/templatewrapper_spec.rb b/spec/unit/parser/templatewrapper_spec.rb index 6080346fb..22fb1c771 100755 --- a/spec/unit/parser/templatewrapper_spec.rb +++ b/spec/unit/parser/templatewrapper_spec.rb @@ -1,140 +1,140 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/templatewrapper' describe Puppet::Parser::TemplateWrapper do before(:each) do @known_resource_types = Puppet::Resource::TypeCollection.new("env") @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types @scope = Puppet::Parser::Scope.new :compiler => @compiler @file = "fake_template" Puppet::Parser::Files.stubs(:find_template).returns("/tmp/fake_template") FileTest.stubs(:exists?).returns("true") File.stubs(:read).with("/tmp/fake_template").returns("template content") @tw = Puppet::Parser::TemplateWrapper.new(@scope) end def mock_template(source=nil) template_mock = mock("template", :result => "woot!") ERB.expects(:new).with("template contents", 0, "-").returns(template_mock) template_mock.expects(:filename=).with(source) end it "should create a new object TemplateWrapper from a scope" do tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.should be_a_kind_of(Puppet::Parser::TemplateWrapper) end it "should check template file existance and read its content" do Puppet::Parser::Files.expects(:find_template).with("fake_template", @scope.environment.to_s).returns("/tmp/fake_template") File.expects(:read).with("/tmp/fake_template").returns("template content") @tw.file = @file end it "should mark the file for watching" do Puppet::Parser::Files.expects(:find_template).returns("/tmp/fake_template") File.stubs(:read) @known_resource_types.expects(:watch_file).with("/tmp/fake_template") @tw.file = @file end it "should fail if a template cannot be found" do Puppet::Parser::Files.expects(:find_template).returns nil lambda { @tw.file = @file }.should raise_error(Puppet::ParseError) end it "should turn into a string like template[name] for file based template" do @tw.file = @file @tw.to_s.should eql("template[/tmp/fake_template]") end it "should turn into a string like template[inline] for string-based template" do @tw.to_s.should eql("template[inline]") end it "should return the processed template contents with a call to result" do mock_template("/tmp/fake_template") File.expects(:read).with("/tmp/fake_template").returns("template contents") @tw.file = @file @tw.result.should eql("woot!") end it "should return the processed template contents with a call to result and a string" do mock_template @tw.result("template contents").should eql("woot!") end it "should return the contents of a variable if called via method_missing" do @scope["chicken"] = "is good" tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.chicken.should eql("is good") end it "should throw an exception if a variable is called via method_missing and it does not exist" do tw = Puppet::Parser::TemplateWrapper.new(@scope) lambda { tw.chicken }.should raise_error(Puppet::ParseError) end it "should allow you to check whether a variable is defined with has_variable?" do @scope["chicken"] = "is good" tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.has_variable?("chicken").should eql(true) end it "should allow you to check whether a variable is not defined with has_variable?" do tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.has_variable?("chicken").should eql(false) end it "should allow you to retrieve the defined classes with classes" do catalog = mock 'catalog', :classes => ["class1", "class2"] @scope.expects(:catalog).returns( catalog ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.classes.should == ["class1", "class2"] end it "should allow you to retrieve all the tags with all_tags" do catalog = mock 'catalog', :tags => ["tag1", "tag2"] @scope.expects(:catalog).returns( catalog ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.all_tags.should == ["tag1","tag2"] end it "should allow you to retrieve the tags defined in the current scope" do @scope.expects(:tags).returns( ["tag1", "tag2"] ) tw = Puppet::Parser::TemplateWrapper.new(@scope) tw.tags.should == ["tag1","tag2"] end it "should set all of the scope's variables as instance variables" do mock_template @scope.expects(:to_hash).returns("one" => "foo") @tw.result("template contents") @tw.instance_variable_get("@one").should == "foo" end it "should not error out if one of the variables is a symbol" do mock_template @scope.expects(:to_hash).returns(:_timestamp => "1234") @tw.result("template contents") end %w{! . ; :}.each do |badchar| it "should translate #{badchar} to _ when setting the instance variables" do mock_template @scope.expects(:to_hash).returns("one#{badchar}" => "foo") @tw.result("template contents") @tw.instance_variable_get("@one_").should == "foo" end end end diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb index 8b139613a..8e59f53ea 100755 --- a/spec/unit/parser/type_loader_spec.rb +++ b/spec/unit/parser/type_loader_spec.rb @@ -1,230 +1,230 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/parser/type_loader' require 'puppet_spec/files' describe Puppet::Parser::TypeLoader do include PuppetSpec::Files before do @loader = Puppet::Parser::TypeLoader.new(:myenv) end it "should support an environment" do loader = Puppet::Parser::TypeLoader.new(:myenv) loader.environment.name.should == :myenv end it "should include the Environment Helper" do @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper) end it "should delegate its known resource types to its environment" do @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) end describe "when loading names from namespaces" do it "should do nothing if the name to import is an empty string" do @loader.expects(:name2files).never @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil end it "should attempt to import each generated name" do @loader.expects(:import).with("foo/bar",nil).returns([]) @loader.expects(:import).with("foo",nil).returns([]) @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false } end end describe "when importing" do before do Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}] Puppet::Parser::Parser.any_instance.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) Puppet::Parser::Parser.any_instance.stubs(:file=) end it "should return immediately when imports are being ignored" do Puppet::Parser::Files.expects(:find_manifests).never Puppet[:ignoreimport] = true @loader.import("foo").should be_nil end it "should find all manifests matching the file or pattern" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}] @loader.import("myfile") end it "should use the directory of the current file if one is set" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == make_absolute("/current") }.returns ["modname", %w{one}] @loader.import("myfile", make_absolute("/current/file")) end it "should pass the environment when looking for files" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}] @loader.import("myfile") end it "should fail if no files are found" do Puppet::Parser::Files.expects(:find_manifests).returns [nil, []] lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError) end it "should parse each found file" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", [make_absolute("/one")]] @loader.expects(:parse_file).with(make_absolute("/one")).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") end it "should make each file qualified before attempting to parse it" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] @loader.expects(:parse_file).with(make_absolute("/current/one")).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile", make_absolute("/current/file")) end it "should not attempt to import files that have already been imported" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] Puppet::Parser::Parser.any_instance.expects(:parse).once.returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") # This will fail if it tries to reimport the file. @loader.import("myfile") end end describe "when importing all" do before do @base = tmpdir("base") # Create two module path directories @modulebase1 = File.join(@base, "first") FileUtils.mkdir_p(@modulebase1) @modulebase2 = File.join(@base, "second") FileUtils.mkdir_p(@modulebase2) Puppet[:modulepath] = "#{@modulebase1}#{File::PATH_SEPARATOR}#{@modulebase2}" end def mk_module(basedir, name) module_dir = File.join(basedir, name) # Go ahead and make our manifest directory FileUtils.mkdir_p(File.join(module_dir, "manifests")) return Puppet::Module.new(name) end # We have to pass the base path so that we can # write to modules that are in the second search path def mk_manifests(base, mod, type, files) exts = {"ruby" => ".rb", "puppet" => ".pp"} files.collect do |file| name = mod.name + "::" + file.gsub("/", "::") path = File.join(base, mod.name, "manifests", file + exts[type]) FileUtils.mkdir_p(File.split(path)[0]) # write out the class if type == "ruby" File.open(path, "w") { |f| f.print "hostclass '#{name}' do\nend" } else File.open(path, "w") { |f| f.print "class #{name} {}" } end name end end it "should load all puppet manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "puppet", %w{a b}) mk_manifests(@modulebase2, @module2, "puppet", %w{c d}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should load all ruby manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "ruby", %w{a b}) mk_manifests(@modulebase2, @module2, "ruby", %w{c d}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should not load manifests from duplicate modules later in the module path" do @module1 = mk_module(@modulebase1, "one") # duplicate @module2 = mk_module(@modulebase2, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a}) mk_manifests(@modulebase2, @module2, "puppet", %w{c}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::c").should be_nil end it "should load manifests from subdirectories" do @module1 = mk_module(@modulebase1, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a a/b a/b/c}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::a::b::c").should be_instance_of(Puppet::Resource::Type) end end describe "when parsing a file" do before do @parser = Puppet::Parser::Parser.new(@loader.environment) @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @parser.stubs(:file=) Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser end it "should create a new parser instance for each file using the current environment" do Puppet::Parser::Parser.expects(:new).with(@loader.environment).returns @parser @loader.parse_file("/my/file") end it "should assign the parser its file and parse" do @parser.expects(:file=).with("/my/file") @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.parse_file("/my/file") end end it "should be able to add classes to the current resource type collection" do file = tmpfile("simple_file.pp") File.open(file, "w") { |f| f.puts "class foo {}" } @loader.import(file) @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type) end describe "when deciding where to look for files" do { 'foo' => ['foo'], 'foo::bar' => ['foo/bar', 'foo'], 'foo::bar::baz' => ['foo/bar/baz', 'foo/bar', 'foo'] }.each do |fqname, expected_paths| it "should look for #{fqname.inspect} in #{expected_paths.inspect}" do @loader.instance_eval { name2files(fqname) }.should == expected_paths end end end end diff --git a/spec/unit/property/ensure_spec.rb b/spec/unit/property/ensure_spec.rb index 35151553b..80e1c5fa2 100755 --- a/spec/unit/property/ensure_spec.rb +++ b/spec/unit/property/ensure_spec.rb @@ -1,12 +1,12 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/property/ensure' klass = Puppet::Property::Ensure describe klass do it "should be a subclass of Property" do klass.superclass.must == Puppet::Property end end diff --git a/spec/unit/property/keyvalue_spec.rb b/spec/unit/property/keyvalue_spec.rb index 2bead50e3..01d05a242 100755 --- a/spec/unit/property/keyvalue_spec.rb +++ b/spec/unit/property/keyvalue_spec.rb @@ -1,170 +1,170 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/property/keyvalue' klass = Puppet::Property::KeyValue describe klass do it "should be a subclass of Property" do klass.superclass.must == Puppet::Property end describe "as an instance" do before do # Wow that's a messy interface to the resource. klass.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = klass.new(:resource => @resource) end it "should have a , as default delimiter" do @property.delimiter.should == ";" end it "should have a = as default separator" do @property.separator.should == "=" end it "should have a :membership as default membership" do @property.membership.should == :key_value_membership end it "should return the same value passed into should_to_s" do @property.should_to_s({:foo => "baz", :bar => "boo"}) == "foo=baz;bar=boo" end it "should return the passed in hash values joined with the delimiter from is_to_s" do s = @property.is_to_s({"foo" => "baz" , "bar" => "boo"}) # We can't predict the order the hash is processed in... ["foo=baz;bar=boo", "bar=boo;foo=baz"].should be_include s end describe "when calling inclusive?" do it "should use the membership method to look up on the @resource" do @property.expects(:membership).returns(:key_value_membership) @resource.expects(:[]).with(:key_value_membership) @property.inclusive? end it "should return true when @resource[membership] == inclusive" do @property.stubs(:membership).returns(:key_value_membership) @resource.stubs(:[]).with(:key_value_membership).returns(:inclusive) @property.inclusive?.must == true end it "should return false when @resource[membership] != inclusive" do @property.stubs(:membership).returns(:key_value_membership) @resource.stubs(:[]).with(:key_value_membership).returns(:minimum) @property.inclusive?.must == false end end describe "when calling process_current_hash" do it "should return {} if hash is :absent" do @property.process_current_hash(:absent).must == {} end it "should set every key to nil if inclusive?" do @property.stubs(:inclusive?).returns(true) @property.process_current_hash({:foo => "bar", :do => "re"}).must == { :foo => nil, :do => nil } end it "should return the hash if !inclusive?" do @property.stubs(:inclusive?).returns(false) @property.process_current_hash({:foo => "bar", :do => "re"}).must == {:foo => "bar", :do => "re"} end end describe "when calling should" do it "should return nil if @should is nil" do @property.should.must == nil end it "should call process_current_hash" do @property.should = ["foo=baz", "bar=boo"] @property.stubs(:retrieve).returns({:do => "re", :mi => "fa" }) @property.expects(:process_current_hash).returns({}) @property.should end it "should return the hashed values of @should and the nilled values of retrieve if inclusive" do @property.should = ["foo=baz", "bar=boo"] @property.expects(:retrieve).returns({:do => "re", :mi => "fa" }) @property.expects(:inclusive?).returns(true) @property.should.must == { :foo => "baz", :bar => "boo", :do => nil, :mi => nil } end it "should return the hashed @should + the unique values of retrieve if !inclusive" do @property.should = ["foo=baz", "bar=boo"] @property.expects(:retrieve).returns({:foo => "diff", :do => "re", :mi => "fa"}) @property.expects(:inclusive?).returns(false) @property.should.must == { :foo => "baz", :bar => "boo", :do => "re", :mi => "fa" } end end describe "when calling retrieve" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) end it "should send 'name' to the provider" do @provider.expects(:send).with(:keys) @property.expects(:name).returns(:keys) @property.retrieve end it "should return a hash with the provider returned info" do @provider.stubs(:send).with(:keys).returns({"do" => "re", "mi" => "fa" }) @property.stubs(:name).returns(:keys) @property.retrieve == {"do" => "re", "mi" => "fa" } end it "should return :absent when the provider returns :absent" do @provider.stubs(:send).with(:keys).returns(:absent) @property.stubs(:name).returns(:keys) @property.retrieve == :absent end end describe "when calling hashify" do it "should return the array hashified" do @property.hashify(["foo=baz", "bar=boo"]).must == { :foo => "baz", :bar => "boo" } end end describe "when calling safe_insync?" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) @property.stubs(:name).returns(:prop_name) end it "should return true unless @should is defined and not nil" do @property.safe_insync?("foo") == true end it "should return true if the passed in values is nil" do @property.should = "foo" @property.safe_insync?(nil) == true end it "should return true if hashified should value == (retrieved) value passed in" do @provider.stubs(:prop_name).returns({ :foo => "baz", :bar => "boo" }) @property.should = ["foo=baz", "bar=boo"] @property.expects(:inclusive?).returns(true) @property.safe_insync?({ :foo => "baz", :bar => "boo" }).must == true end it "should return false if prepared value != should value" do @provider.stubs(:prop_name).returns({ "foo" => "bee", "bar" => "boo" }) @property.should = ["foo=baz", "bar=boo"] @property.expects(:inclusive?).returns(true) @property.safe_insync?({ "foo" => "bee", "bar" => "boo" }).must == false end end end end diff --git a/spec/unit/property/list_spec.rb b/spec/unit/property/list_spec.rb index a29d65751..bd7d9651d 100755 --- a/spec/unit/property/list_spec.rb +++ b/spec/unit/property/list_spec.rb @@ -1,165 +1,165 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/property/list' list_class = Puppet::Property::List describe list_class do it "should be a subclass of Property" do list_class.superclass.must == Puppet::Property end describe "as an instance" do before do # Wow that's a messy interface to the resource. list_class.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = list_class.new(:resource => @resource) end it "should have a , as default delimiter" do @property.delimiter.should == "," end it "should have a :membership as default membership" do @property.membership.should == :membership end it "should return the same value passed into should_to_s" do @property.should_to_s("foo") == "foo" end it "should return the passed in array values joined with the delimiter from is_to_s" do @property.is_to_s(["foo","bar"]).should == "foo,bar" end it "should be able to correctly convert ':absent' to a string" do @property.is_to_s(:absent).should == "absent" end describe "when adding should to current" do it "should add the arrays when current is an array" do @property.add_should_with_current(["foo"], ["bar"]).should == ["foo", "bar"] end it "should return should if current is not a array" do @property.add_should_with_current(["foo"], :absent).should == ["foo"] end it "should return only the uniq elements" do @property.add_should_with_current(["foo", "bar"], ["foo", "baz"]).should == ["foo", "bar", "baz"] end end describe "when calling inclusive?" do it "should use the membership method to look up on the @resource" do @property.expects(:membership).returns(:membership) @resource.expects(:[]).with(:membership) @property.inclusive? end it "should return true when @resource[membership] == inclusive" do @property.stubs(:membership).returns(:membership) @resource.stubs(:[]).with(:membership).returns(:inclusive) @property.inclusive?.must == true end it "should return false when @resource[membership] != inclusive" do @property.stubs(:membership).returns(:membership) @resource.stubs(:[]).with(:membership).returns(:minimum) @property.inclusive?.must == false end end describe "when calling should" do it "should return nil if @should is nil" do @property.should.must == nil end it "should return the sorted values of @should as a string if inclusive" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(true) @property.should.must == "bar,foo" end it "should return the uniq sorted values of @should + retrieve as a string if !inclusive" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(false) @property.expects(:retrieve).returns(["foo","baz"]) @property.should.must == "bar,baz,foo" end end describe "when calling retrieve" do before do @provider = mock("provider") @property.stubs(:provider).returns(@provider) end it "should send 'name' to the provider" do @provider.expects(:send).with(:group) @property.expects(:name).returns(:group) @property.retrieve end it "should return an array with the provider returned info" do @provider.stubs(:send).with(:group).returns("foo,bar,baz") @property.stubs(:name).returns(:group) @property.retrieve == ["foo", "bar", "baz"] end it "should return :absent when the provider returns :absent" do @provider.stubs(:send).with(:group).returns(:absent) @property.stubs(:name).returns(:group) @property.retrieve == :absent end end describe "when calling safe_insync?" do it "should return true unless @should is defined and not nil" do @property.must be_safe_insync("foo") end it "should return true unless the passed in values is not nil" do @property.should = "foo" @property.must be_safe_insync(nil) end it "should call prepare_is_for_comparison with value passed in and should" do @property.should = "foo" @property.expects(:prepare_is_for_comparison).with("bar") @property.expects(:should) @property.safe_insync?("bar") end it "should return true if 'is' value is array of comma delimited should values" do @property.should = "bar,foo" @property.expects(:inclusive?).returns(true) @property.must be_safe_insync(["bar","foo"]) end it "should return true if 'is' value is :absent and should value is empty string" do @property.should = "" @property.expects(:inclusive?).returns(true) @property.must be_safe_insync([]) end it "should return false if prepared value != should value" do @property.should = "bar,baz,foo" @property.expects(:inclusive?).returns(true) @property.must_not be_safe_insync(["bar","foo"]) end end describe "when calling dearrayify" do it "should sort and join the array with 'delimiter'" do array = mock "array" array.expects(:sort).returns(array) array.expects(:join).with(@property.delimiter) @property.dearrayify(array) end end end end diff --git a/spec/unit/property/ordered_list_spec.rb b/spec/unit/property/ordered_list_spec.rb index 2ad05367c..c605fd98d 100755 --- a/spec/unit/property/ordered_list_spec.rb +++ b/spec/unit/property/ordered_list_spec.rb @@ -1,63 +1,63 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/property/ordered_list' ordered_list_class = Puppet::Property::OrderedList describe ordered_list_class do it "should be a subclass of List" do ordered_list_class.superclass.must == Puppet::Property::List end describe "as an instance" do before do # Wow that's a messy interface to the resource. ordered_list_class.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = ordered_list_class.new(:resource => @resource) end describe "when adding should to current" do it "should add the arrays when current is an array" do @property.add_should_with_current(["should"], ["current"]).should == ["should", "current"] end it "should return 'should' if current is not a array" do @property.add_should_with_current(["should"], :absent).should == ["should"] end it "should return only the uniq elements leading with the order of 'should'" do @property.add_should_with_current(["this", "is", "should"], ["is", "this", "current"]).should == ["this", "is", "should", "current"] end end describe "when calling should" do it "should return nil if @should is nil" do @property.should.must == nil end it "should return the values of @should (without sorting) as a string if inclusive" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(true) @property.should.must == "foo,bar" end it "should return the uniq values of @should + retrieve as a string if !inclusive with the @ values leading" do @property.should = ["foo", "bar"] @property.expects(:inclusive?).returns(false) @property.expects(:retrieve).returns(["foo","baz"]) @property.should.must == "foo,bar,baz" end end describe "when calling dearrayify" do it "should join the array with the delimiter" do array = mock "array" array.expects(:join).with(@property.delimiter) @property.dearrayify(array) end end end end diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index 3f25b743b..6a1db2264 100755 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -1,510 +1,510 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/property' describe Puppet::Property do let :resource do Puppet::Type.type(:host).new :name => "foo" end let :subclass do # We need a completely fresh subclass every time, because we modify both # class and instance level things inside the tests. subclass = Class.new(Puppet::Property) do @name = :foo end subclass.initvars subclass end let :property do subclass.new :resource => resource end it "should be able to look up the modified name for a given value" do subclass.newvalue(:foo) subclass.value_name("foo").should == :foo end it "should be able to look up the modified name for a given value matching a regex" do subclass.newvalue(%r{.}) subclass.value_name("foo").should == %r{.} end it "should be able to look up a given value option" do subclass.newvalue(:foo, :event => :whatever) subclass.value_option(:foo, :event).should == :whatever end it "should be able to specify required features" do subclass.should respond_to(:required_features=) end {"one" => [:one],:one => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value| it "should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})" do subclass.required_features = in_value subclass.required_features.should == out_value end } it "should return its name as a string when converted to a string" do property.to_s.should == property.name.to_s end it "should be able to shadow metaparameters" do property.must respond_to(:shadow) end describe "when returning the default event name" do it "should use the current 'should' value to pick the event name" do property.expects(:should).returns "myvalue" subclass.expects(:value_option).with('myvalue', :event).returns :event_name property.event_name end it "should return any event defined with the specified value" do property.expects(:should).returns :myval subclass.expects(:value_option).with(:myval, :event).returns :event_name property.event_name.should == :event_name end describe "and the property is 'ensure'" do before :each do property.stubs(:name).returns :ensure resource.expects(:type).returns :mytype end it "should use _created if the 'should' value is 'present'" do property.expects(:should).returns :present property.event_name.should == :mytype_created end it "should use _removed if the 'should' value is 'absent'" do property.expects(:should).returns :absent property.event_name.should == :mytype_removed end it "should use _changed if the 'should' value is not 'absent' or 'present'" do property.expects(:should).returns :foo property.event_name.should == :mytype_changed end it "should use _changed if the 'should value is nil" do property.expects(:should).returns nil property.event_name.should == :mytype_changed end end it "should use _changed if the property is not 'ensure'" do property.stubs(:name).returns :myparam property.expects(:should).returns :foo property.event_name.should == :myparam_changed end it "should use _changed if no 'should' value is set" do property.stubs(:name).returns :myparam property.expects(:should).returns nil property.event_name.should == :myparam_changed end end describe "when creating an event" do before :each do property.stubs(:should).returns "myval" end it "should use an event from the resource as the base event" do event = Puppet::Transaction::Event.new resource.expects(:event).returns event property.event.should equal(event) end it "should have the default event name" do property.expects(:event_name).returns :my_event property.event.name.should == :my_event end it "should have the property's name" do property.event.property.should == property.name.to_s end it "should have the 'should' value set" do property.stubs(:should).returns "foo" property.event.desired_value.should == "foo" end it "should provide its path as the source description" do property.stubs(:path).returns "/my/param" property.event.source_description.should == "/my/param" end end describe "when shadowing metaparameters" do let :shadow_class do shadow_class = Class.new(Puppet::Property) do @name = :alias end shadow_class.initvars shadow_class end it "should create an instance of the metaparameter at initialization" do Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => resource) shadow_class.new :resource => resource end it "should munge values using the shadow's munge method" do shadow = mock 'shadow' Puppet::Type.metaparamclass(:alias).expects(:new).returns shadow shadow.expects(:munge).with "foo" property = shadow_class.new :resource => resource property.munge("foo") end end describe "when defining new values" do it "should define a method for each value created with a block that's not a regex" do subclass.newvalue(:foo) { } property.must respond_to(:set_foo) end end describe "when assigning the value" do it "should just set the 'should' value" do property.value = "foo" property.should.must == "foo" end it "should validate each value separately" do property.expects(:validate).with("one") property.expects(:validate).with("two") property.value = %w{one two} end it "should munge each value separately and use any result as the actual value" do property.expects(:munge).with("one").returns :one property.expects(:munge).with("two").returns :two # Do this so we get the whole array back. subclass.array_matching = :all property.value = %w{one two} property.should.must == [:one, :two] end it "should return any set value" do (property.value = :one).should == :one end end describe "when returning the value" do it "should return nil if no value is set" do property.should.must be_nil end it "should return the first set 'should' value if :array_matching is set to :first" do subclass.array_matching = :first property.should = %w{one two} property.should.must == "one" end it "should return all set 'should' values as an array if :array_matching is set to :all" do subclass.array_matching = :all property.should = %w{one two} property.should.must == %w{one two} end it "should default to :first array_matching" do subclass.array_matching.should == :first end it "should unmunge the returned value if :array_matching is set to :first" do property.class.unmunge do |v| v.to_sym end subclass.array_matching = :first property.should = %w{one two} property.should.must == :one end it "should unmunge all the returned values if :array_matching is set to :all" do property.class.unmunge do |v| v.to_sym end subclass.array_matching = :all property.should = %w{one two} property.should.must == [:one, :two] end end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do lambda { property.should = "foo" }.should_not raise_error end it "should fail if the value is not a defined value or alias and does not match a regex" do subclass.newvalue(:foo) lambda { property.should = "bar" }.should raise_error end it "should succeeed if the value is one of the defined values" do subclass.newvalue(:foo) lambda { property.should = :foo }.should_not raise_error end it "should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do subclass.newvalue(:foo) lambda { property.should = "foo" }.should_not raise_error end it "should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do subclass.newvalue("foo") lambda { property.should = :foo }.should_not raise_error end it "should succeed if the value is one of the defined aliases" do subclass.newvalue("foo") subclass.aliasvalue("bar", "foo") lambda { property.should = :bar }.should_not raise_error end it "should succeed if the value matches one of the regexes" do subclass.newvalue(/./) lambda { property.should = "bar" }.should_not raise_error end it "should validate that all required features are present" do subclass.newvalue(:foo, :required_features => [:a, :b]) resource.provider.expects(:satisfies?).with([:a, :b]).returns true property.should = :foo end it "should fail if required features are missing" do subclass.newvalue(:foo, :required_features => [:a, :b]) resource.provider.expects(:satisfies?).with([:a, :b]).returns false lambda { property.should = :foo }.should raise_error(Puppet::Error) end it "should internally raise an ArgumentError if required features are missing" do subclass.newvalue(:foo, :required_features => [:a, :b]) resource.provider.expects(:satisfies?).with([:a, :b]).returns false lambda { property.validate_features_per_value :foo }.should raise_error(ArgumentError) end it "should validate that all required features are present for regexes" do value = subclass.newvalue(/./, :required_features => [:a, :b]) resource.provider.expects(:satisfies?).with([:a, :b]).returns true property.should = "foo" end it "should support specifying an individual required feature" do value = subclass.newvalue(/./, :required_features => :a) resource.provider.expects(:satisfies?).returns true property.should = "foo" end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do property.munge("foo").should == "foo" end it "should return return any matching defined values" do subclass.newvalue(:foo) property.munge("foo").should == :foo end it "should return any matching aliases" do subclass.newvalue(:foo) subclass.aliasvalue(:bar, :foo) property.munge("bar").should == :foo end it "should return the value if it matches a regex" do subclass.newvalue(/./) property.munge("bar").should == "bar" end it "should return the value if no other option is matched" do subclass.newvalue(:foo) property.munge("bar").should == "bar" end end describe "when syncing the 'should' value" do it "should set the value" do subclass.newvalue(:foo) property.should = :foo property.expects(:set).with(:foo) property.sync end end describe "when setting a value" do it "should catch exceptions and raise Puppet::Error" do subclass.newvalue(:foo) { raise "eh" } lambda { property.set(:foo) }.should raise_error(Puppet::Error) end describe "that was defined without a block" do it "should call the settor on the provider" do subclass.newvalue(:bar) resource.provider.expects(:foo=).with :bar property.set(:bar) end end describe "that was defined with a block" do it "should call the method created for the value if the value is not a regex" do subclass.newvalue(:bar) {} property.expects(:set_bar) property.set(:bar) end it "should call the provided block if the value is a regex" do subclass.newvalue(/./) { self.test } property.expects(:test) property.set("foo") end end end describe "when producing a change log" do it "should say 'defined' when the current value is 'absent'" do property.change_to_s(:absent, "foo").should =~ /^defined/ end it "should say 'undefined' when the new value is 'absent'" do property.change_to_s("foo", :absent).should =~ /^undefined/ end it "should say 'changed' when neither value is 'absent'" do property.change_to_s("foo", "bar").should =~ /changed/ end end shared_examples_for "#insync?" do # We share a lot of behaviour between the all and first matching, so we # use a shared behaviour set to emulate that. The outside world makes # sure the class, etc, point to the right content. [[], [12], [12, 13]].each do |input| it "should return true if should is empty with is => #{input.inspect}" do property.should = [] property.must be_insync(input) end end end describe "#insync?" do context "array_matching :all" do # `@should` is an array of scalar values, and `is` is an array of scalar values. before :each do property.class.array_matching = :all end it_should_behave_like "#insync?" context "if the should value is an array" do before :each do property.should = [1, 2] end it "should match if is exactly matches" do property.must be_insync [1, 2] end it "should match if it matches, but all stringified" do property.must be_insync ["1", "2"] end it "should not match if some-but-not-all values are stringified" do property.must_not be_insync ["1", 2] property.must_not be_insync [1, "2"] end it "should not match if order is different but content the same" do property.must_not be_insync [2, 1] end it "should not match if there are more items in should than is" do property.must_not be_insync [1] end it "should not match if there are less items in should than is" do property.must_not be_insync [1, 2, 3] end it "should not match if `is` is empty but `should` isn't" do property.must_not be_insync [] end end end context "array_matching :first" do # `@should` is an array of scalar values, and `is` is a scalar value. before :each do property.class.array_matching = :first end it_should_behave_like "#insync?" [[1], # only the value [1, 2], # matching value first [2, 1], # matching value last [0, 1, 2], # matching value in the middle ].each do |input| it "should by true if one unmodified should value of #{input.inspect} matches what is" do property.should = input property.must be_insync 1 end it "should be true if one stringified should value of #{input.inspect} matches what is" do property.should = input property.must be_insync "1" end end it "should not match if we expect a string but get the non-stringified value" do property.should = ["1"] property.must_not be_insync 1 end [[0], [0, 2]].each do |input| it "should not match if no should values match what is" do property.should = input property.must_not be_insync 1 property.must_not be_insync "1" # shouldn't match either. end end end end describe "#property_matches?" do [1, "1", [1], :one].each do |input| it "should treat two equal objects as equal (#{input.inspect})" do property.property_matches?(input, input).should be_true end end it "should treat two objects as equal if the first argument is the stringified version of the second" do property.property_matches?("1", 1).should be_true end it "should NOT treat two objects as equal if the first argument is not a string, and the second argument is a string, even if it stringifies to the first" do property.property_matches?(1, "1").should be_false end end end diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb index d5a37d9e8..3195283de 100755 --- a/spec/unit/provider/augeas/augeas_spec.rb +++ b/spec/unit/provider/augeas/augeas_spec.rb @@ -1,732 +1,732 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/package' provider_class = Puppet::Type.type(:augeas).provider(:augeas) describe provider_class do before(:each) do @resource = Puppet::Type.type(:augeas).new( :name => "test", :root => my_fixture_dir, :provider => :augeas ) @provider = provider_class.new(@resource) end after(:each) do @provider.close_augeas end describe "command parsing" do it "should break apart a single line into three tokens and clean up the context" do @resource[:context] = "/context" tokens = @provider.parse_commands("set Jar/Jar Binks") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/context/Jar/Jar" tokens[0][2].should == "Binks" end it "should break apart a multiple line into six tokens" do tokens = @provider.parse_commands("set /Jar/Jar Binks\nrm anakin") tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 2 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "anakin" end it "should strip whitespace and ignore blank lines" do tokens = @provider.parse_commands(" set /Jar/Jar Binks \t\n \n\n rm anakin ") tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 2 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "anakin" end it "should handle arrays" do @resource[:context] = "/foo/" commands = ["set /Jar/Jar Binks", "rm anakin"] tokens = @provider.parse_commands(commands) tokens.size.should == 2 tokens[0].size.should == 3 tokens[1].size.should == 2 tokens[0][0].should == "set" tokens[0][1].should == "/Jar/Jar" tokens[0][2].should == "Binks" tokens[1][0].should == "rm" tokens[1][1].should == "/foo/anakin" end # This is not supported in the new parsing class #it "should concat the last values" do # provider = provider_class.new # tokens = provider.parse_commands("set /Jar/Jar Binks is my copilot") # tokens.size.should == 1 # tokens[0].size.should == 3 # tokens[0][0].should == "set" # tokens[0][1].should == "/Jar/Jar" # tokens[0][2].should == "Binks is my copilot" #end it "should accept spaces in the value and single ticks" do @resource[:context] = "/foo/" tokens = @provider.parse_commands("set JarJar 'Binks is my copilot'") tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == "/foo/JarJar" tokens[0][2].should == "Binks is my copilot" end it "should accept spaces in the value and double ticks" do @resource[:context] = "/foo/" tokens = @provider.parse_commands('set /JarJar "Binks is my copilot"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == '/JarJar' tokens[0][2].should == 'Binks is my copilot' end it "should accept mixed ticks" do @resource[:context] = "/foo/" tokens = @provider.parse_commands('set JarJar "Some \'Test\'"') tokens.size.should == 1 tokens[0].size.should == 3 tokens[0][0].should == "set" tokens[0][1].should == '/foo/JarJar' tokens[0][2].should == "Some \'Test\'" end it "should handle predicates with literals" do @resource[:context] = "/foo/" tokens = @provider.parse_commands("rm */*[module='pam_console.so']") tokens.should == [["rm", "/foo/*/*[module='pam_console.so']"]] end it "should handle whitespace in predicates" do @resource[:context] = "/foo/" tokens = @provider.parse_commands("ins 42 before /files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]") tokens.should == [["ins", "42", "before","/files/etc/hosts/*/ipaddr[ . = '127.0.0.1' ]"]] end it "should handle multiple predicates" do @resource[:context] = "/foo/" tokens = @provider.parse_commands("clear pam.d/*/*[module = 'system-auth'][type = 'account']") tokens.should == [["clear", "/foo/pam.d/*/*[module = 'system-auth'][type = 'account']"]] end it "should handle nested predicates" do @resource[:context] = "/foo/" args = ["clear", "/foo/pam.d/*/*[module[ ../type = 'type] = 'system-auth'][type[last()] = 'account']"] tokens = @provider.parse_commands(args.join(" ")) tokens.should == [ args ] end it "should handle escaped doublequotes in doublequoted string" do @resource[:context] = "/foo/" tokens = @provider.parse_commands("set /foo \"''\\\"''\"") tokens.should == [[ "set", "/foo", "''\\\"''" ]] end it "should allow escaped spaces and brackets in paths" do @resource[:context] = "/foo/" args = [ "set", "/white\\ space/\\[section", "value" ] tokens = @provider.parse_commands(args.join(" \t ")) tokens.should == [ args ] end it "should allow single quoted escaped spaces in paths" do @resource[:context] = "/foo/" args = [ "set", "'/white\\ space/key'", "value" ] tokens = @provider.parse_commands(args.join(" \t ")) tokens.should == [[ "set", "/white\\ space/key", "value" ]] end it "should allow double quoted escaped spaces in paths" do @resource[:context] = "/foo/" args = [ "set", '"/white\\ space/key"', "value" ] tokens = @provider.parse_commands(args.join(" \t ")) tokens.should == [[ "set", "/white\\ space/key", "value" ]] end it "should remove trailing slashes" do @resource[:context] = "/foo/" tokens = @provider.parse_commands("set foo/ bar") tokens.should == [[ "set", "/foo/foo", "bar" ]] end end describe "get filters" do before do augeas = stub("augeas", :get => "value") augeas.stubs("close") @provider.aug = augeas end it "should return false for a = nonmatch" do command = ["get", "fake value", "==", "value"] @provider.process_get(command).should == true end it "should return true for a != match" do command = ["get", "fake value", "!=", "value"] @provider.process_get(command).should == false end it "should return true for a =~ match" do command = ["get", "fake value", "=~", "val*"] @provider.process_get(command).should == true end it "should return false for a == nonmatch" do command = ["get", "fake value", "=~", "num*"] @provider.process_get(command).should == false end end describe "match filters" do before do augeas = stub("augeas", :match => ["set", "of", "values"]) augeas.stubs("close") @provider = provider_class.new(@resource) @provider.aug = augeas end it "should return true for size match" do command = ["match", "fake value", "size == 3"] @provider.process_match(command).should == true end it "should return false for a size non match" do command = ["match", "fake value", "size < 3"] @provider.process_match(command).should == false end it "should return true for includes match" do command = ["match", "fake value", "include values"] @provider.process_match(command).should == true end it "should return false for includes non match" do command = ["match", "fake value", "include JarJar"] @provider.process_match(command).should == false end it "should return true for includes match" do command = ["match", "fake value", "not_include JarJar"] @provider.process_match(command).should == true end it "should return false for includes non match" do command = ["match", "fake value", "not_include values"] @provider.process_match(command).should == false end it "should return true for an array match" do command = ["match", "fake value", "== ['set', 'of', 'values']"] @provider.process_match(command).should == true end it "should return false for an array non match" do command = ["match", "fake value", "== ['this', 'should', 'not', 'match']"] @provider.process_match(command).should == false end it "should return false for an array match with noteq" do command = ["match", "fake value", "!= ['set', 'of', 'values']"] @provider.process_match(command).should == false end it "should return true for an array non match with noteq" do command = ["match", "fake value", "!= ['this', 'should', 'not', 'match']"] @provider.process_match(command).should == true end end describe "need to run" do before(:each) do @augeas = stub("augeas") @augeas.stubs("close") @provider.aug = @augeas # These tests pretend to be an earlier version so the provider doesn't # attempt to make the change in the need_to_run? method @provider.stubs(:get_augeas_version).returns("0.3.5") end it "should handle no filters" do @augeas.stubs("match").returns(["set", "of", "values"]) @provider.need_to_run?.should == true end it "should return true when a get filter matches" do @resource[:onlyif] = "get path == value" @augeas.stubs("get").returns("value") @provider.need_to_run?.should == true end it "should return false when a get filter does not match" do @resource[:onlyif] = "get path == another value" @augeas.stubs("get").returns("value") @provider.need_to_run?.should == false end it "should return true when a match filter matches" do @resource[:onlyif] = "match path size == 3" @augeas.stubs("match").returns(["set", "of", "values"]) @provider.need_to_run?.should == true end it "should return false when a match filter does not match" do @resource[:onlyif] = "match path size == 2" @augeas.stubs("match").returns(["set", "of", "values"]) @provider.need_to_run?.should == false end # Now setting force to true it "setting force should not change the above logic" do @resource[:force] = true @resource[:onlyif] = "match path size == 2" @augeas.stubs("match").returns(["set", "of", "values"]) @provider.need_to_run?.should == false end #Ticket 5211 testing it "should return true when a size != the provided value" do @resource[:onlyif] = "match path size != 17" @augeas.stubs("match").returns(["set", "of", "values"]) @provider.need_to_run?.should == true end #Ticket 5211 testing it "should return false when a size doeas equal the provided value" do @resource[:onlyif] = "match path size != 3" @augeas.stubs("match").returns(["set", "of", "values"]) @provider.need_to_run?.should == false end # Ticket 2728 (diff files) describe "and Puppet[:show_diff] is set" do before(:each) do Puppet[:show_diff] = true @resource[:root] = "" @provider.stubs(:get_augeas_version).returns("0.10.0") @augeas.stubs(:set).returns(true) @augeas.stubs(:save).returns(true) end it "should call diff when a file is shown to have been changed" do file = "/etc/hosts" File.stubs(:delete) @resource[:context] = "/files" @resource[:changes] = ["set #{file}/foo bar"] @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"]) @augeas.stubs(:get).with("/augeas/events/saved").returns("/files#{file}") @augeas.expects(:set).with("/augeas/save", "newfile") @augeas.expects(:close).never() @provider.expects("diff").with("#{file}", "#{file}.augnew").returns("") @provider.should be_need_to_run end it "should call diff for each file thats changed" do file1 = "/etc/hosts" file2 = "/etc/resolv.conf" File.stubs(:delete) @resource[:context] = "/files" @resource[:changes] = ["set #{file1}/foo bar", "set #{file2}/baz biz"] @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved[1]", "/augeas/events/saved[2]"]) @augeas.stubs(:get).with("/augeas/events/saved[1]").returns("/files#{file1}") @augeas.stubs(:get).with("/augeas/events/saved[2]").returns("/files#{file2}") @augeas.expects(:set).with("/augeas/save", "newfile") @augeas.expects(:close).never() @provider.expects(:diff).with("#{file1}", "#{file1}.augnew").returns("") @provider.expects(:diff).with("#{file2}", "#{file2}.augnew").returns("") @provider.should be_need_to_run end describe "and resource[:root] is set" do it "should call diff when a file is shown to have been changed" do root = "/tmp/foo" file = "/etc/hosts" File.stubs(:delete) @resource[:context] = "/files" @resource[:changes] = ["set #{file}/foo bar"] @resource[:root] = root @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"]) @augeas.stubs(:get).with("/augeas/events/saved").returns("/files#{file}") @augeas.expects(:set).with("/augeas/save", "newfile") @augeas.expects(:close).never() @provider.expects(:diff).with("#{root}#{file}", "#{root}#{file}.augnew").returns("") @provider.should be_need_to_run end end it "should not call diff if no files change" do file = "/etc/hosts" @resource[:context] = "/files" @resource[:changes] = ["set #{file}/foo bar"] @augeas.stubs(:match).with("/augeas/events/saved").returns([]) @augeas.expects(:set).with("/augeas/save", "newfile") @augeas.expects(:get).with("/augeas/events/saved").never() @augeas.expects(:close) @provider.expects(:diff).never() @provider.should_not be_need_to_run end it "should cleanup the .augnew file" do file = "/etc/hosts" @resource[:context] = "/files" @resource[:changes] = ["set #{file}/foo bar"] @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved"]) @augeas.stubs(:get).with("/augeas/events/saved").returns("/files#{file}") @augeas.expects(:set).with("/augeas/save", "newfile") @augeas.expects(:close) File.expects(:delete).with(file + ".augnew") @provider.expects(:diff).with("#{file}", "#{file}.augnew").returns("") @provider.should be_need_to_run end # Workaround for Augeas bug #264 which reports filenames twice it "should handle duplicate /augeas/events/saved filenames" do file = "/etc/hosts" @resource[:context] = "/files" @resource[:changes] = ["set #{file}/foo bar"] @augeas.stubs(:match).with("/augeas/events/saved").returns(["/augeas/events/saved[1]", "/augeas/events/saved[2]"]) @augeas.stubs(:get).with("/augeas/events/saved[1]").returns("/files#{file}") @augeas.stubs(:get).with("/augeas/events/saved[2]").returns("/files#{file}") @augeas.expects(:set).with("/augeas/save", "newfile") @augeas.expects(:close) File.expects(:delete).with(file + ".augnew").once() @provider.expects(:diff).with("#{file}", "#{file}.augnew").returns("").once() @provider.should be_need_to_run end it "should fail with an error if saving fails" do file = "/etc/hosts" @resource[:context] = "/files" @resource[:changes] = ["set #{file}/foo bar"] @augeas.stubs(:save).returns(false) @augeas.stubs(:match).with("/augeas/events/saved").returns([]) @augeas.expects(:close) @provider.expects(:diff).never() lambda { @provider.need_to_run? }.should raise_error end end end describe "augeas execution integration" do before do @augeas = stub("augeas", :load) @augeas.stubs("close") @augeas.stubs(:match).with("/augeas/events/saved").returns([]) @provider.aug = @augeas @provider.stubs(:get_augeas_version).returns("0.3.5") end it "should handle set commands" do @resource[:changes] = "set JarJar Binks" @resource[:context] = "/some/path/" @augeas.expects(:set).with("/some/path/JarJar", "Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle rm commands" do @resource[:changes] = "rm /Jar/Jar" @augeas.expects(:rm).with("/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle remove commands" do @resource[:changes] = "remove /Jar/Jar" @augeas.expects(:rm).with("/Jar/Jar") @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle clear commands" do @resource[:changes] = "clear Jar/Jar" @resource[:context] = "/foo/" @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins commands with before" do @resource[:changes] = "ins Binks before Jar/Jar" @resource[:context] = "/foo" @augeas.expects(:insert).with("/foo/Jar/Jar", "Binks", true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins commands with after" do @resource[:changes] = "ins Binks after /Jar/Jar" @resource[:context] = "/foo" @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle ins with no context" do @resource[:changes] = "ins Binks after /Jar/Jar" @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle multiple commands" do @resource[:changes] = ["ins Binks after /Jar/Jar", "clear Jar/Jar"] @resource[:context] = "/foo/" @augeas.expects(:insert).with("/Jar/Jar", "Binks", false) @augeas.expects(:clear).with("/foo/Jar/Jar").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle defvar commands" do @resource[:changes] = "defvar myjar Jar/Jar" @resource[:context] = "/foo/" @augeas.expects(:defvar).with("myjar", "/foo/Jar/Jar").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should pass through augeas variables without context" do @resource[:changes] = ["defvar myjar Jar/Jar","set $myjar/Binks 1"] @resource[:context] = "/foo/" @augeas.expects(:defvar).with("myjar", "/foo/Jar/Jar").returns(true) # this is the important bit, shouldn't be /foo/$myjar/Binks @augeas.expects(:set).with("$myjar/Binks", "1").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle defnode commands" do @resource[:changes] = "defnode newjar Jar/Jar[last()+1] Binks" @resource[:context] = "/foo/" @augeas.expects(:defnode).with("newjar", "/foo/Jar/Jar[last()+1]", "Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle mv commands" do @resource[:changes] = "mv Jar/Jar Binks" @resource[:context] = "/foo/" @augeas.expects(:mv).with("/foo/Jar/Jar", "/foo/Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end it "should handle setm commands" do @resource[:changes] = ["set test[1]/Jar/Jar Foo","set test[2]/Jar/Jar Bar","setm test Jar/Jar Binks"] @resource[:context] = "/foo/" @augeas.expects(:set).with("/foo/test[1]/Jar/Jar", "Foo").returns(true) @augeas.expects(:set).with("/foo/test[2]/Jar/Jar", "Bar").returns(true) @augeas.expects(:setm).with("/foo/test", "Jar/Jar", "Binks").returns(true) @augeas.expects(:save).returns(true) @augeas.expects(:close) @provider.execute_changes.should == :executed end end describe "when making changes", :if => Puppet.features.augeas? do include PuppetSpec::Files it "should not clobber the file if it's a symlink" do Puppet::Util::Storage.stubs(:store) link = tmpfile('link') target = tmpfile('target') FileUtils.touch(target) FileUtils.symlink(target, link) resource = Puppet::Type.type(:augeas).new( :name => 'test', :incl => link, :lens => 'Sshd.lns', :changes => "set PermitRootLogin no" ) catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.apply File.ftype(link).should == 'link' File.readlink(link).should == target File.read(target).should =~ /PermitRootLogin no/ end end describe "load/save failure reporting" do before do @augeas = stub("augeas") @augeas.stubs("close") @provider.aug = @augeas end describe "should find load errors" do before do @augeas.expects(:match).with("/augeas//error").returns(["/augeas/files/foo/error"]) @augeas.expects(:match).with("/augeas/files/foo/error/*").returns(["/augeas/files/foo/error/path", "/augeas/files/foo/error/message"]) @augeas.expects(:get).with("/augeas/files/foo/error/path").returns("/foo") @augeas.expects(:get).with("/augeas/files/foo/error/message").returns("Failed to...") end it "and output to debug" do @provider.expects(:debug).times(4) @provider.print_load_errors end it "and output a warning and to debug" do @provider.expects(:warning).once() @provider.expects(:debug).times(3) @provider.print_load_errors(:warning => true) end end it "should find save errors and output to debug" do @augeas.expects(:match).with("/augeas//error[. = 'put_failed']").returns(["/augeas/files/foo/error"]) @augeas.expects(:match).with("/augeas/files/foo/error/*").returns(["/augeas/files/foo/error/path", "/augeas/files/foo/error/message"]) @augeas.expects(:get).with("/augeas/files/foo/error/path").returns("/foo") @augeas.expects(:get).with("/augeas/files/foo/error/message").returns("Failed to...") @provider.expects(:debug).times(4) @provider.print_put_errors end end # Run initialisation tests of the real Augeas library to test our open_augeas # method. This relies on Augeas and ruby-augeas on the host to be # functioning. describe "augeas lib initialisation", :if => Puppet.features.augeas? do # Expect lenses for fstab and hosts it "should have loaded standard files by default" do aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] aug.match("/files/etc/hosts").should == ["/files/etc/hosts"] aug.match("/files/etc/test").should == [] end it "should report load errors to debug only" do @provider.expects(:print_load_errors).with(:warning => false) aug = @provider.open_augeas aug.should_not == nil end # Only the file specified should be loaded it "should load one file if incl/lens used" do @resource[:incl] = "/etc/hosts" @resource[:lens] = "Hosts.lns" @provider.expects(:print_load_errors).with(:warning => true) aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == [] aug.match("/files/etc/hosts").should == ["/files/etc/hosts"] aug.match("/files/etc/test").should == [] end it "should also load lenses from load_path" do @resource[:load_path] = my_fixture_dir aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] aug.match("/files/etc/hosts").should == ["/files/etc/hosts"] aug.match("/files/etc/test").should == ["/files/etc/test"] end # Optimisations added for Augeas 0.8.2 or higher is available, see #7285 describe ">= 0.8.2 optimisations", :if => Puppet.features.augeas? && Facter.value(:augeasversion) && Puppet::Util::Package.versioncmp(Facter.value(:augeasversion), "0.8.2") >= 0 do it "should only load one file if relevant context given" do @resource[:context] = "/files/etc/fstab" @provider.expects(:print_load_errors).with(:warning => true) aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] aug.match("/files/etc/hosts").should == [] end it "should only load one lens from load_path if context given" do @resource[:context] = "/files/etc/test" @resource[:load_path] = my_fixture_dir aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == [] aug.match("/files/etc/hosts").should == [] aug.match("/files/etc/test").should == ["/files/etc/test"] end it "should load standard files if context isn't specific" do @resource[:context] = "/files/etc" aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] aug.match("/files/etc/hosts").should == ["/files/etc/hosts"] end it "should not optimise if the context is a complex path" do @resource[:context] = "/files/*[label()='etc']" aug = @provider.open_augeas aug.should_not == nil aug.match("/files/etc/fstab").should == ["/files/etc/fstab"] aug.match("/files/etc/hosts").should == ["/files/etc/hosts"] end end end end diff --git a/spec/unit/provider/cisco_spec.rb b/spec/unit/provider/cisco_spec.rb index 0696221c4..a911f35e6 100755 --- a/spec/unit/provider/cisco_spec.rb +++ b/spec/unit/provider/cisco_spec.rb @@ -1,15 +1,15 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/cisco' describe Puppet::Provider::Cisco do it "should implement a device class method" do Puppet::Provider::Cisco.should respond_to(:device) end it "should create a cisco device instance" do Puppet::Util::NetworkDevice::Cisco::Device.expects(:new).returns :device Puppet::Provider::Cisco.device(:url).should == :device end end diff --git a/spec/unit/provider/confine/exists_spec.rb b/spec/unit/provider/confine/exists_spec.rb index 30b8793e8..6fc2a8605 100755 --- a/spec/unit/provider/confine/exists_spec.rb +++ b/spec/unit/provider/confine/exists_spec.rb @@ -1,80 +1,80 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine/exists' describe Puppet::Provider::Confine::Exists do before do @confine = Puppet::Provider::Confine::Exists.new("/my/file") @confine.label = "eh" end it "should be named :exists" do Puppet::Provider::Confine::Exists.name.should == :exists end it "should not pass if exists is nil" do confine = Puppet::Provider::Confine::Exists.new(nil) confine.label = ":exists => nil" confine.expects(:pass?).with(nil) confine.should_not be_valid end it "should use the 'pass?' method to test validity" do @confine.expects(:pass?).with("/my/file") @confine.valid? end it "should return false if the value is false" do @confine.pass?(false).should be_false end it "should return false if the value does not point to a file" do FileTest.expects(:exist?).with("/my/file").returns false @confine.pass?("/my/file").should be_false end it "should return true if the value points to a file" do FileTest.expects(:exist?).with("/my/file").returns true @confine.pass?("/my/file").should be_true end it "should produce a message saying that a file is missing" do @confine.message("/my/file").should be_include("does not exist") end describe "and the confine is for binaries" do before { @confine.stubs(:for_binary).returns true } it "should use its 'which' method to look up the full path of the file" do @confine.expects(:which).returns nil @confine.pass?("/my/file") end it "should return false if no executable can be found" do @confine.expects(:which).with("/my/file").returns nil @confine.pass?("/my/file").should be_false end it "should return true if the executable can be found" do @confine.expects(:which).with("/my/file").returns "/my/file" @confine.pass?("/my/file").should be_true end end it "should produce a summary containing all missing files" do FileTest.stubs(:exist?).returns true FileTest.expects(:exist?).with("/two").returns false FileTest.expects(:exist?).with("/four").returns false confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four} confine.summary.should == %w{/two /four} end it "should summarize multiple instances by returning a flattened array of their summaries" do c1 = mock '1', :summary => %w{one} c2 = mock '2', :summary => %w{two} c3 = mock '3', :summary => %w{three} Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three} end end diff --git a/spec/unit/provider/confine/false_spec.rb b/spec/unit/provider/confine/false_spec.rb index 1afa57cbc..9dcb9ba80 100755 --- a/spec/unit/provider/confine/false_spec.rb +++ b/spec/unit/provider/confine/false_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine/false' describe Puppet::Provider::Confine::False do it "should be named :false" do Puppet::Provider::Confine::False.name.should == :false end it "should require a value" do lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) end describe "when testing values" do before { @confine = Puppet::Provider::Confine::False.new("foo") } it "should use the 'pass?' method to test validity" do @confine = Puppet::Provider::Confine::False.new("foo") @confine.label = "eh" @confine.expects(:pass?).with("foo") @confine.valid? end it "should return true if the value is false" do @confine.pass?(false).should be_true end it "should return false if the value is not false" do @confine.pass?("else").should be_false end it "should produce a message that a value is true" do @confine = Puppet::Provider::Confine::False.new("foo") @confine.message("eh").should be_include("true") end end it "should be able to produce a summary with the number of incorrectly true values" do confine = Puppet::Provider::Confine::False.new %w{one two three four} confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) confine.summary.should == 2 end it "should summarize multiple instances by summing their summaries" do c1 = mock '1', :summary => 1 c2 = mock '2', :summary => 2 c3 = mock '3', :summary => 3 Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6 end end diff --git a/spec/unit/provider/confine/feature_spec.rb b/spec/unit/provider/confine/feature_spec.rb index 04040dd80..db5767335 100755 --- a/spec/unit/provider/confine/feature_spec.rb +++ b/spec/unit/provider/confine/feature_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine/feature' describe Puppet::Provider::Confine::Feature do it "should be named :feature" do Puppet::Provider::Confine::Feature.name.should == :feature end it "should require a value" do lambda { Puppet::Provider::Confine::Feature.new }.should raise_error(ArgumentError) end it "should always convert values to an array" do Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array) end describe "when testing values" do before do @confine = Puppet::Provider::Confine::Feature.new("myfeature") @confine.label = "eh" end it "should use the Puppet features instance to test validity" do Puppet.features.expects(:myfeature?) @confine.valid? end it "should return true if the feature is present" do Puppet.features.add(:myfeature) do true end @confine.pass?("myfeature").should be_true end it "should return false if the value is false" do Puppet.features.add(:myfeature) do false end @confine.pass?("myfeature").should be_false end it "should log that a feature is missing" do @confine.message("myfeat").should be_include("missing") end end it "should summarize multiple instances by returning a flattened array of all missing features" do confines = [] confines << Puppet::Provider::Confine::Feature.new(%w{one two}) confines << Puppet::Provider::Confine::Feature.new(%w{two}) confines << Puppet::Provider::Confine::Feature.new(%w{three four}) features = mock 'feature' features.stub_everything Puppet.stubs(:features).returns features Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort end end diff --git a/spec/unit/provider/confine/true_spec.rb b/spec/unit/provider/confine/true_spec.rb index 795819bd3..712c8e35c 100755 --- a/spec/unit/provider/confine/true_spec.rb +++ b/spec/unit/provider/confine/true_spec.rb @@ -1,52 +1,52 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine/true' describe Puppet::Provider::Confine::True do it "should be named :true" do Puppet::Provider::Confine::True.name.should == :true end it "should require a value" do lambda { Puppet::Provider::Confine::True.new }.should raise_error(ArgumentError) end describe "when testing values" do before do @confine = Puppet::Provider::Confine::True.new("foo") @confine.label = "eh" end it "should use the 'pass?' method to test validity" do @confine.expects(:pass?).with("foo") @confine.valid? end it "should return true if the value is not false" do @confine.pass?("else").should be_true end it "should return false if the value is false" do @confine.pass?(nil).should be_false end it "should produce the message that a value is false" do @confine.message("eh").should be_include("false") end end it "should produce the number of false values when asked for a summary" do @confine = Puppet::Provider::Confine::True.new %w{one two three four} @confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false) @confine.summary.should == 2 end it "should summarize multiple instances by summing their summaries" do c1 = mock '1', :summary => 1 c2 = mock '2', :summary => 2 c3 = mock '3', :summary => 3 Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6 end end diff --git a/spec/unit/provider/confine/variable_spec.rb b/spec/unit/provider/confine/variable_spec.rb index 7b9f53c3d..94aac6cf6 100755 --- a/spec/unit/provider/confine/variable_spec.rb +++ b/spec/unit/provider/confine/variable_spec.rb @@ -1,106 +1,106 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine/variable' describe Puppet::Provider::Confine::Variable do it "should be named :variable" do Puppet::Provider::Confine::Variable.name.should == :variable end it "should require a value" do lambda { Puppet::Provider::Confine::Variable.new }.should raise_error(ArgumentError) end it "should always convert values to an array" do Puppet::Provider::Confine::Variable.new("/some/file").values.should be_instance_of(Array) end it "should have an accessor for its name" do Puppet::Provider::Confine::Variable.new(:bar).should respond_to(:name) end describe "when testing values" do before do @confine = Puppet::Provider::Confine::Variable.new("foo") @confine.name = :myvar end it "should use settings if the variable name is a valid setting" do Puppet.settings.expects(:valid?).with(:myvar).returns true Puppet.settings.expects(:value).with(:myvar).returns "foo" @confine.valid? end it "should use Facter if the variable name is not a valid setting" do Puppet.settings.expects(:valid?).with(:myvar).returns false Facter.expects(:value).with(:myvar).returns "foo" @confine.valid? end it "should be valid if the value matches the facter value" do @confine.expects(:test_value).returns "foo" @confine.should be_valid end it "should return false if the value does not match the facter value" do @confine.expects(:test_value).returns "fee" @confine.should_not be_valid end it "should be case insensitive" do @confine.expects(:test_value).returns "FOO" @confine.should be_valid end it "should not care whether the value is a string or symbol" do @confine.expects(:test_value).returns "FOO" @confine.should be_valid end it "should produce a message that the fact value is not correct" do @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) @confine.name = "eh" message = @confine.message("value") message.should be_include("facter") message.should be_include("bar,bee") end it "should be valid if the test value matches any of the provided values" do @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee}) @confine.expects(:test_value).returns "bee" @confine.should be_valid end end describe "when summarizing multiple instances" do it "should return a hash of failing variables and their values" do c1 = Puppet::Provider::Confine::Variable.new("one") c1.name = "uno" c1.expects(:valid?).returns false c2 = Puppet::Provider::Confine::Variable.new("two") c2.name = "dos" c2.expects(:valid?).returns true c3 = Puppet::Provider::Confine::Variable.new("three") c3.name = "tres" c3.expects(:valid?).returns false Puppet::Provider::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}} end it "should combine the values of multiple confines with the same fact" do c1 = Puppet::Provider::Confine::Variable.new("one") c1.name = "uno" c1.expects(:valid?).returns false c2 = Puppet::Provider::Confine::Variable.new("two") c2.name = "uno" c2.expects(:valid?).returns false Puppet::Provider::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}} end end end diff --git a/spec/unit/provider/confine_collection_spec.rb b/spec/unit/provider/confine_collection_spec.rb index f1dbaf35d..5aaa230b9 100755 --- a/spec/unit/provider/confine_collection_spec.rb +++ b/spec/unit/provider/confine_collection_spec.rb @@ -1,133 +1,133 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine_collection' describe Puppet::Provider::ConfineCollection do it "should be able to add confines" do Puppet::Provider::ConfineCollection.new("label").should respond_to(:confine) end it "should require a label at initialization" do lambda { Puppet::Provider::ConfineCollection.new }.should raise_error(ArgumentError) end it "should make its label available" do Puppet::Provider::ConfineCollection.new("mylabel").label.should == "mylabel" end describe "when creating confine instances" do it "should create an instance of the named test with the provided values" do test_class = mock 'test_class' test_class.expects(:new).with(%w{my values}).returns(stub('confine', :label= => nil)) Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} end it "should copy its label to the confine instance" do confine = mock 'confine' test_class = mock 'test_class' test_class.expects(:new).returns confine Puppet::Provider::Confine.expects(:test).returns test_class confine.expects(:label=).with("label") Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values} end describe "and the test cannot be found" do it "should create a Facter test with the provided values and set the name to the test name" do confine = Puppet::Provider::Confine.test(:variable).new(%w{my values}) confine.expects(:name=).with(:foo) confine.class.expects(:new).with(%w{my values}).returns confine Puppet::Provider::ConfineCollection.new("label").confine(:foo => %w{my values}) end end describe "and the 'for_binary' option was provided" do it "should mark the test as a binary confine" do confine = Puppet::Provider::Confine.test(:exists).new(:bar) confine.expects(:for_binary=).with true Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine Puppet::Provider::ConfineCollection.new("label").confine :exists => :bar, :for_binary => true end end end it "should be valid if no confines are present" do Puppet::Provider::ConfineCollection.new("label").should be_valid end it "should be valid if all confines pass" do c1 = stub 'c1', :valid? => true, :label= => nil c2 = stub 'c2', :valid? => true, :label= => nil Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) confiner = Puppet::Provider::ConfineCollection.new("label") confiner.confine :true => :bar, :false => :bee confiner.should be_valid end it "should not be valid if any confines fail" do c1 = stub 'c1', :valid? => true, :label= => nil c2 = stub 'c2', :valid? => false, :label= => nil Puppet::Provider::Confine.test(:true).expects(:new).returns(c1) Puppet::Provider::Confine.test(:false).expects(:new).returns(c2) confiner = Puppet::Provider::ConfineCollection.new("label") confiner.confine :true => :bar, :false => :bee confiner.should_not be_valid end describe "when providing a summary" do before do @confiner = Puppet::Provider::ConfineCollection.new("label") end it "should return a hash" do @confiner.summary.should be_instance_of(Hash) end it "should return an empty hash if the confiner is valid" do @confiner.summary.should == {} end it "should add each test type's summary to the hash" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:true => :tsumm, :false => :fsumm} end it "should not include tests that return 0" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0 Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:false => :fsumm} end it "should not include tests that return empty arrays" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns [] Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:false => :fsumm} end it "should not include tests that return empty hashes" do @confiner.confine :true => :bar, :false => :bee Puppet::Provider::Confine.test(:true).expects(:summarize).returns({}) Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm @confiner.summary.should == {:false => :fsumm} end end end diff --git a/spec/unit/provider/confine_spec.rb b/spec/unit/provider/confine_spec.rb index ade444276..5d44c98f9 100755 --- a/spec/unit/provider/confine_spec.rb +++ b/spec/unit/provider/confine_spec.rb @@ -1,77 +1,77 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confine' describe Puppet::Provider::Confine do it "should require a value" do lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError) end it "should always convert values to an array" do Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array) end it "should have a 'true' test" do Puppet::Provider::Confine.test(:true).should be_instance_of(Class) end it "should have a 'false' test" do Puppet::Provider::Confine.test(:false).should be_instance_of(Class) end it "should have a 'feature' test" do Puppet::Provider::Confine.test(:feature).should be_instance_of(Class) end it "should have an 'exists' test" do Puppet::Provider::Confine.test(:exists).should be_instance_of(Class) end it "should have a 'variable' test" do Puppet::Provider::Confine.test(:variable).should be_instance_of(Class) end describe "when testing all values" do before do @confine = Puppet::Provider::Confine.new(%w{a b c}) @confine.label = "foo" end it "should be invalid if any values fail" do @confine.stubs(:pass?).returns true @confine.expects(:pass?).with("b").returns false @confine.should_not be_valid end it "should be valid if all values pass" do @confine.stubs(:pass?).returns true @confine.should be_valid end it "should short-cut at the first failing value" do @confine.expects(:pass?).once.returns false @confine.valid? end it "should log failing confines with the label and message" do @confine.stubs(:pass?).returns false @confine.expects(:message).returns "My message" @confine.expects(:label).returns "Mylabel" Puppet.expects(:debug).with("Mylabel: My message") @confine.valid? end end describe "when testing the result of the values" do before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) } it "should return an array with the result of the test for each value" do @confine.stubs(:pass?).returns true @confine.expects(:pass?).with("b").returns false @confine.expects(:pass?).with("d").returns false @confine.result.should == [true, false, true, false] end end end diff --git a/spec/unit/provider/confiner_spec.rb b/spec/unit/provider/confiner_spec.rb index 23ec162a5..c2634530d 100755 --- a/spec/unit/provider/confiner_spec.rb +++ b/spec/unit/provider/confiner_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/confiner' describe Puppet::Provider::Confiner do before do @object = Object.new @object.extend(Puppet::Provider::Confiner) end it "should have a method for defining confines" do @object.should respond_to(:confine) end it "should have a method for returning its confine collection" do @object.should respond_to(:confine_collection) end it "should have a method for testing suitability" do @object.should respond_to(:suitable?) end it "should delegate its confine method to its confine collection" do coll = mock 'collection' @object.stubs(:confine_collection).returns coll coll.expects(:confine).with(:foo => :bar, :bee => :baz) @object.confine(:foo => :bar, :bee => :baz) end it "should create a new confine collection if one does not exist" do Puppet::Provider::ConfineCollection.expects(:new).with("mylabel").returns "mycoll" @object.expects(:to_s).returns "mylabel" @object.confine_collection.should == "mycoll" end it "should reuse the confine collection" do @object.confine_collection.should equal(@object.confine_collection) end describe "when testing suitability" do before do @coll = mock 'collection' @object.stubs(:confine_collection).returns @coll end it "should return true if the confine collection is valid" do @coll.expects(:valid?).returns true @object.should be_suitable end it "should return false if the confine collection is invalid" do @coll.expects(:valid?).returns false @object.should_not be_suitable end it "should return the summary of the confine collection if a long result is asked for" do @coll.expects(:summary).returns "myresult" @object.suitable?(false).should == "myresult" end end end diff --git a/spec/unit/provider/cron/crontab_spec.rb b/spec/unit/provider/cron/crontab_spec.rb index e96e75bc1..61b2dde67 100755 --- a/spec/unit/provider/cron/crontab_spec.rb +++ b/spec/unit/provider/cron/crontab_spec.rb @@ -1,116 +1,116 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:cron).provider(:crontab) do subject do provider = Puppet::Type.type(:cron).provider(:crontab) provider.initvars provider end context "with the simple samples" do FIELDS = { :crontab => %w{command minute hour month monthday weekday}.collect { |o| o.intern }, :freebsd_special => %w{special command}.collect { |o| o.intern }, :environment => [:line], :blank => [:line], :comment => [:line], } def compare_crontab_record(have, want) want.each do |param, value| have.should be_key param have[param].should == value end (FIELDS[have[:record_type]] - want.keys).each do |name| have[name].should == :absent end end def compare_crontab_text(have, want) # We should have four header lines, and then the text... have.lines.to_a[0..3].should be_all {|x| x =~ /^# / } have.lines.to_a[4..-1].join('').should == want end ######################################################################## # Simple input fixtures for testing. samples = YAML.load(File.read(my_fixture('single_line.yaml'))) samples.each do |name, data| it "should parse crontab line #{name} correctly" do compare_crontab_record subject.parse_line(data[:text]), data[:record] end it "should reconstruct the crontab line #{name} from the record" do subject.to_line(data[:record]).should == data[:text] end end records = [] text = "" # Sorting is from the original, and avoids :empty being the last line, # since the provider will ignore that and cause this to fail. samples.sort_by {|x| x.first.to_s }.each do |name, data| records << data[:record] text << data[:text] + "\n" end it "should parse all sample records at once" do subject.parse(text).zip(records).each do |round| compare_crontab_record *round end end it "should reconstitute the file from the records" do compare_crontab_text subject.to_file(records), text end context "multi-line crontabs" do tests = { :simple => [:spaces_in_command_with_times], :with_name => [:name, :spaces_in_command_with_times], :with_env => [:environment, :spaces_in_command_with_times], :with_multiple_envs => [:environment, :lowercase_environment, :spaces_in_command_with_times], :with_name_and_env => [:name_with_spaces, :another_env, :spaces_in_command_with_times], :with_name_and_multiple_envs => [:long_name, :another_env, :fourth_env, :spaces_in_command_with_times] } all_records = [] all_text = '' tests.each do |name, content| data = content.map {|x| samples[x] or raise "missing sample data #{x}" } text = data.map {|x| x[:text] }.join("\n") + "\n" records = data.map {|x| x[:record] } # Capture the whole thing for later, too... all_records += records all_text += text context name.to_s.gsub('_', ' ') do it "should regenerate the text from the record" do compare_crontab_text subject.to_file(records), text end it "should parse the records from the text" do subject.parse(text).zip(records).each do |round| compare_crontab_record *round end end end end it "should parse the whole set of records from the text" do subject.parse(all_text).zip(all_records).each do |round| compare_crontab_record *round end end it "should regenerate the whole text from the set of all records" do compare_crontab_text subject.to_file(all_records), all_text end end end end diff --git a/spec/unit/provider/exec/posix_spec.rb b/spec/unit/provider/exec/posix_spec.rb index 7bc1d7521..dab56942f 100755 --- a/spec/unit/provider/exec/posix_spec.rb +++ b/spec/unit/provider/exec/posix_spec.rb @@ -1,204 +1,204 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:exec).provider(:posix) do include PuppetSpec::Files def make_exe command = tmpfile('my_command') FileUtils.touch(command) File.chmod(0755, command) command end let(:resource) { Puppet::Type.type(:exec).new(:title => File.expand_path('/foo'), :provider => :posix) } let(:provider) { described_class.new(resource) } describe "#validatecmd" do it "should fail if no path is specified and the command is not fully qualified" do expect { provider.validatecmd("foo") }.to raise_error( Puppet::Error, "'foo' is not qualified and no path was specified. Please qualify the command or specify a path." ) end it "should pass if a path is given" do provider.resource[:path] = ['/bogus/bin'] provider.validatecmd("../foo") end it "should pass if command is fully qualifed" do provider.resource[:path] = ['/bogus/bin'] provider.validatecmd(File.expand_path("/bin/blah/foo")) end end describe "#run" do describe "when the command is an absolute path" do let(:command) { tmpfile('foo') } it "should fail if the command doesn't exist" do expect { provider.run(command) }.to raise_error(ArgumentError, "Could not find command '#{command}'") end it "should fail if the command isn't a file" do FileUtils.mkdir(command) FileUtils.chmod(0755, command) expect { provider.run(command) }.to raise_error(ArgumentError, "'#{command}' is a directory, not a file") end it "should fail if the command isn't executable" do FileUtils.touch(command) File.stubs(:executable?).with(command).returns(false) expect { provider.run(command) }.to raise_error(ArgumentError, "'#{command}' is not executable") end end describe "when the command is a relative path" do it "should execute the command if it finds it in the path and is executable" do command = make_exe provider.resource[:path] = [File.dirname(command)] filename = File.basename(command) Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == filename) && (arguments.is_a? Hash) } provider.run(filename) end it "should fail if the command isn't in the path" do resource[:path] = ["/fake/path"] expect { provider.run('foo') }.to raise_error(ArgumentError, "Could not find command 'foo'") end it "should fail if the command is in the path but not executable" do command = tmpfile('foo') FileUtils.touch(command) FileTest.stubs(:executable?).with(command).returns(false) resource[:path] = [File.dirname(command)] filename = File.basename(command) expect { provider.run(filename) }.to raise_error(ArgumentError, "Could not find command '#{filename}'") end end it "should not be able to execute shell builtins" do provider.resource[:path] = ['/bin'] expect { provider.run("cd ..") }.to raise_error(ArgumentError, "Could not find command 'cd'") end it "should execute the command if the command given includes arguments or subcommands" do provider.resource[:path] = ['/bogus/bin'] command = make_exe Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == "#{command} bar --sillyarg=true --blah") && (arguments.is_a? Hash) } provider.run("#{command} bar --sillyarg=true --blah") end it "should fail if quoted command doesn't exist" do provider.resource[:path] = ['/bogus/bin'] command = "#{File.expand_path('/foo')} bar --sillyarg=true --blah" expect { provider.run(%Q["#{command}"]) }.to raise_error(ArgumentError, "Could not find command '#{command}'") end it "should warn if you're overriding something in environment" do provider.resource[:environment] = ['WHATEVER=/something/else', 'WHATEVER=/foo'] command = make_exe Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == command) && (arguments.is_a? Hash) } provider.run(command) @logs.map {|l| "#{l.level}: #{l.message}" }.should == ["warning: Overriding environment setting 'WHATEVER' with '/foo'"] end describe "posix locale settings", :unless => Puppet.features.microsoft_windows? do # a sentinel value that we can use to emulate what locale environment variables might be set to on an international # system. lang_sentinel_value = "es_ES.UTF-8" # a temporary hash that contains sentinel values for each of the locale environment variables that we override in # "exec" locale_sentinel_env = {} Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| locale_sentinel_env[var] = lang_sentinel_value } command = "/bin/echo $%s" it "should not override user's locale during execution" do # we'll do this once without any sentinel values, to give us a little more test coverage orig_env = {} Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| orig_env[var] = ENV[var] if ENV[var] } orig_env.keys.each do |var| output, status = provider.run(command % var) output.strip.should == orig_env[var] end # now, once more... but with our sentinel values Puppet::Util.withenv(locale_sentinel_env) do Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| output, status = provider.run(command % var) output.strip.should == locale_sentinel_env[var] end end end it "should respect locale overrides in user's 'environment' configuration" do provider.resource[:environment] = ['LANG=foo', 'LC_ALL=bar'] output, status = provider.run(command % 'LANG') output.strip.should == 'foo' output, status = provider.run(command % 'LC_ALL') output.strip.should == 'bar' end end describe "posix user-related environment vars", :unless => Puppet.features.microsoft_windows? do # a temporary hash that contains sentinel values for each of the user-related environment variables that we # are expected to unset during an "exec" user_sentinel_env = {} Puppet::Util::POSIX::USER_ENV_VARS.each { |var| user_sentinel_env[var] = "Abracadabra" } command = "/bin/echo $%s" it "should unset user-related environment vars during execution" do # first we set up a temporary execution environment with sentinel values for the user-related environment vars # that we care about. Puppet::Util.withenv(user_sentinel_env) do # with this environment, we loop over the vars in question Puppet::Util::POSIX::USER_ENV_VARS.each do |var| # ensure that our temporary environment is set up as we expect ENV[var].should == user_sentinel_env[var] # run an "exec" via the provider and ensure that it unsets the vars output, status = provider.run(command % var) output.strip.should == "" # ensure that after the exec, our temporary env is still intact ENV[var].should == user_sentinel_env[var] end end end it "should respect overrides to user-related environment vars in caller's 'environment' configuration" do sentinel_value = "Abracadabra" # set the "environment" property of the resource, populating it with a hash containing sentinel values for # each of the user-related posix environment variables provider.resource[:environment] = Puppet::Util::POSIX::USER_ENV_VARS.collect { |var| "#{var}=#{sentinel_value}"} # loop over the posix user-related environment variables Puppet::Util::POSIX::USER_ENV_VARS.each do |var| # run an 'exec' to get the value of each variable output, status = provider.run(command % var) # ensure that it matches our expected sentinel value output.strip.should == sentinel_value end end end end end diff --git a/spec/unit/provider/exec/shell_spec.rb b/spec/unit/provider/exec/shell_spec.rb index aafb572ee..e2a7e7789 100755 --- a/spec/unit/provider/exec/shell_spec.rb +++ b/spec/unit/provider/exec/shell_spec.rb @@ -1,53 +1,53 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:exec).provider(:shell), :unless => Puppet.features.microsoft_windows? do let :resource do Puppet::Resource.new(:exec, 'foo') end let :provider do described_class.new(resource) end describe "#run" do it "should be able to run builtin shell commands" do output, status = provider.run("if [ 1 = 1 ]; then echo 'blah'; fi") status.exitstatus.should == 0 output.should == "blah\n" end it "should be able to run commands with single quotes in them" do output, status = provider.run("echo 'foo bar'") status.exitstatus.should == 0 output.should == "foo bar\n" end it "should be able to run commands with double quotes in them" do output, status = provider.run('echo "foo bar"') status.exitstatus.should == 0 output.should == "foo bar\n" end it "should be able to run multiple commands separated by a semicolon" do output, status = provider.run("echo 'foo' ; echo 'bar'") status.exitstatus.should == 0 output.should == "foo\nbar\n" end it "should be able to read values from the environment parameter" do resource[:environment] = "FOO=bar" output, status = provider.run("echo $FOO") status.exitstatus.should == 0 output.should == "bar\n" end it "#14060: should interpolate inside the subshell, not outside it" do resource[:environment] = "foo=outer" output, status = provider.run("foo=inner; echo \"foo is $foo\"") status.exitstatus.should == 0 output.should == "foo is inner\n" end end describe "#validatecmd" do it "should always return true because builtins don't need path or to be fully qualified" do provider.validatecmd('whateverdoesntmatter').should == true end end end diff --git a/spec/unit/provider/exec/windows_spec.rb b/spec/unit/provider/exec/windows_spec.rb index 5e1b5c912..9ee24b623 100755 --- a/spec/unit/provider/exec/windows_spec.rb +++ b/spec/unit/provider/exec/windows_spec.rb @@ -1,107 +1,107 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:exec).provider(:windows), :as_platform => :windows do include PuppetSpec::Files let(:resource) { Puppet::Type.type(:exec).new(:title => 'C:\foo', :provider => :windows) } let(:provider) { described_class.new(resource) } after :all do # This provider may not be suitable on some machines, so we want to reset # the default so it isn't used by mistake in future specs. Puppet::Type.type(:exec).defaultprovider = nil end describe "#extractexe" do describe "when the command has no arguments" do it "should return the command if it's quoted" do provider.extractexe('"foo"').should == 'foo' end it "should return the command if it's quoted and contains spaces" do provider.extractexe('"foo bar"').should == 'foo bar' end it "should return the command if it's not quoted" do provider.extractexe('foo').should == 'foo' end end describe "when the command has arguments" do it "should return the command if it's quoted" do provider.extractexe('"foo" bar baz').should == 'foo' end it "should return the command if it's quoted and contains spaces" do provider.extractexe('"foo bar" baz "quux quiz"').should == 'foo bar' end it "should return the command if it's not quoted" do provider.extractexe('foo bar baz').should == 'foo' end end end describe "#checkexe" do describe "when the command is absolute", :if => Puppet.features.microsoft_windows? do it "should return if the command exists and is a file" do command = tmpfile('command') FileUtils.touch(command) provider.checkexe(command).should == nil end it "should fail if the command doesn't exist" do command = tmpfile('command') expect { provider.checkexe(command) }.to raise_error(ArgumentError, "Could not find command '#{command}'") end it "should fail if the command isn't a file" do command = tmpfile('command') FileUtils.mkdir(command) expect { provider.checkexe(command) }.to raise_error(ArgumentError, "'#{command}' is a directory, not a file") end end describe "when the command is relative" do describe "and a path is specified" do before :each do provider.stubs(:which) end it "should search for executables with no extension" do provider.resource[:path] = [File.expand_path('/bogus/bin')] provider.expects(:which).with('foo').returns('foo') provider.checkexe('foo') end it "should fail if the command isn't in the path" do expect { provider.checkexe('foo') }.to raise_error(ArgumentError, "Could not find command 'foo'") end end it "should fail if no path is specified" do expect { provider.checkexe('foo') }.to raise_error(ArgumentError, "Could not find command 'foo'") end end end describe "#validatecmd" do it "should fail if the command isn't absolute and there is no path" do expect { provider.validatecmd('foo') }.to raise_error(Puppet::Error, /'foo' is not qualified and no path was specified/) end it "should not fail if the command is absolute and there is no path" do provider.validatecmd('C:\foo').should == nil end it "should not fail if the command is not absolute and there is a path" do resource[:path] = 'C:\path;C:\another_path' provider.validatecmd('foo').should == nil end end end diff --git a/spec/unit/provider/exec_spec.rb b/spec/unit/provider/exec_spec.rb index deea7748e..b6b8c3b24 100755 --- a/spec/unit/provider/exec_spec.rb +++ b/spec/unit/provider/exec_spec.rb @@ -1,35 +1,35 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/exec' describe Puppet::Provider::Exec do describe "#extractexe" do it "should return the first element of an array" do subject.extractexe(['one', 'two']).should == 'one' end { # double-quoted commands %q{"/has whitespace"} => "/has whitespace", %q{"/no/whitespace"} => "/no/whitespace", # singe-quoted commands %q{'/has whitespace'} => "/has whitespace", %q{'/no/whitespace'} => "/no/whitespace", # combinations %q{"'/has whitespace'"} => "'/has whitespace'", %q{'"/has whitespace"'} => '"/has whitespace"', %q{"/has 'special' characters"} => "/has 'special' characters", %q{'/has "special" characters'} => '/has "special" characters', # whitespace split commands %q{/has whitespace} => "/has", %q{/no/whitespace} => "/no/whitespace", }.each do |base_command, exe| ['', ' and args', ' "and args"', " 'and args'"].each do |args| command = base_command + args it "should extract #{exe.inspect} from #{command.inspect}" do subject.extractexe(command).should == exe end end end end end diff --git a/spec/unit/provider/file/posix_spec.rb b/spec/unit/provider/file/posix_spec.rb index c7a33510a..0880f00e2 100755 --- a/spec/unit/provider/file/posix_spec.rb +++ b/spec/unit/provider/file/posix_spec.rb @@ -1,232 +1,232 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).provider(:posix), :if => Puppet.features.posix? do include PuppetSpec::Files let(:path) { tmpfile('posix_file_spec') } let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => 0777, :provider => described_class.name } let(:provider) { resource.provider } describe "#mode" do it "should return a string with the higher-order bits stripped away" do FileUtils.touch(path) File.chmod(0644, path) provider.mode.should == '644' end it "should return absent if the file doesn't exist" do provider.mode.should == :absent end end describe "#mode=" do it "should chmod the file to the specified value" do FileUtils.touch(path) File.chmod(0644, path) provider.mode = '0755' provider.mode.should == '755' end it "should pass along any errors encountered" do expect do provider.mode = '644' end.to raise_error(Puppet::Error, /failed to set mode/) end end describe "#uid2name" do it "should return the name of the user identified by the id" do Etc.stubs(:getpwuid).with(501).returns(Struct::Passwd.new('jilluser', nil, 501)) provider.uid2name(501).should == 'jilluser' end it "should return the argument if it's already a name" do provider.uid2name('jilluser').should == 'jilluser' end it "should return nil if the argument is above the maximum uid" do provider.uid2name(Puppet[:maximum_uid] + 1).should == nil end it "should return nil if the user doesn't exist" do Etc.expects(:getpwuid).raises(ArgumentError, "can't find user for 999") provider.uid2name(999).should == nil end end describe "#name2uid" do it "should return the id of the user if it exists" do passwd = Struct::Passwd.new('bobbo', nil, 502) Etc.stubs(:getpwnam).with('bobbo').returns(passwd) Etc.stubs(:getpwuid).with(502).returns(passwd) provider.name2uid('bobbo').should == 502 end it "should return the argument if it's already an id" do provider.name2uid('503').should == 503 end it "should return false if the user doesn't exist" do Etc.stubs(:getpwnam).with('chuck').raises(ArgumentError, "can't find user for chuck") provider.name2uid('chuck').should == false end end describe "#owner" do it "should return the uid of the file owner" do FileUtils.touch(path) owner = File.stat(path).uid provider.owner.should == owner end it "should return absent if the file can't be statted" do provider.owner.should == :absent end it "should warn and return :silly if the value is beyond the maximum uid" do stat = stub('stat', :uid => Puppet[:maximum_uid] + 1) resource.stubs(:stat).returns(stat) provider.owner.should == :silly @logs.should be_any {|log| log.level == :warning and log.message =~ /Apparently using negative UID/} end end describe "#owner=" do it "should set the owner but not the group of the file" do File.expects(:lchown).with(15, nil, resource[:path]) provider.owner = 15 end it "should chown a link if managing links" do resource[:links] = :manage File.expects(:lchown).with(20, nil, resource[:path]) provider.owner = 20 end it "should chown a link target if following links" do resource[:links] = :follow File.expects(:chown).with(20, nil, resource[:path]) provider.owner = 20 end it "should pass along any error encountered setting the owner" do File.expects(:lchown).raises(ArgumentError) expect { provider.owner = 25 }.to raise_error(Puppet::Error, /Failed to set owner to '25'/) end end describe "#gid2name" do it "should return the name of the group identified by the id" do Etc.stubs(:getgrgid).with(501).returns(Struct::Passwd.new('unicorns', nil, nil, 501)) provider.gid2name(501).should == 'unicorns' end it "should return the argument if it's already a name" do provider.gid2name('leprechauns').should == 'leprechauns' end it "should return nil if the argument is above the maximum gid" do provider.gid2name(Puppet[:maximum_uid] + 1).should == nil end it "should return nil if the group doesn't exist" do Etc.expects(:getgrgid).raises(ArgumentError, "can't find group for 999") provider.gid2name(999).should == nil end end describe "#name2gid" do it "should return the id of the group if it exists" do passwd = Struct::Passwd.new('penguins', nil, nil, 502) Etc.stubs(:getgrnam).with('penguins').returns(passwd) Etc.stubs(:getgrgid).with(502).returns(passwd) provider.name2gid('penguins').should == 502 end it "should return the argument if it's already an id" do provider.name2gid('503').should == 503 end it "should return false if the group doesn't exist" do Etc.stubs(:getgrnam).with('wombats').raises(ArgumentError, "can't find group for wombats") provider.name2gid('wombats').should == false end end describe "#group" do it "should return the gid of the file group" do FileUtils.touch(path) group = File.stat(path).gid provider.group.should == group end it "should return absent if the file can't be statted" do provider.group.should == :absent end it "should warn and return :silly if the value is beyond the maximum gid" do stat = stub('stat', :gid => Puppet[:maximum_uid] + 1) resource.stubs(:stat).returns(stat) provider.group.should == :silly @logs.should be_any {|log| log.level == :warning and log.message =~ /Apparently using negative GID/} end end describe "#group=" do it "should set the group but not the owner of the file" do File.expects(:lchown).with(nil, 15, resource[:path]) provider.group = 15 end it "should change the group for a link if managing links" do resource[:links] = :manage File.expects(:lchown).with(nil, 20, resource[:path]) provider.group = 20 end it "should change the group for a link target if following links" do resource[:links] = :follow File.expects(:chown).with(nil, 20, resource[:path]) provider.group = 20 end it "should pass along any error encountered setting the group" do File.expects(:lchown).raises(ArgumentError) expect { provider.group = 25 }.to raise_error(Puppet::Error, /Failed to set group to '25'/) end end describe "when validating" do it "should not perform any validation" do resource.validate end end end diff --git a/spec/unit/provider/file/windows_spec.rb b/spec/unit/provider/file/windows_spec.rb index f9ab78615..409281306 100755 --- a/spec/unit/provider/file/windows_spec.rb +++ b/spec/unit/provider/file/windows_spec.rb @@ -1,154 +1,154 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' if Puppet.features.microsoft_windows? require 'puppet/util/windows' class WindowsSecurity extend Puppet::Util::Windows::Security end end describe Puppet::Type.type(:file).provider(:windows), :if => Puppet.features.microsoft_windows? do include PuppetSpec::Files let(:path) { tmpfile('windows_file_spec') } let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => 0777, :provider => described_class.name } let(:provider) { resource.provider } describe "#mode" do it "should return a string with the higher-order bits stripped away" do FileUtils.touch(path) WindowsSecurity.set_mode(0644, path) provider.mode.should == '644' end it "should return absent if the file doesn't exist" do provider.mode.should == :absent end end describe "#mode=" do it "should chmod the file to the specified value" do FileUtils.touch(path) WindowsSecurity.set_mode(0644, path) provider.mode = '0755' provider.mode.should == '755' end it "should pass along any errors encountered" do expect do provider.mode = '644' end.to raise_error(Puppet::Error, /failed to set mode/) end end describe "#id2name" do it "should return the name of the user identified by the sid" do result = [stub('user', :name => 'quinn')] Puppet::Util::ADSI.stubs(:execquery).returns(result) provider.id2name('S-1-1-50').should == 'quinn' end it "should return the argument if it's already a name" do provider.id2name('flannigan').should == 'flannigan' end it "should return nil if the user doesn't exist" do Puppet::Util::ADSI.stubs(:execquery).returns [] provider.id2name('S-1-1-50').should == nil end end describe "#name2id" do it "should return the sid of the user" do Puppet::Util::ADSI.stubs(:execquery).returns [stub('account', :Sid => 'S-1-1-50')] provider.name2id('anybody').should == 'S-1-1-50' end it "should return the argument if it's already a sid" do provider.name2id('S-1-1-50').should == 'S-1-1-50' end it "should return nil if the user doesn't exist" do Puppet::Util::ADSI.stubs(:execquery).returns [] provider.name2id('someone').should == nil end end describe "#owner" do it "should return the sid of the owner if the file does exist" do FileUtils.touch(resource[:path]) provider.stubs(:get_owner).with(resource[:path]).returns('S-1-1-50') provider.owner.should == 'S-1-1-50' end it "should return absent if the file doesn't exist" do provider.owner.should == :absent end end describe "#owner=" do it "should set the owner to the specified value" do provider.expects(:set_owner).with('S-1-1-50', resource[:path]) provider.owner = 'S-1-1-50' end it "should propagate any errors encountered when setting the owner" do provider.stubs(:set_owner).raises(ArgumentError) expect { provider.owner = 'S-1-1-50' }.to raise_error(Puppet::Error, /Failed to set owner/) end end describe "#group" do it "should return the sid of the group if the file does exist" do FileUtils.touch(resource[:path]) provider.stubs(:get_group).with(resource[:path]).returns('S-1-1-50') provider.group.should == 'S-1-1-50' end it "should return absent if the file doesn't exist" do provider.group.should == :absent end end describe "#group=" do it "should set the group to the specified value" do provider.expects(:set_group).with('S-1-1-50', resource[:path]) provider.group = 'S-1-1-50' end it "should propagate any errors encountered when setting the group" do provider.stubs(:set_group).raises(ArgumentError) expect { provider.group = 'S-1-1-50' }.to raise_error(Puppet::Error, /Failed to set group/) end end describe "when validating" do {:owner => 'foo', :group => 'foo', :mode => 0777}.each do |k,v| it "should fail if the filesystem doesn't support ACLs and we're managing #{k}" do described_class.any_instance.stubs(:supports_acl?).returns false expect { Puppet::Type.type(:file).new :path => path, k => v }.to raise_error(Puppet::Error, /Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS/) end end it "should not fail if the filesystem doesn't support ACLs and we're not managing permissions" do described_class.any_instance.stubs(:supports_acl?).returns false Puppet::Type.type(:file).new :path => path end end end diff --git a/spec/unit/provider/group/groupadd_spec.rb b/spec/unit/provider/group/groupadd_spec.rb index 585eb02af..63be8a6a5 100755 --- a/spec/unit/provider/group/groupadd_spec.rb +++ b/spec/unit/provider/group/groupadd_spec.rb @@ -1,40 +1,40 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:group).provider(:groupadd) describe provider_class do before do @resource = stub("resource", :name => "mygroup") @provider = provider_class.new(@resource) end it "should add -o when allowdupe is enabled and the group is being created" do @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @resource.stubs(:system?).returns true @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.create end it "should add -o when allowdupe is enabled and the gid is being modified" do @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.gid = 150 end it "should add -r when system is enabled and the group is being created" do @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @resource.expects(:system?).returns true @resource.stubs(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-r") } @provider.create end end diff --git a/spec/unit/provider/group/ldap_spec.rb b/spec/unit/provider/group/ldap_spec.rb index 28a57e0ee..641e02a55 100755 --- a/spec/unit/provider/group/ldap_spec.rb +++ b/spec/unit/provider/group/ldap_spec.rb @@ -1,101 +1,101 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:group).provider(:ldap) describe provider_class do it "should have the Ldap provider class as its baseclass" do provider_class.superclass.should equal(Puppet::Provider::Ldap) end it "should manage :posixGroup objectclass" do provider_class.manager.objectclasses.should == [:posixGroup] end it "should use 'ou=Groups' as its relative base" do provider_class.manager.location.should == "ou=Groups" end it "should use :cn as its rdn" do provider_class.manager.rdn.should == :cn end it "should map :name to 'cn'" do provider_class.manager.ldap_name(:name).should == 'cn' end it "should map :gid to 'gidNumber'" do provider_class.manager.ldap_name(:gid).should == 'gidNumber' end it "should map :members to 'memberUid', to be used by the user ldap provider" do provider_class.manager.ldap_name(:members).should == 'memberUid' end describe "when being created" do before do # So we don't try to actually talk to ldap @connection = mock 'connection' provider_class.manager.stubs(:connect).yields @connection end describe "with no gid specified" do it "should pick the first available GID after the largest existing GID" do low = {:name=>["luke"], :gid=>["600"]} high = {:name=>["testing"], :gid=>["640"]} provider_class.manager.expects(:search).returns([low, high]) resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:gid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["641"] } instance.create instance.flush end it "should pick '501' as its GID if no groups are found" do provider_class.manager.expects(:search).returns nil resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:gid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["gidNumber"] == ["501"] } instance.create instance.flush end end end it "should have a method for converting group names to GIDs" do provider_class.should respond_to(:name2id) end describe "when converting from a group name to GID" do it "should use the ldap manager to look up the GID" do provider_class.manager.expects(:search).with("cn=foo") provider_class.name2id("foo") end it "should return nil if no group is found" do provider_class.manager.expects(:search).with("cn=foo").returns nil provider_class.name2id("foo").should be_nil provider_class.manager.expects(:search).with("cn=bar").returns [] provider_class.name2id("bar").should be_nil end # We shouldn't ever actually have more than one gid, but it doesn't hurt # to test for the possibility. it "should return the first gid from the first returned group" do provider_class.manager.expects(:search).with("cn=foo").returns [{:name => "foo", :gid => [10, 11]}, {:name => :bar, :gid => [20, 21]}] provider_class.name2id("foo").should == 10 end end end diff --git a/spec/unit/provider/group/pw_spec.rb b/spec/unit/provider/group/pw_spec.rb index 3dfc5ec71..371ec0a78 100755 --- a/spec/unit/provider/group/pw_spec.rb +++ b/spec/unit/provider/group/pw_spec.rb @@ -1,81 +1,81 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:group).provider(:pw) describe provider_class do let :resource do Puppet::Type.type(:group).new(:name => "testgroup", :provider => :pw) end let :provider do resource.provider end describe "when creating groups" do let :provider do prov = resource.provider prov.expects(:exists?).returns nil prov end it "should run pw with no additional flags when no properties are given" do provider.addcmd.must == [provider_class.command(:pw), "groupadd", "testgroup"] provider.expects(:execute).with([provider_class.command(:pw), "groupadd", "testgroup"]) provider.create end it "should use -o when allowdupe is enabled" do resource[:allowdupe] = true provider.expects(:execute).with(includes("-o")) provider.create end it "should use -g with the correct argument when the gid property is set" do resource[:gid] = 12345 provider.expects(:execute).with(all_of(includes("-g"), includes(12345))) provider.create end it "should use -M with the correct argument when the members property is set" do resource[:members] = "user1" provider.expects(:execute).with(all_of(includes("-M"), includes("user1"))) provider.create end it "should use -M with all the given users when the members property is set to an array" do resource[:members] = ["user1", "user2"] provider.expects(:execute).with(all_of(includes("-M"), includes("user1,user2"))) provider.create end end describe "when deleting groups" do it "should run pw with no additional flags" do provider.expects(:exists?).returns true provider.deletecmd.must == [provider_class.command(:pw), "groupdel", "testgroup"] provider.expects(:execute).with([provider_class.command(:pw), "groupdel", "testgroup"]) provider.delete end end describe "when modifying groups" do it "should run pw with the correct arguments" do provider.modifycmd("gid", 12345).must == [provider_class.command(:pw), "groupmod", "testgroup", "-g", 12345] provider.expects(:execute).with([provider_class.command(:pw), "groupmod", "testgroup", "-g", 12345]) provider.gid = 12345 end it "should use -M with the correct argument when the members property is changed" do resource[:members] = "user1" provider.expects(:execute).with(all_of(includes("-M"), includes("user2"))) provider.members = "user2" end it "should use -M with all the given users when the members property is changed with an array" do resource[:members] = ["user1", "user2"] provider.expects(:execute).with(all_of(includes("-M"), includes("user3,user4"))) provider.members = ["user3", "user4"] end end end diff --git a/spec/unit/provider/host/parsed_spec.rb b/spec/unit/provider/host/parsed_spec.rb index 9cb5890cc..8a060065b 100755 --- a/spec/unit/provider/host/parsed_spec.rb +++ b/spec/unit/provider/host/parsed_spec.rb @@ -1,197 +1,197 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' require 'puppet_spec/files' provider_class = Puppet::Type.type(:host).provider(:parsed) describe provider_class do include PuppetSpec::Files before do @host_class = Puppet::Type.type(:host) @provider = @host_class.provider(:parsed) @hostfile = tmpfile('hosts') @provider.any_instance.stubs(:target).returns @hostfile end after :each do @provider.initvars end def mkhost(args) hostresource = Puppet::Type::Host.new(:name => args[:name]) hostresource.stubs(:should).with(:target).returns @hostfile # Using setters of provider to build our testobject # Note: We already proved, that in case of host_aliases # the provider setter "host_aliases=(value)" will be # called with the joined array, so we just simulate that host = @provider.new(hostresource) args.each do |property,value| value = value.join(" ") if property == :host_aliases and value.is_a?(Array) host.send("#{property}=", value) end host end def genhost(host) @provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields host.flush @provider.target_object(@hostfile).read end describe "when parsing a line with ip and hostname" do it "should parse an ipv4 from the first field" do @provider.parse_line("127.0.0.1 localhost")[:ip].should == "127.0.0.1" end it "should parse an ipv6 from the first field" do @provider.parse_line("::1 localhost")[:ip].should == "::1" end it "should parse the name from the second field" do @provider.parse_line("::1 localhost")[:name].should == "localhost" end it "should set an empty comment" do @provider.parse_line("::1 localhost")[:comment].should == "" end it "should set host_aliases to :absent" do @provider.parse_line("::1 localhost")[:host_aliases].should == :absent end end describe "when parsing a line with ip, hostname and comment" do before do @testline = "127.0.0.1 localhost # A comment with a #-char" end it "should parse the ip from the first field" do @provider.parse_line(@testline)[:ip].should == "127.0.0.1" end it "should parse the hostname from the second field" do @provider.parse_line(@testline)[:name].should == "localhost" end it "should parse the comment after the first '#' character" do @provider.parse_line(@testline)[:comment].should == 'A comment with a #-char' end end describe "when parsing a line with ip, hostname and aliases" do it "should parse alias from the third field" do @provider.parse_line("127.0.0.1 localhost localhost.localdomain")[:host_aliases].should == "localhost.localdomain" end it "should parse multiple aliases" do @provider.parse_line("127.0.0.1 host alias1 alias2")[:host_aliases].should == 'alias1 alias2' @provider.parse_line("127.0.0.1 host alias1\talias2")[:host_aliases].should == 'alias1 alias2' @provider.parse_line("127.0.0.1 host alias1\talias2 alias3")[:host_aliases].should == 'alias1 alias2 alias3' end end describe "when parsing a line with ip, hostname, aliases and comment" do before do # Just playing with a few different delimiters @testline = "127.0.0.1\t host alias1\talias2 alias3 # A comment with a #-char" end it "should parse the ip from the first field" do @provider.parse_line(@testline)[:ip].should == "127.0.0.1" end it "should parse the hostname from the second field" do @provider.parse_line(@testline)[:name].should == "host" end it "should parse all host_aliases from the third field" do @provider.parse_line(@testline)[:host_aliases].should == 'alias1 alias2 alias3' end it "should parse the comment after the first '#' character" do @provider.parse_line(@testline)[:comment].should == 'A comment with a #-char' end end describe "when operating on /etc/hosts like files" do it_should_behave_like "all parsedfile providers", provider_class, my_fixtures('valid*') it "should be able to generate a simple hostfile entry" do host = mkhost( :name => 'localhost', :ip => '127.0.0.1', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost\n" end it "should be able to generate an entry with one alias" do host = mkhost( :name => 'localhost.localdomain', :ip => '127.0.0.1', :host_aliases => 'localhost', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost.localdomain\tlocalhost\n" end it "should be able to generate an entry with more than one alias" do host = mkhost( :name => 'host', :ip => '192.0.0.1', :host_aliases => [ 'a1','a2','a3','a4' ], :ensure => :present ) genhost(host).should == "192.0.0.1\thost\ta1 a2 a3 a4\n" end it "should be able to generate a simple hostfile entry with comments" do host = mkhost( :name => 'localhost', :ip => '127.0.0.1', :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost\t# Bazinga!\n" end it "should be able to generate an entry with one alias and a comment" do host = mkhost( :name => 'localhost.localdomain', :ip => '127.0.0.1', :host_aliases => 'localhost', :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost.localdomain\tlocalhost\t# Bazinga!\n" end it "should be able to generate an entry with more than one alias and a comment" do host = mkhost( :name => 'host', :ip => '192.0.0.1', :host_aliases => [ 'a1','a2','a3','a4' ], :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "192.0.0.1\thost\ta1 a2 a3 a4\t# Bazinga!\n" end end end diff --git a/spec/unit/provider/interface/cisco_spec.rb b/spec/unit/provider/interface/cisco_spec.rb index 3d400ea4b..5a475dd1f 100755 --- a/spec/unit/provider/interface/cisco_spec.rb +++ b/spec/unit/provider/interface/cisco_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/interface/cisco' provider_class = Puppet::Type.type(:interface).provider(:cisco) describe provider_class do before do @device = stub_everything 'device' @resource = stub("resource", :name => "Fa0/1") @provider = provider_class.new(@device, @resource) end it "should have a parent of Puppet::Provider::Cisco" do provider_class.should < Puppet::Provider::Cisco end it "should have an instances method" do provider_class.should respond_to(:instances) end describe "when looking up instances at prefetch" do before do @device.stubs(:command).yields(@device) end it "should delegate to the device interface fetcher" do @device.expects(:interface) provider_class.lookup(@device, "Fa0/1") end it "should return the given interface data" do @device.expects(:interface).returns({ :description => "thisone", :mode => :access}) provider_class.lookup(@device, "Fa0").should == {:description => "thisone", :mode => :access } end end describe "when an instance is being flushed" do it "should call the device interface update method with current and past properties" do @instance = provider_class.new(@device, :ensure => :present, :name => "Fa0/1", :description => "myinterface") @instance.description = "newdesc" @instance.resource = @resource @resource.stubs(:[]).with(:name).returns("Fa0/1") device = stub_everything 'device' @instance.stubs(:device).returns(device) device.expects(:command).yields(device) interface = stub 'interface' device.expects(:new_interface).with("Fa0/1").returns(interface) interface.expects(:update).with( {:ensure => :present, :name => "Fa0/1", :description => "myinterface"}, {:ensure => :present, :name => "Fa0/1", :description => "newdesc"}) @instance.flush end end end diff --git a/spec/unit/provider/ldap_spec.rb b/spec/unit/provider/ldap_spec.rb index 913e81fb9..491d249fb 100755 --- a/spec/unit/provider/ldap_spec.rb +++ b/spec/unit/provider/ldap_spec.rb @@ -1,244 +1,244 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/ldap' describe Puppet::Provider::Ldap do before do @class = Class.new(Puppet::Provider::Ldap) end it "should be able to define its manager" do manager = mock 'manager' Puppet::Util::Ldap::Manager.expects(:new).returns manager @class.stubs :mk_resource_methods manager.expects(:manages).with(:one) @class.manages(:one).should equal(manager) @class.manager.should equal(manager) end it "should be able to prefetch instances from ldap" do @class.should respond_to(:prefetch) end it "should create its resource getter/setter methods when the manager is defined" do manager = mock 'manager' Puppet::Util::Ldap::Manager.expects(:new).returns manager @class.expects :mk_resource_methods manager.stubs(:manages) @class.manages(:one).should equal(manager) end it "should have an instances method" do @class.should respond_to(:instances) end describe "when providing a list of instances" do it "should convert all results returned from the manager's :search method into provider instances" do manager = mock 'manager' @class.stubs(:manager).returns manager manager.expects(:search).returns %w{one two three} @class.expects(:new).with("one").returns(1) @class.expects(:new).with("two").returns(2) @class.expects(:new).with("three").returns(3) @class.instances.should == [1,2,3] end end it "should have a prefetch method" do @class.should respond_to(:prefetch) end describe "when prefetching" do before do @manager = mock 'manager' @class.stubs(:manager).returns @manager @resource = mock 'resource' @resources = {"one" => @resource} end it "should find an entry for each passed resource" do @manager.expects(:find).with("one").returns nil @class.stubs(:new) @resource.stubs(:provider=) @class.prefetch(@resources) end describe "resources that do not exist" do it "should create a provider with :ensure => :absent" do result = mock 'result' @manager.expects(:find).with("one").returns nil @class.expects(:new).with(:ensure => :absent).returns "myprovider" @resource.expects(:provider=).with("myprovider") @class.prefetch(@resources) end end describe "resources that exist" do it "should create a provider with the results of the find" do @manager.expects(:find).with("one").returns("one" => "two") @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" @resource.expects(:provider=).with("myprovider") @class.prefetch(@resources) end it "should set :ensure to :present in the returned values" do @manager.expects(:find).with("one").returns("one" => "two") @class.expects(:new).with("one" => "two", :ensure => :present).returns "myprovider" @resource.expects(:provider=).with("myprovider") @class.prefetch(@resources) end end end describe "when being initialized" do it "should fail if no manager has been defined" do lambda { @class.new }.should raise_error(Puppet::DevError) end it "should fail if the manager is invalid" do manager = stub "manager", :valid? => false @class.stubs(:manager).returns manager lambda { @class.new }.should raise_error(Puppet::DevError) end describe "with a hash" do before do @manager = stub "manager", :valid? => true @class.stubs(:manager).returns @manager @resource_class = mock 'resource_class' @class.stubs(:resource_type).returns @resource_class @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class.stubs(:attrclass).with(:one).returns(@property_class) @resource_class.stubs(:valid_parameter?).returns true end it "should store a copy of the hash as its ldap_properties" do instance = @class.new(:one => :two) instance.ldap_properties.should == {:one => :two} end it "should only store the first value of each value array for those attributes that do not match all values" do @property_class.expects(:array_matching).returns :first instance = @class.new(:one => %w{two three}) instance.properties.should == {:one => "two"} end it "should store the whole value array for those attributes that match all values" do @property_class.expects(:array_matching).returns :all instance = @class.new(:one => %w{two three}) instance.properties.should == {:one => %w{two three}} end it "should only use the first value for attributes that are not properties" do # Yay. hackish, but easier than mocking everything. @resource_class.expects(:attrclass).with(:a).returns Puppet::Type.type(:user).attrclass(:name) @property_class.stubs(:array_matching).returns :all instance = @class.new(:one => %w{two three}, :a => %w{b c}) instance.properties.should == {:one => %w{two three}, :a => "b"} end it "should discard any properties not valid in the resource class" do @resource_class.expects(:valid_parameter?).with(:a).returns false @property_class.stubs(:array_matching).returns :all instance = @class.new(:one => %w{two three}, :a => %w{b}) instance.properties.should == {:one => %w{two three}} end end end describe "when an instance" do before do @manager = stub "manager", :valid? => true @class.stubs(:manager).returns @manager @instance = @class.new @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:one, :two] @class.stubs(:resource_type).returns @resource_class end it "should have a method for creating the ldap entry" do @instance.should respond_to(:create) end it "should have a method for removing the ldap entry" do @instance.should respond_to(:delete) end it "should have a method for returning the class's manager" do @instance.manager.should equal(@manager) end it "should indicate when the ldap entry already exists" do @instance = @class.new(:ensure => :present) @instance.exists?.should be_true end it "should indicate when the ldap entry does not exist" do @instance = @class.new(:ensure => :absent) @instance.exists?.should be_false end describe "is being flushed" do it "should call the manager's :update method with its name, current attributes, and desired attributes" do @instance.stubs(:name).returns "myname" @instance.stubs(:ldap_properties).returns(:one => :two) @instance.stubs(:properties).returns(:three => :four) @manager.expects(:update).with(@instance.name, {:one => :two}, {:three => :four}) @instance.flush end end describe "is being created" do before do @rclass = mock 'resource_class' @rclass.stubs(:validproperties).returns([:one, :two]) @resource = mock 'resource' @resource.stubs(:class).returns @rclass @resource.stubs(:should).returns nil @instance.stubs(:resource).returns @resource end it "should set its :ensure value to :present" do @instance.create @instance.properties[:ensure].should == :present end it "should set all of the other attributes from the resource" do @resource.expects(:should).with(:one).returns "oneval" @resource.expects(:should).with(:two).returns "twoval" @instance.create @instance.properties[:one].should == "oneval" @instance.properties[:two].should == "twoval" end end describe "is being deleted" do it "should set its :ensure value to :absent" do @instance.delete @instance.properties[:ensure].should == :absent end end end end diff --git a/spec/unit/provider/macauthorization_spec.rb b/spec/unit/provider/macauthorization_spec.rb index dbe36a04b..fb5c0dc8e 100755 --- a/spec/unit/provider/macauthorization_spec.rb +++ b/spec/unit/provider/macauthorization_spec.rb @@ -1,152 +1,152 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the macauthorization provider # require 'spec_helper' require 'puppet' require 'facter/util/plist' provider_class = Puppet::Type.type(:macauthorization).provider(:macauthorization) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' @authname = "foo.spam.eggs.puppettest" @authplist = {} @rules = {@authname => @authplist} authdb = {} authdb["rules"] = { "foorule" => "foo" } authdb["rights"] = { "fooright" => "foo" } # Stub out Plist::parse_xml Plist.stubs(:parse_xml).returns(authdb) # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure @resource.stubs(:[]).with(:name).returns @authname @resource.stubs(:[]).with(:ensure).returns :present @resource.stubs(:ref).returns "MacAuthorization[#{@authname}]" @provider = provider_class.new(@resource) end it "should have a create method" do @provider.should respond_to(:create) end it "should have a destroy method" do @provider.should respond_to(:destroy) end it "should have an exists? method" do @provider.should respond_to(:exists?) end it "should have a flush method" do @provider.should respond_to(:flush) end properties = [ :allow_root, :authenticate_user, :auth_class, :comment, :group, :k_of_n, :mechanisms, :rule, :session_owner, :shared, :timeout, :tries, :auth_type ] properties.each do |prop| it "should have a #{prop.to_s} method" do @provider.should respond_to(prop.to_s) end it "should have a #{prop.to_s}= method" do @provider.should respond_to(prop.to_s + "=") end end describe "when destroying a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method destroy_right" do @provider.expects(:destroy_right) @provider.destroy end it "should call the external command 'security authorizationdb remove @authname" do @provider.expects(:security).with("authorizationdb", :remove, @authname) @provider.destroy end end describe "when destroying a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method destroy_rule" do @provider.expects(:destroy_rule) @provider.destroy end end describe "when flushing a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method flush_right" do @provider.expects(:flush_right) @provider.flush end it "should call the internal method set_right" do @provider.expects(:execute).with { |cmds, args| cmds.include?("read") and cmds.include?(@authname) and args[:combine] == false }.once @provider.expects(:set_right) @provider.flush end it "should read and write to the auth database with the right arguments" do @provider.expects(:execute).with { |cmds, args| cmds.include?("read") and cmds.include?(@authname) and args[:combine] == false }.once @provider.expects(:execute).with { |cmds, args| cmds.include?("write") and cmds.include?(@authname) and args[:combine] == false and args[:stdinfile] != nil }.once @provider.flush end end describe "when flushing a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method flush_rule" do @provider.expects(:flush_rule) @provider.flush end it "should call the internal method set_rule" do @provider.expects(:set_rule) @provider.flush end end end diff --git a/spec/unit/provider/mcx/mcxcontent_spec.rb b/spec/unit/provider/mcx/mcxcontent_spec.rb index 4676575be..87897307b 100755 --- a/spec/unit/provider/mcx/mcxcontent_spec.rb +++ b/spec/unit/provider/mcx/mcxcontent_spec.rb @@ -1,156 +1,156 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:mcx).provider(:mcxcontent) # describe creates a new ExampleGroup object. describe provider_class do # :each executes before each test. # :all executes once for the test group and before :each. before :each do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @attached_to = "/Users/foobar" @ds_path = "/Local/Default/Users/foobar" # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure and enable @resource.stubs(:[]).with(:name).returns @attached_to @resource.stubs(:[]).with(:ensure).returns :present @resource.stubs(:ref).returns "Mcx[#{@attached_to}]" # stub out the provider methods that actually touch the filesystem # or execute commands @provider.class.stubs(:execute).returns('') @provider.stubs(:execute).returns('') @provider.stubs(:resource).returns @resource end it "should have a create method." do @provider.should respond_to(:create) end it "should have a destroy method." do @provider.should respond_to(:destroy) end it "should have an exists? method." do @provider.should respond_to(:exists?) end it "should have an content method." do @provider.should respond_to(:content) end it "should have an content= method." do @provider.should respond_to(:content=) end describe "when managing the resource" do it "should execute external command dscl from :create" do @provider.class.expects(:dscl).returns('').once @provider.create end it "should execute external command dscl from :destroy" do @provider.class.expects(:dscl).with('localhost', '-mcxdelete', @ds_path).returns('').once @provider.destroy end it "should execute external command dscl from :exists?" do @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('').once @provider.exists? end it "should execute external command dscl from :content" do @provider.class.expects(:dscl).with('localhost', '-mcxexport', @ds_path).returns('') @provider.content end it "should execute external command dscl from :content=" do @provider.class.expects(:dscl).returns('') @provider.content='' end end describe "when creating and parsing the name for ds_type" do before :each do @resource.stubs(:[]).with(:name).returns "/Foo/bar" end it "should not accept /Foo/bar" do lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should accept /Foo/bar with ds_type => user" do @resource.stubs(:[]).with(:ds_type).returns "user" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept /Foo/bar with ds_type => group" do @resource.stubs(:[]).with(:ds_type).returns "group" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept /Foo/bar with ds_type => computer" do @resource.stubs(:[]).with(:ds_type).returns "computer" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :name => /Foo/bar with ds_type => computerlist" do @resource.stubs(:[]).with(:ds_type).returns "computerlist" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end end describe "when creating and :name => foobar" do before :each do @resource.stubs(:[]).with(:name).returns "foobar" end it "should not accept unspecified :ds_type and :ds_name" do lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should not accept unspecified :ds_type" do @resource.stubs(:[]).with(:ds_type).returns "user" lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should not accept unspecified :ds_name" do @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should raise_error(MCXContentProviderException) end it "should accept :ds_type => user, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "user" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :ds_type => group, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "group" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :ds_type => computer, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "computer" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should accept :ds_type => computerlist, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "computerlist" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should_not raise_error(MCXContentProviderException) end it "should not accept :ds_type => bogustype, ds_name => foo" do @resource.stubs(:[]).with(:ds_type).returns "bogustype" @resource.stubs(:[]).with(:ds_name).returns "foo" lambda { @provider.create }.should raise_error(MCXContentProviderException) end end describe "when gathering existing instances" do it "should define an instances class method." do @provider.class.should respond_to(:instances) end it "should call external command dscl -list /Local/Default/ on each known ds_type" do @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Users").returns('') @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Groups").returns('') @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/Computers").returns('') @provider.class.expects(:dscl).with('localhost', '-list', "/Local/Default/ComputerLists").returns('') @provider.class.instances end end end diff --git a/spec/unit/provider/mount/parsed_spec.rb b/spec/unit/provider/mount/parsed_spec.rb index 8cb6efbc3..94feb5674 100755 --- a/spec/unit/provider/mount/parsed_spec.rb +++ b/spec/unit/provider/mount/parsed_spec.rb @@ -1,289 +1,289 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' provider_class = Puppet::Type.type(:mount).provider(:parsed) describe provider_class, :unless => Puppet.features.microsoft_windows? do before :each do @mount_class = Puppet::Type.type(:mount) @provider = @mount_class.provider(:parsed) end # LAK:FIXME I can't mock Facter because this test happens at parse-time. it "should default to /etc/vfstab on Solaris" do pending "This test only works on Solaris" unless Facter.value(:operatingsystem) == 'Solaris' Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/vfstab' end it "should default to /etc/fstab on anything else" do pending "This test does not work on Solaris" if Facter.value(:operatingsystem) == 'Solaris' Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/fstab' end describe "when parsing a line" do it "should not crash on incomplete lines in fstab" do parse = @provider.parse <<-FSTAB /dev/incomplete /dev/device name FSTAB lambda{ @provider.to_line(parse[0]) }.should_not raise_error end # it_should_behave_like "all parsedfile providers", # provider_class, my_fixtures('*.fstab') describe "on Solaris", :if => Facter.value(:operatingsystem) == 'Solaris' do before :each do @example_line = "/dev/dsk/c0d0s0 /dev/rdsk/c0d0s0 \t\t / \t ufs 1 no\t-" end it "should extract device from the first field" do @provider.parse_line(@example_line)[:device].should == '/dev/dsk/c0d0s0' end it "should extract blockdevice from second field" do @provider.parse_line(@example_line)[:blockdevice].should == "/dev/rdsk/c0d0s0" end it "should extract name from third field" do @provider.parse_line(@example_line)[:name].should == "/" end it "should extract fstype from fourth field" do @provider.parse_line(@example_line)[:fstype].should == "ufs" end it "should extract pass from fifth field" do @provider.parse_line(@example_line)[:pass].should == "1" end it "should extract atboot from sixth field" do @provider.parse_line(@example_line)[:atboot].should == "no" end it "should extract options from seventh field" do @provider.parse_line(@example_line)[:options].should == "-" end end describe "on other platforms than Solaris", :if => Facter.value(:operatingsystem) != 'Solaris' do before :each do @example_line = "/dev/vg00/lv01\t/spare \t \t ext3 defaults\t1 2" end it "should extract device from the first field" do @provider.parse_line(@example_line)[:device].should == '/dev/vg00/lv01' end it "should extract name from second field" do @provider.parse_line(@example_line)[:name].should == "/spare" end it "should extract fstype from third field" do @provider.parse_line(@example_line)[:fstype].should == "ext3" end it "should extract options from fourth field" do @provider.parse_line(@example_line)[:options].should == "defaults" end it "should extract dump from fifth field" do @provider.parse_line(@example_line)[:dump].should == "1" end it "should extract options from sixth field" do @provider.parse_line(@example_line)[:pass].should == "2" end end end describe "mountinstances" do it "should get name from mountoutput found on Solaris" do Facter.stubs(:value).with(:operatingsystem).returns 'Solaris' @provider.stubs(:mountcmd).returns(File.read(my_fixture('solaris.mount'))) mounts = @provider.mountinstances mounts.size.should == 6 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/proc', :mounted => :yes } mounts[2].should == { :name => '/etc/mnttab', :mounted => :yes } mounts[3].should == { :name => '/tmp', :mounted => :yes } mounts[4].should == { :name => '/export/home', :mounted => :yes } mounts[5].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on HP-UX" do Facter.stubs(:value).with(:operatingsystem).returns 'HP-UX' @provider.stubs(:mountcmd).returns(File.read(my_fixture('hpux.mount'))) mounts = @provider.mountinstances mounts.size.should == 17 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/devices', :mounted => :yes } mounts[2].should == { :name => '/dev', :mounted => :yes } mounts[3].should == { :name => '/system/contract', :mounted => :yes } mounts[4].should == { :name => '/proc', :mounted => :yes } mounts[5].should == { :name => '/etc/mnttab', :mounted => :yes } mounts[6].should == { :name => '/etc/svc/volatile', :mounted => :yes } mounts[7].should == { :name => '/system/object', :mounted => :yes } mounts[8].should == { :name => '/etc/dfs/sharetab', :mounted => :yes } mounts[9].should == { :name => '/lib/libc.so.1', :mounted => :yes } mounts[10].should == { :name => '/dev/fd', :mounted => :yes } mounts[11].should == { :name => '/tmp', :mounted => :yes } mounts[12].should == { :name => '/var/run', :mounted => :yes } mounts[13].should == { :name => '/export', :mounted => :yes } mounts[14].should == { :name => '/export/home', :mounted => :yes } mounts[15].should == { :name => '/rpool', :mounted => :yes } mounts[16].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on Darwin" do Facter.stubs(:value).with(:operatingsystem).returns 'Darwin' @provider.stubs(:mountcmd).returns(File.read(my_fixture('darwin.mount'))) mounts = @provider.mountinstances mounts.size.should == 6 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/dev', :mounted => :yes } mounts[2].should == { :name => '/net', :mounted => :yes } mounts[3].should == { :name => '/home', :mounted => :yes } mounts[4].should == { :name => '/usr', :mounted => :yes } mounts[5].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on Linux" do Facter.stubs(:value).with(:operatingsystem).returns 'Gentoo' @provider.stubs(:mountcmd).returns(File.read(my_fixture('linux.mount'))) mounts = @provider.mountinstances mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/lib64/rc/init.d', :mounted => :yes } mounts[2].should == { :name => '/sys', :mounted => :yes } mounts[3].should == { :name => '/usr/portage', :mounted => :yes } mounts[4].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on AIX" do Facter.stubs(:value).with(:operatingsystem).returns 'AIX' @provider.stubs(:mountcmd).returns(File.read(my_fixture('aix.mount'))) mounts = @provider.mountinstances mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/tmp', :mounted => :yes } mounts[2].should == { :name => '/home', :mounted => :yes } mounts[3].should == { :name => '/usr', :mounted => :yes } mounts[4].should == { :name => '/usr/code', :mounted => :yes } end it "should raise an error if a line is not understandable" do @provider.stubs(:mountcmd).returns("bazinga!") lambda { @provider.mountinstances }.should raise_error Puppet::Error end end it "should support AIX's paragraph based /etc/filesystems" my_fixtures('*.fstab').each do |fstab| platform = File.basename(fstab, '.fstab') describe "when calling instances on #{platform}" do before :each do if Facter[:operatingsystem] == "Solaris" then platform == 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" else platform != 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" end # Stub the mount output to our fixture. begin mount = my_fixture(platform + '.mount') @provider.stubs(:mountcmd).returns File.read(mount) rescue pending "is #{platform}.mount missing at this point?" end # Note: we have to stub default_target before creating resources # because it is used by Puppet::Type::Mount.new to populate the # :target property. @provider.stubs(:default_target).returns fstab @retrieve = @provider.instances.collect { |prov| {:name => prov.get(:name), :ensure => prov.get(:ensure)}} end # Following mountpoint are present in all fstabs/mountoutputs it "should include unmounted resources" do @retrieve.should include(:name => '/', :ensure => :mounted) end it "should include mounted resources" do @retrieve.should include(:name => '/boot', :ensure => :unmounted) end it "should include ghost resources" do @retrieve.should include(:name => '/ghost', :ensure => :ghost) end end describe "when prefetching on #{platform}" do before :each do if Facter[:operatingsystem] == "Solaris" then platform == 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" else platform != 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" end # Stub the mount output to our fixture. begin mount = my_fixture(platform + '.mount') @provider.stubs(:mountcmd).returns File.read(mount) rescue pending "is #{platform}.mount missing at this point?" end # Note: we have to stub default_target before creating resources # because it is used by Puppet::Type::Mount.new to populate the # :target property. @provider.stubs(:default_target).returns fstab @res_ghost = Puppet::Type::Mount.new(:name => '/ghost') # in no fake fstab @res_mounted = Puppet::Type::Mount.new(:name => '/') # in every fake fstab @res_unmounted = Puppet::Type::Mount.new(:name => '/boot') # in every fake fstab @res_absent = Puppet::Type::Mount.new(:name => '/absent') # in no fake fstab # Simulate transaction.rb:prefetch @resource_hash = {} [@res_ghost, @res_mounted, @res_unmounted, @res_absent].each do |resource| @resource_hash[resource.name] = resource end end it "should set :ensure to :unmounted if found in fstab but not mounted" do @provider.prefetch(@resource_hash) @res_unmounted.provider.get(:ensure).should == :unmounted end it "should set :ensure to :ghost if not found in fstab but mounted" do @provider.prefetch(@resource_hash) @res_ghost.provider.get(:ensure).should == :ghost end it "should set :ensure to :mounted if found in fstab and mounted" do @provider.prefetch(@resource_hash) @res_mounted.provider.get(:ensure).should == :mounted end it "should set :ensure to :absent if not found in fstab and not mounted" do @provider.prefetch(@resource_hash) @res_absent.provider.get(:ensure).should == :absent end end end end diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index 963bfba7c..499c79380 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -1,145 +1,145 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/mount' describe Puppet::Provider::Mount do before :each do @mounter = Object.new @mounter.extend(Puppet::Provider::Mount) @name = "/" @resource = stub 'resource' @resource.stubs(:[]).with(:name).returns(@name) @mounter.stubs(:resource).returns(@resource) end describe Puppet::Provider::Mount, " when mounting" do before :each do @mounter.stubs(:get).with(:ensure).returns(:mounted) end it "should use the 'mountcmd' method to mount" do @mounter.stubs(:options).returns(nil) @mounter.expects(:mountcmd) @mounter.mount end it "should add the options following '-o' if they exist and are not set to :absent" do @mounter.stubs(:options).returns("ro") @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } @mounter.mount end it "should specify the filesystem name to the mount command" do @mounter.stubs(:options).returns(nil) @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } @mounter.mount end it "should update the :ensure state to :mounted if it was :unmounted before" do @mounter.expects(:mountcmd) @mounter.stubs(:options).returns(nil) @mounter.expects(:get).with(:ensure).returns(:unmounted) @mounter.expects(:set).with(:ensure => :mounted) @mounter.mount end it "should update the :ensure state to :ghost if it was :absent before" do @mounter.expects(:mountcmd) @mounter.stubs(:options).returns(nil) @mounter.expects(:get).with(:ensure).returns(:absent) @mounter.expects(:set).with(:ensure => :ghost) @mounter.mount end end describe Puppet::Provider::Mount, " when remounting" do it "should use '-o remount' if the resource specifies it supports remounting" do @mounter.stubs(:info) @resource.stubs(:[]).with(:remounts).returns(:true) @mounter.expects(:mountcmd).with("-o", "remount", @name) @mounter.remount end it "should unmount and mount if the resource does not specify it supports remounting" do @mounter.stubs(:info) @resource.stubs(:[]).with(:remounts).returns(false) @mounter.expects(:unmount) @mounter.expects(:mount) @mounter.remount end it "should log that it is remounting" do @resource.stubs(:[]).with(:remounts).returns(:true) @mounter.stubs(:mountcmd) @mounter.expects(:info).with("Remounting") @mounter.remount end end describe Puppet::Provider::Mount, " when unmounting" do before :each do @mounter.stubs(:get).with(:ensure).returns(:unmounted) end it "should call the :umount command with the resource name" do @mounter.expects(:umount).with(@name) @mounter.unmount end it "should update the :ensure state to :absent if it was :ghost before" do @mounter.expects(:umount).with(@name).returns true @mounter.expects(:get).with(:ensure).returns(:ghost) @mounter.expects(:set).with(:ensure => :absent) @mounter.unmount end it "should update the :ensure state to :unmounted if it was :mounted before" do @mounter.expects(:umount).with(@name).returns true @mounter.expects(:get).with(:ensure).returns(:mounted) @mounter.expects(:set).with(:ensure => :unmounted) @mounter.unmount end end describe Puppet::Provider::Mount, " when determining if it is mounted" do it "should query the property_hash" do @mounter.expects(:get).with(:ensure).returns(:mounted) @mounter.mounted? end it "should return true if prefetched value is :mounted" do @mounter.stubs(:get).with(:ensure).returns(:mounted) @mounter.mounted? == true end it "should return true if prefetched value is :ghost" do @mounter.stubs(:get).with(:ensure).returns(:ghost) @mounter.mounted? == true end it "should return false if prefetched value is :absent" do @mounter.stubs(:get).with(:ensure).returns(:absent) @mounter.mounted? == false end it "should return false if prefetched value is :unmounted" do @mounter.stubs(:get).with(:ensure).returns(:unmounted) @mounter.mounted? == false end end end diff --git a/spec/unit/provider/naginator_spec.rb b/spec/unit/provider/naginator_spec.rb index 1d8e78015..e95094621 100755 --- a/spec/unit/provider/naginator_spec.rb +++ b/spec/unit/provider/naginator_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/naginator' describe Puppet::Provider::Naginator do before do @resource_type = stub 'resource_type', :name => :nagios_test @class = Class.new(Puppet::Provider::Naginator) @class.stubs(:resource_type).returns @resource_type end it "should be able to look up the associated Nagios type" do nagios_type = mock "nagios_type" nagios_type.stubs :attr_accessor Nagios::Base.expects(:type).with(:test).returns nagios_type @class.nagios_type.should equal(nagios_type) end it "should use the Nagios type to determine whether an attribute is valid" do nagios_type = mock "nagios_type" nagios_type.stubs :attr_accessor Nagios::Base.expects(:type).with(:test).returns nagios_type nagios_type.expects(:parameters).returns [:foo, :bar] @class.valid_attr?(:test, :foo).should be_true end it "should use Naginator to parse configuration snippets" do parser = mock 'parser' parser.expects(:parse).with("my text").returns "my instances" Nagios::Parser.expects(:new).returns(parser) @class.parse("my text").should == "my instances" end it "should join Nagios::Base records with '\\n' when asked to convert them to text" do @class.expects(:header).returns "myheader\n" @class.to_file([:one, :two]).should == "myheader\none\ntwo" end it "should be able to prefetch instance from configuration files" do @class.should respond_to(:prefetch) end it "should be able to generate a list of instances" do @class.should respond_to(:instances) end it "should never skip records" do @class.should_not be_skip_record("foo") end end diff --git a/spec/unit/provider/nameservice/directoryservice_spec.rb b/spec/unit/provider/nameservice/directoryservice_spec.rb index a09894385..5f8008e1c 100755 --- a/spec/unit/provider/nameservice/directoryservice_spec.rb +++ b/spec/unit/provider/nameservice/directoryservice_spec.rb @@ -1,189 +1,189 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' # We use this as a reasonable way to obtain all the support infrastructure. [:user, :group].each do |type_for_this_round| provider_class = Puppet::Type.type(type_for_this_round).provider(:directoryservice) describe provider_class do before do @resource = stub("resource") @provider = provider_class.new(@resource) end it "[#6009] should handle nested arrays of members" do current = ["foo", "bar", "baz"] desired = ["foo", ["quux"], "qorp"] group = 'example' @resource.stubs(:[]).with(:name).returns(group) @resource.stubs(:[]).with(:auth_membership).returns(true) @provider.instance_variable_set(:@property_value_cache_hash, { :members => current }) %w{bar baz}.each do |del| @provider.expects(:execute).once. with([:dseditgroup, '-o', 'edit', '-n', '.', '-d', del, group]) end %w{quux qorp}.each do |add| @provider.expects(:execute).once. with([:dseditgroup, '-o', 'edit', '-n', '.', '-a', add, group]) end expect { @provider.set(:members, desired) }.should_not raise_error end end end describe 'DirectoryService.single_report' do it 'should fail on OS X < 10.5' do Puppet::Provider::NameService::DirectoryService.stubs(:get_macosx_version_major).returns("10.4") lambda { Puppet::Provider::NameService::DirectoryService.single_report('resource_name') }.should raise_error(RuntimeError, "Puppet does not support OS X versions < 10.5") end it 'should use plist data on >= 10.5' do Puppet::Provider::NameService::DirectoryService.stubs(:get_macosx_version_major).returns("10.5") Puppet::Provider::NameService::DirectoryService.stubs(:get_ds_path).returns('Users') Puppet::Provider::NameService::DirectoryService.stubs(:list_all_present).returns( ['root', 'user1', 'user2', 'resource_name'] ) Puppet::Provider::NameService::DirectoryService.stubs(:generate_attribute_hash) Puppet::Provider::NameService::DirectoryService.stubs(:execute) Puppet::Provider::NameService::DirectoryService.expects(:parse_dscl_plist_data) Puppet::Provider::NameService::DirectoryService.single_report('resource_name') end end describe 'DirectoryService.get_exec_preamble' do it 'should fail on OS X < 10.5' do Puppet::Provider::NameService::DirectoryService.stubs(:get_macosx_version_major).returns("10.4") lambda { Puppet::Provider::NameService::DirectoryService.get_exec_preamble('-list') }.should raise_error(RuntimeError, "Puppet does not support OS X versions < 10.5") end it 'should use plist data on >= 10.5' do Puppet::Provider::NameService::DirectoryService.stubs(:get_macosx_version_major).returns("10.5") Puppet::Provider::NameService::DirectoryService.stubs(:get_ds_path).returns('Users') Puppet::Provider::NameService::DirectoryService.get_exec_preamble('-list').should include("-plist") end end describe 'DirectoryService password behavior' do # The below is a binary plist containing a ShadowHashData key which CONTAINS # another binary plist. The nested binary plist contains a 'SALTED-SHA512' # key that contains a base64 encoded salted-SHA512 password hash... let (:binary_plist) { "bplist00\324\001\002\003\004\005\006\a\bXCRAM-MD5RNT]SALTED-SHA512[RECOVERABLEO\020 \231k2\3360\200GI\201\355J\216\202\215y\243\001\206J\300\363\032\031\022\006\2359\024\257\217<\361O\020\020F\353\at\377\277\226\276c\306\254\031\037J(\235O\020D\335\006{\3744g@\377z\204\322\r\332t\021\330\n\003\246K\223\356\034!P\261\305t\035\346\352p\206\003n\247MMA\310\301Z<\366\246\023\0161W3\340\357\000\317T\t\301\311+\204\246L7\276\370\320*\245O\021\002\000k\024\221\270x\353\001\237\346D}\377?\265]\356+\243\v[\350\316a\340h\376<\322\266\327\016\306n\272r\t\212A\253L\216\214\205\016\241 [\360/\335\002#\\A\372\241a\261\346\346\\\251\330\312\365\016\n\341\017\016\225&;\322\\\004*\ru\316\372\a \362?8\031\247\231\030\030\267\315\023\v\343{@\227\301s\372h\212\000a\244&\231\366\nt\277\2036,\027bZ+\223W\212g\333`\264\331N\306\307\362\257(^~ b\262\247&\231\261t\341\231%\244\247\203eOt\365\271\201\273\330\350\363C^A\327F\214!\217hgf\e\320k\260n\315u~\336\371M\t\235k\230S\375\311\303\240\351\037d\273\321y\335=K\016`_\317\230\2612_\023K\036\350\v\232\323Y\310\317_\035\227%\237\v\340\023\016\243\233\025\306:\227\351\370\364x\234\231\266\367\016w\275\333-\351\210}\375x\034\262\272kRuHa\362T/F!\347B\231O`K\304\037'k$$\245h)e\363\365mT\b\317\\2\361\026\351\254\375Jl1~\r\371\267\352\2322I\341\272\376\243^Un\266E7\230[VocUJ\220N\2116D/\025f=\213\314\325\vG}\311\360\377DT\307m\261&\263\340\272\243_\020\271rG^BW\210\030l\344\0324\335\233\300\023\272\225Im\330\n\227*Yv[\006\315\330y'\a\321\373\273A\240\305F{S\246I#/\355\2425\031\031GGF\270y\n\331\004\023G@\331\000\361\343\350\264$\032\355_\210y\000\205\342\375\212q\024\004\026W:\205 \363v?\035\270L-\270=\022\323\2003\v\336\277\t\237\356\374\n\267n\003\367\342\330;\371S\326\016`B6@Njm>\240\021%\336\345\002(P\204Yn\3279l\0228\264\254\304\2528t\372h\217\347sA\314\345\245\337)]\000\b\000\021\000\032\000\035\000+\0007\000Z\000m\000\264\000\000\000\000\000\000\002\001\000\000\000\000\000\000\000\t\000\000\000\000\000\000\000\000\000\000\000\000\000\000\002\270" } # The below is a base64 encoded salted-SHA512 password hash. let (:pw_string) { "\335\006{\3744g@\377z\204\322\r\332t\021\330\n\003\246K\223\356\034!P\261\305t\035\346\352p\206\003n\247MMA\310\301Z<\366\246\023\0161W3\340\357\000\317T\t\301\311+\204\246L7\276\370\320*\245" } # The below is a salted-SHA512 password hash in hex. let (:sha512_hash) { 'dd067bfc346740ff7a84d20dda7411d80a03a64b93ee1c2150b1c5741de6ea7086036ea74d4d41c8c15a3cf6a6130e315733e0ef00cf5409c1c92b84a64c37bef8d02aa5' } let :plist_path do '/var/db/dslocal/nodes/Default/users/jeff.plist' end let :ds_provider do Puppet::Provider::NameService::DirectoryService end let :shadow_hash_data do {'ShadowHashData' => [StringIO.new(binary_plist)]} end subject do Puppet::Provider::NameService::DirectoryService end before :each do subject.expects(:get_macosx_version_major).returns("10.7") end it 'should execute convert_binary_to_xml once when getting the password on >= 10.7' do subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)}) File.expects(:exists?).with(plist_path).once.returns(true) Plist.expects(:parse_xml).returns(shadow_hash_data) # On Mac OS X 10.7 we first need to convert to xml when reading the password subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) subject.get_password('uid', 'jeff') end it 'should fail if a salted-SHA512 password hash is not passed in >= 10.7' do expect { subject.set_password('jeff', 'uid', 'badpassword') }.should raise_error(RuntimeError, /OS X 10.7 requires a Salted SHA512 hash password of 136 characters./) end it 'should convert xml-to-binary and binary-to-xml when setting the pw on >= 10.7' do subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)}) subject.expects(:convert_xml_to_binary).returns(binary_plist) File.expects(:exists?).with(plist_path).once.returns(true) Plist.expects(:parse_xml).returns(shadow_hash_data) # On Mac OS X 10.7 we first need to convert to xml subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) # And again back to a binary plist or DirectoryService will complain subject.expects(:plutil).with('-convert', 'binary1', plist_path) Plist::Emit.expects(:save_plist).with(shadow_hash_data, plist_path) subject.set_password('jeff', 'uid', sha512_hash) end it '[#13686] should handle an empty ShadowHashData field in the users plist' do subject.expects(:convert_xml_to_binary).returns(binary_plist) File.expects(:exists?).with(plist_path).once.returns(true) Plist.expects(:parse_xml).returns({'ShadowHashData' => nil}) subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path) subject.expects(:plutil).with('-convert', 'binary1', plist_path) Plist::Emit.expects(:save_plist) subject.set_password('jeff', 'uid', sha512_hash) end end describe '(#4855) directoryservice group resource failure' do let :provider_class do Puppet::Type.type(:group).provider(:directoryservice) end let :group_members do ['root','jeff'] end let :user_account do ['root'] end let :stub_resource do stub('resource') end subject do provider_class.new(stub_resource) end before :each do @resource = stub("resource") @provider = provider_class.new(@resource) end it 'should delete a group member if the user does not exist' do stub_resource.stubs(:[]).with(:name).returns('fake_group') stub_resource.stubs(:name).returns('fake_group') subject.expects(:execute).with([:dseditgroup, '-o', 'edit', '-n', '.', '-d', 'jeff', 'fake_group']).raises(Puppet::ExecutionFailure, 'it broke') subject.expects(:execute).with([:dscl, '.', '-delete', '/Groups/fake_group', 'GroupMembership', 'jeff']) subject.remove_unwanted_members(group_members, user_account) end end diff --git a/spec/unit/provider/network_device_spec.rb b/spec/unit/provider/network_device_spec.rb index aae6ad68a..bf0001d5d 100755 --- a/spec/unit/provider/network_device_spec.rb +++ b/spec/unit/provider/network_device_spec.rb @@ -1,152 +1,152 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/network_device' require 'ostruct' Puppet::Type.type(:vlan).provide :test, :parent => Puppet::Provider::NetworkDevice do mk_resource_methods def self.lookup(device, name) end def self.device(url) :device end end provider_class = Puppet::Type.type(:vlan).provider(:test) describe provider_class do before do @resource = stub("resource", :name => "test") @provider = provider_class.new(@resource) end it "should be able to prefetch instances from the device" do provider_class.should respond_to(:prefetch) end it "should have an instances method" do provider_class.should respond_to(:instances) end describe "when prefetching" do before do @resource = stub_everything 'resource' @resources = {"200" => @resource} provider_class.stubs(:lookup) end it "should lookup an entry for each passed resource" do provider_class.expects(:lookup).with{ |device,value| value == "200" }.returns nil provider_class.stubs(:new) @resource.stubs(:provider=) provider_class.prefetch(@resources) end describe "resources that do not exist" do it "should create a provider with :ensure => :absent" do provider_class.stubs(:lookup).returns(nil) provider_class.expects(:new).with(:device, :ensure => :absent).returns "myprovider" @resource.expects(:provider=).with("myprovider") provider_class.prefetch(@resources) end end describe "resources that exist" do it "should create a provider with the results of the find and ensure at present" do provider_class.stubs(:lookup).returns({ :name => "200", :description => "myvlan"}) provider_class.expects(:new).with(:device, :name => "200", :description => "myvlan", :ensure => :present).returns "myprovider" @resource.expects(:provider=).with("myprovider") provider_class.prefetch(@resources) end end end describe "when being initialized" do describe "with a hash" do before do @resource_class = mock 'resource_class' provider_class.stubs(:resource_type).returns @resource_class @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class.stubs(:attrclass).with(:one).returns(@property_class) @resource_class.stubs(:valid_parameter?).returns true end it "should store a copy of the hash as its vlan_properties" do instance = provider_class.new(:device, :one => :two) instance.former_properties.should == {:one => :two} end end end describe "when an instance" do before do @instance = provider_class.new(:device) @property_class = stub 'property_class', :array_matching => :all, :superclass => Puppet::Property @resource_class = stub 'resource_class', :attrclass => @property_class, :valid_parameter? => true, :validproperties => [:description] provider_class.stubs(:resource_type).returns @resource_class end it "should have a method for creating the instance" do @instance.should respond_to(:create) end it "should have a method for removing the instance" do @instance.should respond_to(:destroy) end it "should indicate when the instance already exists" do @instance = provider_class.new(:device, :ensure => :present) @instance.exists?.should be_true end it "should indicate when the instance does not exist" do @instance = provider_class.new(:device, :ensure => :absent) @instance.exists?.should be_false end describe "is being flushed" do it "should flush properties" do @instance = provider_class.new(:ensure => :present, :name => "200", :description => "myvlan") @instance.flush @instance.properties.should be_empty end end describe "is being created" do before do @rclass = mock 'resource_class' @rclass.stubs(:validproperties).returns([:description]) @resource = stub_everything 'resource' @resource.stubs(:class).returns @rclass @resource.stubs(:should).returns nil @instance.stubs(:resource).returns @resource end it "should set its :ensure value to :present" do @instance.create @instance.properties[:ensure].should == :present end it "should set all of the other attributes from the resource" do @resource.expects(:should).with(:description).returns "myvlan" @instance.create @instance.properties[:description].should == "myvlan" end end describe "is being destroyed" do it "should set its :ensure value to :absent" do @instance.destroy @instance.properties[:ensure].should == :absent end end end end diff --git a/spec/unit/provider/package/aix_spec.rb b/spec/unit/provider/package/aix_spec.rb index fba29d7a7..f54bcd5ce 100755 --- a/spec/unit/provider/package/aix_spec.rb +++ b/spec/unit/provider/package/aix_spec.rb @@ -1,65 +1,65 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:aix) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:source).returns "mysource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.resource = @resource end [:install, :uninstall, :latest, :query, :update].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end end it "should uninstall a package" do @provider.expects(:installp).with('-gu', 'mypackage') @provider.uninstall end describe "when installing" do it "should install a package" do @resource.stubs(:should).with(:ensure).returns(:installed) @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage') @provider.install end it "should install a specific package version" do @resource.stubs(:should).with(:ensure).returns("1.2.3.4") @provider.expects(:installp).with('-acgwXY', '-d', 'mysource', 'mypackage 1.2.3.4') @provider.install end end describe "when finding the latest version" do it "should return the current version when no later version is present" do @provider.stubs(:latest_info).returns(nil) @provider.stubs(:properties).returns( { :ensure => "1.2.3.4" } ) @provider.latest.should == "1.2.3.4" end it "should return the latest version of a package" do @provider.stubs(:latest_info).returns( { :version => "1.2.3.5" } ) @provider.latest.should == "1.2.3.5" end end it "update should install a package" do @provider.expects(:install).with(false) @provider.update end end diff --git a/spec/unit/provider/package/appdmg_spec.rb b/spec/unit/provider/package/appdmg_spec.rb index bde9efcfe..ff4677afa 100755 --- a/spec/unit/provider/package/appdmg_spec.rb +++ b/spec/unit/provider/package/appdmg_spec.rb @@ -1,42 +1,42 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package).provider(:appdmg) do let(:resource) { Puppet::Type.type(:package).new(:name => 'foo', :provider => :appdmg) } let(:provider) { described_class.new(resource) } describe "when installing an appdmg" do let(:fake_mountpoint) { "/tmp/dmg.foo" } let(:empty_hdiutil_plist) { Plist::Emit.dump({}) } let(:fake_hdiutil_plist) { Plist::Emit.dump({"system-entities" => [{"mount-point" => fake_mountpoint}]}) } before do fh = mock 'filehandle' fh.stubs(:path).yields "/tmp/foo" resource[:source] = "foo.dmg" described_class.stubs(:open).yields fh Dir.stubs(:mktmpdir).returns "/tmp/testtmp123" FileUtils.stubs(:remove_entry_secure) end describe "from a remote source" do let(:tmpdir) { "/tmp/good123" } before :each do resource[:source] = "http://fake.puppetlabs.com/foo.dmg" end it "should call tmpdir and use the returned directory" do Dir.expects(:mktmpdir).returns tmpdir Dir.stubs(:entries).returns ["foo.app"] described_class.expects(:curl).with do |*args| args[0] == "-o" and args[1].include? tmpdir end described_class.stubs(:hdiutil).returns fake_hdiutil_plist described_class.expects(:installapp) provider.install end end end end diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb index b020b0f4f..1b2c42d5a 100755 --- a/spec/unit/provider/package/apt_spec.rb +++ b/spec/unit/provider/package/apt_spec.rb @@ -1,144 +1,144 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider = Puppet::Type.type(:package).provider(:apt) describe provider do before do @resource = stub 'resource', :[] => "asdf" @provider = provider.new(@resource) @fakeresult = "install ok installed asdf 1.0\n" end it "should be versionable" do provider.should be_versionable end it "should use :install to update" do @provider.expects(:install) @provider.update end it "should use 'apt-get remove' to uninstall" do @provider.expects(:aptget).with("-y", "-q", :remove, "asdf") @provider.uninstall end it "should use 'apt-get purge' and 'dpkg purge' to purge" do @provider.expects(:aptget).with("-y", "-q", :remove, "--purge", "asdf") @provider.expects(:dpkg).with("--purge", "asdf") @provider.purge end it "should use 'apt-cache policy' to determine the latest version of a package" do @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf: Installed: 1:1.0 Candidate: 1:1.1 Version table: 1:1.0 650 http://ftp.osuosl.org testing/main Packages *** 1:1.1 100 /var/lib/dpkg/status" @provider.latest.should == "1:1.1" end it "should print and error and return nil if no policy is found" do @provider.expects(:aptcache).with(:policy, "asdf").returns "asdf:" @provider.expects(:err) @provider.latest.should be_nil end it "should be able to preseed" do @provider.should respond_to(:run_preseed) end it "should preseed with the provided responsefile when preseeding is called for" do @resource.expects(:[]).with(:responsefile).returns "/my/file" FileTest.expects(:exist?).with("/my/file").returns true @provider.expects(:info) @provider.expects(:preseed).with("/my/file") @provider.run_preseed end it "should not preseed if no responsefile is provided" do @resource.expects(:[]).with(:responsefile).returns nil @provider.expects(:info) @provider.expects(:preseed).never @provider.run_preseed end it "should fail if a cdrom is listed in the sources list and :allowcdrom is not specified" describe "when installing" do it "should preseed if a responsefile is provided" do @resource.expects(:[]).with(:responsefile).returns "/my/file" @provider.expects(:run_preseed) @provider.stubs(:aptget) @provider.install end it "should check for a cdrom" do @provider.expects(:checkforcdrom) @provider.stubs(:aptget) @provider.install end it "should use 'apt-get install' with the package name if no version is asked for" do @resource.expects(:[]).with(:ensure).returns :installed @provider.expects(:aptget).with { |*command| command[-1] == "asdf" and command[-2] == :install } @provider.install end it "should specify the package version if one is asked for" do @resource.expects(:[]).with(:ensure).returns "1.0" @provider.expects(:aptget).with { |*command| command[-1] == "asdf=1.0" } @provider.install end it "should use --force-yes if a package version is specified" do @resource.expects(:[]).with(:ensure).returns "1.0" @provider.expects(:aptget).with { |*command| command.include?("--force-yes") } @provider.install end it "should do a quiet install" do @provider.expects(:aptget).with { |*command| command.include?("-q") } @provider.install end it "should default to 'yes' for all questions" do @provider.expects(:aptget).with { |*command| command.include?("-y") } @provider.install end it "should keep config files if asked" do @resource.expects(:[]).with(:configfiles).returns :keep @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confold") } @provider.install end it "should replace config files if asked" do @resource.expects(:[]).with(:configfiles).returns :replace @provider.expects(:aptget).with { |*command| command.include?("DPkg::Options::=--force-confnew") } @provider.install end end end diff --git a/spec/unit/provider/package/aptitude_spec.rb b/spec/unit/provider/package/aptitude_spec.rb index fc531a1f8..8dc36dd99 100755 --- a/spec/unit/provider/package/aptitude_spec.rb +++ b/spec/unit/provider/package/aptitude_spec.rb @@ -1,38 +1,38 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package).provider(:aptitude) do let :type do Puppet::Type.type(:package) end let :pkg do type.new(:name => 'faff', :provider => :aptitude, :source => '/tmp/faff.deb') end it { should be_versionable } context "when retrieving ensure" do { :absent => "deinstall ok config-files faff 1.2.3-1\n", "1.2.3-1" => "install ok installed faff 1.2.3-1\n", }.each do |expect, output| it "should detect #{expect} packages" do pkg.provider.expects(:dpkgquery). with('-W', '--showformat', '${Status} ${Package} ${Version}\n', 'faff'). returns(output) pkg.property(:ensure).retrieve.should == expect end end end it "should try and install when asked" do pkg.provider.expects(:aptitude). with('-y', '-o', 'DPkg::Options::=--force-confold', :install, 'faff'). returns(0) pkg.provider.install end it "should try and purge when asked" do pkg.provider.expects(:aptitude).with('-y', 'purge', 'faff').returns(0) pkg.provider.purge end end diff --git a/spec/unit/provider/package/aptrpm_spec.rb b/spec/unit/provider/package/aptrpm_spec.rb index d12dfd3f6..bd9f470e8 100755 --- a/spec/unit/provider/package/aptrpm_spec.rb +++ b/spec/unit/provider/package/aptrpm_spec.rb @@ -1,39 +1,39 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package).provider(:aptrpm) do let :type do Puppet::Type.type(:package) end let :pkg do type.new(:name => 'faff', :provider => :aptrpm, :source => '/tmp/faff.rpm') end it { should be_versionable } context "when retrieving ensure" do def rpm pkg.provider.expects(:rpm). with('-q', 'faff', '--nosignature', '--nodigest', '--qf', "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n") end it "should report absent packages" do rpm.raises(Puppet::ExecutionFailure, "couldn't find rpm") pkg.property(:ensure).retrieve.should == :absent end it "should report present packages correctly" do rpm.returns("faff-1.2.3-1 0 1.2.3-1 5 i686\n") pkg.property(:ensure).retrieve.should == "1.2.3-1-5" end end it "should try and install when asked" do pkg.provider.expects(:aptget). with('-q', '-y', 'install', 'faff'). returns(0) pkg.provider.install end it "should try and purge when asked" do pkg.provider.expects(:aptget).with('-y', '-q', 'remove', '--purge', 'faff').returns(0) pkg.provider.purge end end diff --git a/spec/unit/provider/package/dpkg_spec.rb b/spec/unit/provider/package/dpkg_spec.rb index 818045b11..8fbd476ca 100755 --- a/spec/unit/provider/package/dpkg_spec.rb +++ b/spec/unit/provider/package/dpkg_spec.rb @@ -1,227 +1,227 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'stringio' provider = Puppet::Type.type(:package).provider(:dpkg) describe provider do before do @resource = stub 'resource', :[] => "asdf" @provider = provider.new(@resource) @provider.expects(:execute).never # forbid "manual" executions @fakeresult = "install ok installed asdf 1.0\n" end it "should have documentation" do provider.doc.should be_instance_of(String) end describe "when listing all instances" do before do provider.stubs(:command).with(:dpkgquery).returns "myquery" end it "should use dpkg-query" do provider.expects(:command).with(:dpkgquery).returns "myquery" Puppet::Util::Execution.expects(:execpipe).with("myquery -W --showformat '${Status} ${Package} ${Version}\\n'").yields StringIO.new(@fakeresult) provider.instances end it "should create and return an instance with each parsed line from dpkg-query" do pipe = mock 'pipe' pipe.expects(:each).never pipe.expects(:each_line).yields @fakeresult Puppet::Util::Execution.expects(:execpipe).yields pipe asdf = mock 'pkg1' provider.expects(:new).with(:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg).returns asdf provider.instances.should == [asdf] end it "should warn on and ignore any lines it does not understand" do pipe = mock 'pipe' pipe.expects(:each).never pipe.expects(:each_line).yields "foobar" Puppet::Util::Execution.expects(:execpipe).yields pipe Puppet.expects(:warning) provider.expects(:new).never provider.instances.should == [] end end describe "when querying the current state" do it "should use dpkg-query" do @provider.expects(:dpkgquery).with("-W", "--showformat",'${Status} ${Package} ${Version}\\n', "asdf").returns @fakeresult @provider.query end it "should consider the package purged if dpkg-query fails" do @provider.expects(:dpkgquery).raises Puppet::ExecutionFailure.new("eh") @provider.query[:ensure].should == :purged end it "should return a hash of the found status with the desired state, error state, status, name, and 'ensure'" do @provider.expects(:dpkgquery).returns @fakeresult @provider.query.should == {:ensure => "1.0", :error => "ok", :desired => "install", :name => "asdf", :status => "installed", :provider => :dpkg} end it "should consider the package absent if the dpkg-query result cannot be interpreted" do @provider.expects(:dpkgquery).returns "somebaddata" @provider.query[:ensure].should == :absent end it "should fail if an error is discovered" do @provider.expects(:dpkgquery).returns @fakeresult.sub("ok", "error") lambda { @provider.query }.should raise_error(Puppet::Error) end it "should consider the package purged if it is marked 'not-installed'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "not-installed") @provider.query[:ensure].should == :purged end it "should consider the package absent if it is marked 'config-files'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "config-files") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'half-installed'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-installed") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'unpacked'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "unpacked") @provider.query[:ensure].should == :absent end it "should consider the package absent if it is marked 'half-configured'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("installed", "half-configured") @provider.query[:ensure].should == :absent end it "should consider the package held if its state is 'hold'" do @provider.expects(:dpkgquery).returns @fakeresult.sub("install", "hold") @provider.query[:ensure].should == :held end end it "should be able to install" do @provider.should respond_to(:install) end describe "when installing" do before do @resource.stubs(:[]).with(:source).returns "mypkg" end it "should fail to install if no source is specified in the resource" do @resource.expects(:[]).with(:source).returns nil lambda { @provider.install }.should raise_error(ArgumentError) end it "should use 'dpkg -i' to install the package" do @resource.expects(:[]).with(:source).returns "mypackagefile" @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[-1] == "mypackagefile" and command[-2] == "-i" } @provider.install end it "should keep old config files if told to do so" do @resource.expects(:[]).with(:configfiles).returns :keep @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[0] == "--force-confold" } @provider.install end it "should replace old config files if told to do so" do @resource.expects(:[]).with(:configfiles).returns :replace @provider.expects(:unhold) @provider.expects(:dpkg).with { |*command| command[0] == "--force-confnew" } @provider.install end it "should ensure any hold is removed" do @provider.expects(:unhold).once @provider.expects(:dpkg) @provider.install end end describe "when holding or unholding" do before do @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" @tempfile.stubs(:write) Tempfile.stubs(:new).returns @tempfile end it "should install first if holding" do @provider.stubs(:execute) @provider.expects(:install).once @provider.hold end it "should execute dpkg --set-selections when holding" do @provider.stubs(:install) @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once @provider.hold end it "should execute dpkg --set-selections when unholding" do @provider.stubs(:install) @provider.expects(:execute).with([:dpkg, '--set-selections'], {:stdinfile => @tempfile.path}).once @provider.hold end end it "should use :install to update" do @provider.expects(:install) @provider.update end describe "when determining latest available version" do it "should return the version found by dpkg-deb" do @resource.expects(:[]).with(:source).returns "myfile" @provider.expects(:dpkg_deb).with { |*command| command[-1] == "myfile" }.returns "asdf\t1.0" @provider.latest.should == "1.0" end it "should warn if the package file contains a different package" do @provider.expects(:dpkg_deb).returns("foo\tversion") @provider.expects(:warning) @provider.latest end it "should cope with names containing ++" do @resource = stub 'resource', :[] => "asdf++" @provider = provider.new(@resource) @provider.expects(:dpkg_deb).returns "asdf++\t1.0" @provider.latest.should == "1.0" end end it "should use 'dpkg -r' to uninstall" do @provider.expects(:dpkg).with("-r", "asdf") @provider.uninstall end it "should use 'dpkg --purge' to purge" do @provider.expects(:dpkg).with("--purge", "asdf") @provider.purge end end diff --git a/spec/unit/provider/package/freebsd_spec.rb b/spec/unit/provider/package/freebsd_spec.rb index e5550f43b..6db3bf5bb 100755 --- a/spec/unit/provider/package/freebsd_spec.rb +++ b/spec/unit/provider/package/freebsd_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:freebsd) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.resource = @resource end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end describe "when installing" do before :each do @resource.stubs(:should).with(:ensure).returns(:installed) end it "should install a package from a path to a directory" do # For better or worse, trailing '/' is needed. --daniel 2011-01-26 path = '/path/to/directory/' @resource.stubs(:[]).with(:source).returns(path) Puppet::Util.expects(:withenv).once.with({:PKG_PATH => path}).yields @provider.expects(:pkgadd).once.with("mypackage") expect { @provider.install }.should_not raise_error end %w{http https ftp}.each do |protocol| it "should install a package via #{protocol}" do # For better or worse, trailing '/' is needed. --daniel 2011-01-26 path = "#{protocol}://localhost/" @resource.stubs(:[]).with(:source).returns(path) Puppet::Util.expects(:withenv).once.with({:PACKAGESITE => path}).yields @provider.expects(:pkgadd).once.with('-r', "mypackage") expect { @provider.install }.should_not raise_error end end end end diff --git a/spec/unit/provider/package/gem_spec.rb b/spec/unit/provider/package/gem_spec.rb index 8e519d6f4..f02fe7eb8 100755 --- a/spec/unit/provider/package/gem_spec.rb +++ b/spec/unit/provider/package/gem_spec.rb @@ -1,143 +1,143 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:gem) describe provider_class do let(:resource) do Puppet::Type.type(:package).new( :name => 'myresource', :ensure => :installed ) end let(:provider) do provider = provider_class.new provider.resource = resource provider end describe "when installing" do it "should use the path to the gem" do provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" provider.expects(:execute).with { |args| args[0] == "/my/gem" }.returns "" provider.install end it "should specify that the gem is being installed" do provider.expects(:execute).with { |args| args[1] == "install" }.returns "" provider.install end it "should specify that dependencies should be included" do provider.expects(:execute).with { |args| args[2] == "--include-dependencies" }.returns "" provider.install end it "should specify that documentation should not be included" do provider.expects(:execute).with { |args| args[3] == "--no-rdoc" }.returns "" provider.install end it "should specify that RI should not be included" do provider.expects(:execute).with { |args| args[4] == "--no-ri" }.returns "" provider.install end it "should specify the package name" do provider.expects(:execute).with { |args| args[5] == "myresource" }.returns "" provider.install end describe "when a source is specified" do describe "as a normal file" do it "should use the file name instead of the gem name" do resource[:source] = "/my/file" provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" provider.install end end describe "as a file url" do it "should use the file name instead of the gem name" do resource[:source] = "file:///my/file" provider.expects(:execute).with { |args| args[3] == "/my/file" }.returns "" provider.install end end describe "as a puppet url" do it "should fail" do resource[:source] = "puppet://my/file" lambda { provider.install }.should raise_error(Puppet::Error) end end describe "as a non-file and non-puppet url" do it "should treat the source as a gem repository" do resource[:source] = "http://host/my/file" provider.expects(:execute).with { |args| args[3..5] == ["--source", "http://host/my/file", "myresource"] }.returns "" provider.install end end describe "with an invalid uri" do it "should fail" do URI.expects(:parse).raises(ArgumentError) resource[:source] = "http:::::uppet:/:/my/file" lambda { provider.install }.should raise_error(Puppet::Error) end end end end describe "#latest" do it "should return a single value for 'latest'" do #gemlist is used for retrieving both local and remote version numbers, and there are cases # (particularly local) where it makes sense for it to return an array. That doesn't make # sense for '#latest', though. provider.class.expects(:gemlist).with({ :justme => 'myresource'}).returns({ :name => 'myresource', :ensure => ["3.0"], :provider => :gem, }) provider.latest.should == "3.0" end end describe "#instances" do before do provider_class.stubs(:command).with(:gemcmd).returns "/my/gem" end it "should return an empty array when no gems installed" do provider_class.expects(:execute).with(%w{/my/gem list --local}).returns("\n") provider_class.instances.should == [] end it "should return ensure values as an array of installed versions" do provider_class.expects(:execute).with(%w{/my/gem list --local}).returns <<-HEREDOC.gsub(/ /, '') systemu (1.2.0) vagrant (0.8.7, 0.6.9) HEREDOC provider_class.instances.map {|p| p.properties}.should == [ {:ensure => ["1.2.0"], :provider => :gem, :name => 'systemu'}, {:ensure => ["0.8.7", "0.6.9"], :provider => :gem, :name => 'vagrant'} ] end it "should not fail when an unmatched line is returned" do provider_class.expects(:execute).with(%w{/my/gem list --local}). returns(File.read(my_fixture('line-with-1.8.5-warning'))) provider_class.instances.map {|p| p.properties}. should == [{:provider=>:gem, :ensure=>["0.3.2"], :name=>"columnize"}, {:provider=>:gem, :ensure=>["1.1.3"], :name=>"diff-lcs"}, {:provider=>:gem, :ensure=>["0.0.1"], :name=>"metaclass"}, {:provider=>:gem, :ensure=>["0.10.5"], :name=>"mocha"}, {:provider=>:gem, :ensure=>["0.8.7"], :name=>"rake"}, {:provider=>:gem, :ensure=>["2.9.0"], :name=>"rspec-core"}, {:provider=>:gem, :ensure=>["2.9.1"], :name=>"rspec-expectations"}, {:provider=>:gem, :ensure=>["2.9.0"], :name=>"rspec-mocks"}, {:provider=>:gem, :ensure=>["0.9.0"], :name=>"rubygems-bundler"}, {:provider=>:gem, :ensure=>["1.11.3.3"], :name=>"rvm"}] end end end diff --git a/spec/unit/provider/package/hpux_spec.rb b/spec/unit/provider/package/hpux_spec.rb index b781f6540..db51dff55 100755 --- a/spec/unit/provider/package/hpux_spec.rb +++ b/spec/unit/provider/package/hpux_spec.rb @@ -1,51 +1,51 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:hpux) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:source).returns "mysource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.stubs(:resource).returns @resource end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end it "should have an uninstall method" do @provider = provider_class.new @provider.should respond_to(:uninstall) end it "should have a swlist method" do @provider = provider_class.new @provider.should respond_to(:swlist) end describe "when installing" do it "should use a command-line like 'swinstall -x mount_all_filesystems=false -s SOURCE PACKAGE-NAME'" do @provider.expects(:swinstall).with('-x', 'mount_all_filesystems=false', '-s', 'mysource', 'mypackage') @provider.install end end describe "when uninstalling" do it "should use a command-line like 'swremove -x mount_all_filesystems=false PACKAGE-NAME'" do @provider.expects(:swremove).with('-x', 'mount_all_filesystems=false', 'mypackage') @provider.uninstall end end end diff --git a/spec/unit/provider/package/nim_spec.rb b/spec/unit/provider/package/nim_spec.rb index 0fa9f580d..bd2c9a16d 100755 --- a/spec/unit/provider/package/nim_spec.rb +++ b/spec/unit/provider/package/nim_spec.rb @@ -1,41 +1,41 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:nim) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:source).returns "mysource" @resource.stubs(:[]).with(:ensure).returns :installed @provider = provider_class.new @provider.resource = @resource end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end describe "when installing" do it "should install a package" do @resource.stubs(:should).with(:ensure).returns(:installed) @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage'") @provider.install end it "should install a versioned package" do @resource.stubs(:should).with(:ensure).returns("1.2.3.4") @provider.expects(:nimclient).with("-o", "cust", "-a", "installp_flags=acgwXY", "-a", "lpp_source=mysource", "-a", "filesets='mypackage 1.2.3.4'") @provider.install end end end diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb index 1289c6ea1..895555826 100755 --- a/spec/unit/provider/package/openbsd_spec.rb +++ b/spec/unit/provider/package/openbsd_spec.rb @@ -1,115 +1,115 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'stringio' provider_class = Puppet::Type.type(:package).provider(:openbsd) describe provider_class do subject { provider_class } def package(args = {}) defaults = { :name => 'bash', :provider => 'openbsd' } Puppet::Type.type(:package).new(defaults.merge(args)) end before :each do # Stub some provider methods to avoid needing the actual software # installed, so we can test on whatever platform we want. provider_class.stubs(:command).with(:pkginfo).returns('/bin/pkg_info') provider_class.stubs(:command).with(:pkgadd).returns('/bin/pkg_add') provider_class.stubs(:command).with(:pkgdelete).returns('/bin/pkg_delete') end context "::instances" do it "should return nil if execution failed" do subject.expects(:execpipe).raises(Puppet::ExecutionFailure, 'wawawa') subject.instances.should be_nil end it "should return the empty set if no packages are listed" do subject.expects(:execpipe).with(%w{/bin/pkg_info -a}).yields(StringIO.new('')) subject.instances.should be_empty end it "should return all packages when invoked" do fixture = File.new(my_fixture('pkginfo.list')) subject.expects(:execpipe).with(%w{/bin/pkg_info -a}).yields(fixture) subject.instances.map(&:name).sort.should == %w{bash bzip2 expat gettext libiconv lzo openvpn python vim wget}.sort end end context "#install" do it "should fail if the resource doesn't have a source" do provider = subject.new(package()) expect { provider.install }. to raise_error Puppet::Error, /must specify a package source/ end it "should install correctly when given a directory-unlike source" do ENV.should_not be_key 'PKG_PATH' source = '/whatever.pkg' provider = subject.new(package(:source => source)) provider.expects(:pkgadd).with do |name| ENV.should_not be_key 'PKG_PATH' name == source end provider.install ENV.should_not be_key 'PKG_PATH' end it "should install correctly when given a directory-like source" do ENV.should_not be_key 'PKG_PATH' source = '/whatever/' provider = subject.new(package(:source => source)) provider.expects(:pkgadd).with do |name| ENV.should be_key 'PKG_PATH' ENV['PKG_PATH'].should == source name == provider.resource[:name] end provider.install ENV.should_not be_key 'PKG_PATH' end end context "#get_version" do it "should return nil if execution fails" do provider = subject.new(package) provider.expects(:execpipe).raises(Puppet::ExecutionFailure, 'wawawa') provider.get_version.should be_nil end it "should return the package version if in the output" do fixture = File.new(my_fixture('pkginfo.list')) provider = subject.new(package(:name => 'bash')) provider.expects(:execpipe).with(%w{/bin/pkg_info -I bash}).yields(fixture) provider.get_version.should == '3.1.17' end it "should return the empty string if the package is not present" do provider = subject.new(package(:name => 'zsh')) provider.expects(:execpipe).with(%w{/bin/pkg_info -I zsh}).yields(StringIO.new('')) provider.get_version.should == '' end end context "#query" do it "should return the installed version if present" do fixture = File.read(my_fixture('pkginfo.detail')) provider = subject.new(package(:name => 'bash')) provider.expects(:pkginfo).with('bash').returns(fixture) provider.query.should == { :ensure => '3.1.17' } end it "should return nothing if not present" do provider = subject.new(package(:name => 'zsh')) provider.expects(:pkginfo).with('zsh').returns('') provider.query.should be_nil end end end diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb index a37fe5b19..f484cdc5b 100755 --- a/spec/unit/provider/package/pacman_spec.rb +++ b/spec/unit/provider/package/pacman_spec.rb @@ -1,266 +1,266 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'stringio' provider = Puppet::Type.type(:package).provider(:pacman) describe provider do let(:no_extra_options) { { :custom_environment => {} } } let(:executor) { Puppet::Util::Execution } let(:resolver) { Puppet::Util } before do resolver.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman') provider.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman') @resource = Puppet::Type.type(:package).new(:name => 'package') @provider = provider.new(@resource) end describe "when installing" do before do @provider.stubs(:query).returns({ :ensure => '1.0' }) end it "should call pacman to install the right package quietly" do executor. expects(:execute). at_least_once. with(["/usr/bin/pacman", "--noconfirm", "--noprogressbar", "-Sy", @resource[:name]], no_extra_options). returns "" @provider.install end it "should raise an ExecutionFailure if the installation failed" do executor.stubs(:execute).returns("") @provider.expects(:query).returns(nil) lambda { @provider.install }.should raise_exception(Puppet::ExecutionFailure) end context "when :source is specified" do before :each do @install = sequence("install") end context "recognizable by pacman" do %w{ /some/package/file http://some.package.in/the/air ftp://some.package.in/the/air }.each do |source| it "should install #{source} directly" do @resource[:source] = source executor.expects(:execute). with(all_of(includes("-Sy"), includes("--noprogressbar")), no_extra_options). in_sequence(@install). returns("") executor.expects(:execute). with(all_of(includes("-U"), includes(source)), no_extra_options). in_sequence(@install). returns("") @provider.install end end end context "as a file:// URL" do before do @package_file = "file:///some/package/file" @actual_file_path = "/some/package/file" @resource[:source] = @package_file end it "should install from the path segment of the URL" do executor.expects(:execute). with(all_of(includes("-Sy"), includes("--noprogressbar"), includes("--noconfirm")), no_extra_options). in_sequence(@install). returns("") executor.expects(:execute). with(all_of(includes("-U"), includes(@actual_file_path)), no_extra_options). in_sequence(@install). returns("") @provider.install end end context "as a puppet URL" do before do @resource[:source] = "puppet://server/whatever" end it "should fail" do lambda { @provider.install }.should raise_error(Puppet::Error) end end context "as a malformed URL" do before do @resource[:source] = "blah://" end it "should fail" do lambda { @provider.install }.should raise_error(Puppet::Error) end end end end describe "when updating" do it "should call install" do @provider.expects(:install).returns("install return value") @provider.update.should == "install return value" end end describe "when uninstalling" do it "should call pacman to remove the right package quietly" do executor. expects(:execute). with(["/usr/bin/pacman", "--noconfirm", "--noprogressbar", "-R", @resource[:name]], no_extra_options). returns "" @provider.uninstall end end describe "when querying" do it "should query pacman" do executor. expects(:execute). with(["/usr/bin/pacman", "-Qi", @resource[:name]], no_extra_options) @provider.query end it "should return the version" do query_output = <=2.7.1 libfetch>=2.25 pacman-mirrorlist Optional Deps : fakeroot: for makepkg usage as normal user curl: for rankmirrors usage Required By : None Conflicts With : None Replaces : None Installed Size : 2352.00 K Packager : Dan McGee Architecture : i686 Build Date : Sat 22 Jan 2011 03:56:41 PM EST Install Date : Thu 27 Jan 2011 06:45:49 AM EST Install Reason : Explicitly installed Install Script : Yes Description : A library-based package manager with dependency support EOF executor.expects(:execute).returns(query_output) @provider.query.should == {:ensure => "1.01.3-2"} end it "should return a nil if the package isn't found" do executor.expects(:execute).returns("") @provider.query.should be_nil end it "should return a hash indicating that the package is missing on error" do executor.expects(:execute).raises(Puppet::ExecutionFailure.new("ERROR!")) @provider.query.should == { :ensure => :purged, :status => 'missing', :name => @resource[:name], :error => 'ok', } end end describe "when fetching a package list" do it "should query pacman" do provider.expects(:execpipe).with(["/usr/bin/pacman", '-Q']) provider.instances end it "should return installed packages with their versions" do provider.expects(:execpipe).yields(StringIO.new("package1 1.23-4\npackage2 2.00\n")) packages = provider.instances packages.length.should == 2 packages[0].properties.should == { :provider => :pacman, :ensure => '1.23-4', :name => 'package1' } packages[1].properties.should == { :provider => :pacman, :ensure => '2.00', :name => 'package2' } end it "should return nil on error" do provider.expects(:execpipe).raises(Puppet::ExecutionFailure.new("ERROR!")) provider.instances.should be_nil end it "should warn on invalid input" do provider.expects(:execpipe).yields(StringIO.new("blah")) provider.expects(:warning).with("Failed to match line blah") provider.instances.should == [] end end describe "when determining the latest version" do it "should refresh package list" do get_latest_version = sequence("get_latest_version") executor. expects(:execute). in_sequence(get_latest_version). with(['/usr/bin/pacman', '-Sy'], no_extra_options) executor. stubs(:execute). in_sequence(get_latest_version). returns("") @provider.latest end it "should get query pacman for the latest version" do get_latest_version = sequence("get_latest_version") executor. stubs(:execute). in_sequence(get_latest_version) executor. expects(:execute). in_sequence(get_latest_version). with(['/usr/bin/pacman', '-Sp', '--print-format', '%v', @resource[:name]], no_extra_options). returns("") @provider.latest end it "should return the version number from pacman" do executor. expects(:execute). at_least_once(). returns("1.00.2-3\n") @provider.latest.should == "1.00.2-3" end end end diff --git a/spec/unit/provider/package/pip_spec.rb b/spec/unit/provider/package/pip_spec.rb index 2de0eb4fc..a31708897 100755 --- a/spec/unit/provider/package/pip_spec.rb +++ b/spec/unit/provider/package/pip_spec.rb @@ -1,193 +1,193 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:pip) describe provider_class do before do @resource = Puppet::Resource.new(:package, "fake_package") @provider = provider_class.new(@resource) @client = stub_everything('client') @client.stubs(:call).with('package_releases', 'real_package').returns(["1.3", "1.2.5", "1.2.4"]) @client.stubs(:call).with('package_releases', 'fake_package').returns([]) XMLRPC::Client.stubs(:new2).returns(@client) end describe "parse" do it "should return a hash on valid input" do provider_class.parse("real_package==1.2.5").should == { :ensure => "1.2.5", :name => "real_package", :provider => :pip, } end it "should return nil on invalid input" do provider_class.parse("foo").should == nil end end describe "instances" do it "should return an array when pip is present" do provider_class.expects(:which).with('pip').returns("/fake/bin/pip") p = stub("process") p.expects(:collect).yields("real_package==1.2.5") provider_class.expects(:execpipe).with("/fake/bin/pip freeze").yields(p) provider_class.instances end it "should return an empty array when pip is missing" do provider_class.expects(:which).with('pip').returns nil provider_class.instances.should == [] end end describe "query" do before do @resource[:name] = "real_package" end it "should return a hash when pip and the package are present" do provider_class.expects(:instances).returns [provider_class.new({ :ensure => "1.2.5", :name => "real_package", :provider => :pip, })] @provider.query.should == { :ensure => "1.2.5", :name => "real_package", :provider => :pip, } end it "should return nil when the package is missing" do provider_class.expects(:instances).returns [] @provider.query.should == nil end end describe "latest" do it "should find a version number for real_package" do @resource[:name] = "real_package" @provider.latest.should_not == nil end it "should not find a version number for fake_package" do @resource[:name] = "fake_package" @provider.latest.should == nil end it "should handle a timeout gracefully" do @resource[:name] = "fake_package" @client.stubs(:call).raises(Timeout::Error) lambda { @provider.latest }.should raise_error(Puppet::Error) end end describe "install" do before do @resource[:name] = "fake_package" @url = "git+https://example.com/fake_package.git" end it "should install" do @resource[:ensure] = :installed @resource[:source] = nil @provider.expects(:lazy_pip). with("install", '-q', "fake_package") @provider.install end it "should install from SCM" do @resource[:ensure] = :installed @resource[:source] = @url @provider.expects(:lazy_pip). with("install", '-q', '-e', "#{@url}#egg=fake_package") @provider.install end it "should install a particular SCM revision" do @resource[:ensure] = "0123456" @resource[:source] = @url @provider.expects(:lazy_pip). with("install", "-q", "-e", "#{@url}@0123456#egg=fake_package") @provider.install end it "should install a particular version" do @resource[:ensure] = "0.0.0" @resource[:source] = nil @provider.expects(:lazy_pip).with("install", "-q", "fake_package==0.0.0") @provider.install end it "should upgrade" do @resource[:ensure] = :latest @resource[:source] = nil @provider.expects(:lazy_pip). with("install", "-q", "--upgrade", "fake_package") @provider.install end end describe "uninstall" do it "should uninstall" do @resource[:name] = "fake_package" @provider.expects(:lazy_pip). with('uninstall', '-y', '-q', 'fake_package') @provider.uninstall end end describe "update" do it "should just call install" do @provider.expects(:install).returns(nil) @provider.update end end describe "lazy_pip" do it "should succeed if pip is present" do @provider.stubs(:pip).returns(nil) @provider.method(:lazy_pip).call "freeze" end it "should retry if pip has not yet been found" do @provider.expects(:pip).twice.with('freeze').raises(NoMethodError).then.returns(nil) @provider.expects(:which).with('pip').returns("/fake/bin/pip") @provider.method(:lazy_pip).call "freeze" end it "should fail if pip is missing" do @provider.expects(:pip).with('freeze').raises(NoMethodError) @provider.expects(:which).with('pip').returns(nil) expect { @provider.method(:lazy_pip).call("freeze") }.to raise_error(NoMethodError) end it "should output a useful error message if pip is missing" do @provider.expects(:pip).with('freeze').raises(NoMethodError) @provider.expects(:which).with('pip').returns(nil) expect { @provider.method(:lazy_pip).call("freeze") }. to raise_error(NoMethodError, 'Could not locate the pip command.') end end end diff --git a/spec/unit/provider/package/pkg_spec.rb b/spec/unit/provider/package/pkg_spec.rb index 2214cb7d0..956e743b4 100755 --- a/spec/unit/provider/package/pkg_spec.rb +++ b/spec/unit/provider/package/pkg_spec.rb @@ -1,133 +1,133 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package).provider(:pkg) do before :each do @resource = Puppet::Resource.new(:package, 'dummy', :parameters => {:name => 'dummy', :ensure => :latest}) @provider = described_class.new(@resource) end def self.it_should_respond_to(*actions) actions.each do |action| it "should respond to :#{action}" do @provider.should respond_to(action) end end end it_should_respond_to :install, :uninstall, :update, :query, :latest it "should not be versionable" do described_class.should_not be_versionable end it "should use :install to update" do @provider.expects(:install) @provider.update end describe "when calling instances" do it "should correctly parse lines with preferred publisher" do described_class.expects(:pkg).with(:list,'-H').returns File.read(my_fixture('simple')) @instances = described_class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure)} } @instances.size.should == 4 @instances[0].should == {:name => 'SUNPython', :ensure => :present} @instances[1].should == {:name => 'SUNWbind', :ensure => :present} @instances[2].should == {:name => 'SUNWdistro-license-copyright', :ensure => :present} @instances[3].should == {:name => 'SUNWfppd', :ensure => :present} end it "should correctly parse lines with non preferred publisher" do described_class.expects(:pkg).with(:list,'-H').returns File.read(my_fixture('publisher')) @instances = described_class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure)} } @instances.size.should == 2 @instances[0].should == {:name => 'SUNWpcre', :ensure => :present} @instances[1].should == {:name => 'service/network/ssh', :ensure => :present} end it "should correctly parse lines on solaris 11" do described_class.expects(:pkg).with(:list, '-H').returns File.read(my_fixture('solaris11')) described_class.expects(:warning).never @instances = described_class.instances.map { |p| {:name => p.get(:name), :ensure => p.get(:ensure) }} @instances.size.should == 12 @instances[0].should == {:name => 'compress/zip', :ensure => :present} @instances[1].should == {:name => 'archiver/gnu-tar', :ensure => :present} @instances[2].should == {:name => 'compress/bzip2', :ensure => :present} @instances[3].should == {:name => 'compress/gzip', :ensure => :present} @instances[4].should == {:name => 'compress/p7zip', :ensure => :present} @instances[5].should == {:name => 'compress/unzip', :ensure => :present} @instances[6].should == {:name => 'compress/zip', :ensure => :present} @instances[7].should == {:name => 'x11/library/toolkit/libxaw7', :ensure => :present} @instances[8].should == {:name => 'x11/library/toolkit/libxt', :ensure => :present} @instances[9].should == {:name => 'shell/bash', :ensure => :present} @instances[10].should == {:name => 'shell/zsh', :ensure => :present} @instances[11].should == {:name => 'security/sudo', :ensure => :present} end it "should warn about incorrect lines" do fake_output = File.read(my_fixture('incomplete')) error_line = fake_output.split($/).first described_class.expects(:pkg).with(:list,'-H').returns fake_output described_class.expects(:warning).with "Failed to match 'pkg list' line #{error_line.inspect}" described_class.instances end it "should warn about unknown package status" do described_class.expects(:pkg).with(:list,'-H').returns File.read(my_fixture('unknown_status')) described_class.expects(:warning).with "unknown package state for compress/unzip: x--" described_class.instances end end describe "when query a package" do context "on solaris 10" do it "should find the package" do @provider.stubs(:pkg).with(:list,'-H','dummy').returns File.read(my_fixture('dummy_solaris10')) @provider.query.should == { :name => 'dummy', :ensure => :present, :version => '2.5.5-0.111', :status => "installed", :provider => :pkg, } end it "should return :absent when the package is not found" do # I dont know what the acutal error looks like, but according to type/pkg.rb we're just # reacting on the Exception anyways @provider.expects(:pkg).with(:list, "-H", "dummy").raises Puppet::ExecutionFailure, 'Not found' @provider.query.should == {:ensure => :absent, :name => "dummy"} end end context "on solaris 11" do it "should find the package" do @provider.stubs(:pkg).with(:list,'-H','dummy').returns File.read(my_fixture('dummy_solaris11')) @provider.query.should == { :name => 'dummy', :ensure => :present, :version => '1.0.6-0.175.0.0.0.2.537', :status => "installed", :provider => :pkg, } end it "should return :absent when the package is not found" do # I dont know what the acutal error looks like, but according to type/pkg.rb we're just # reacting on the Exception anyways @provider.expects(:pkg).with(:list, "-H", "dummy").raises Puppet::ExecutionFailure, 'Not found' @provider.query.should == {:ensure => :absent, :name => "dummy"} end end it "should return :absent when the packageline cannot be parsed" do @provider.stubs(:pkg).with(:list,'-H','dummy').returns File.read(my_fixture('incomplete')) @provider.query.should == { :name => 'dummy', :ensure => :absent } end end end diff --git a/spec/unit/provider/package/pkgdmg_spec.rb b/spec/unit/provider/package/pkgdmg_spec.rb index f8b23167a..c2a3d7f45 100755 --- a/spec/unit/provider/package/pkgdmg_spec.rb +++ b/spec/unit/provider/package/pkgdmg_spec.rb @@ -1,89 +1,89 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package).provider(:pkgdmg) do let(:resource) { Puppet::Type.type(:package).new(:name => 'foo', :provider => :pkgdmg) } let(:provider) { described_class.new(resource) } it { should_not be_versionable } it { should_not be_uninstallable } describe "when installing it should fail when" do before :each do Puppet::Util.expects(:execute).never end it "no source is specified" do expect { provider.install }.should raise_error(Puppet::Error, /must specify a package source/) end it "the source does not end in .dmg or .pkg" do resource[:source] = "bar" expect { provider.install }.should raise_error(Puppet::Error, /must specify a source string ending in .*dmg.*pkg/) end end # These tests shouldn't be this messy. The pkgdmg provider needs work... describe "when installing a pkgdmg" do let(:fake_mountpoint) { "/tmp/dmg.foo" } let(:empty_hdiutil_plist) { Plist::Emit.dump({}) } let(:fake_hdiutil_plist) { Plist::Emit.dump({"system-entities" => [{"mount-point" => fake_mountpoint}]}) } before do fh = mock 'filehandle' fh.stubs(:path).yields "/tmp/foo" resource[:source] = "foo.dmg" File.stubs(:open).yields fh Dir.stubs(:mktmpdir).returns "/tmp/testtmp123" FileUtils.stubs(:remove_entry_secure) end it "should fail when a disk image with no system entities is mounted" do described_class.stubs(:hdiutil).returns(empty_hdiutil_plist) expect { provider.install }.should raise_error(Puppet::Error, /No disk entities/) end it "should call hdiutil to mount and eject the disk image" do Dir.stubs(:entries).returns [] provider.class.expects(:hdiutil).with("eject", fake_mountpoint).returns 0 provider.class.expects(:hdiutil).with("mount", "-plist", "-nobrowse", "-readonly", "-noidme", "-mountrandom", "/tmp", nil).returns fake_hdiutil_plist provider.install end it "should call installpkg if a pkg/mpkg is found on the dmg" do Dir.stubs(:entries).returns ["foo.pkg"] provider.class.stubs(:hdiutil).returns fake_hdiutil_plist provider.class.expects(:installpkg).with("#{fake_mountpoint}/foo.pkg", resource[:name], "foo.dmg").returns "" provider.install end describe "from a remote source" do let(:tmpdir) { "/tmp/good123" } before :each do resource[:source] = "http://fake.puppetlabs.com/foo.dmg" end it "should call tmpdir and use the returned directory" do Dir.expects(:mktmpdir).returns tmpdir Dir.stubs(:entries).returns ["foo.pkg"] described_class.expects(:curl).with do |*args| args[0] == "-o" and args[1].include? tmpdir end described_class.stubs(:hdiutil).returns fake_hdiutil_plist described_class.expects(:installpkg) provider.install end end end describe "when installing flat pkg file" do it "should call installpkg if a flat pkg file is found instead of a .dmg image" do resource[:source] = "/tmp/test.pkg" resource[:name] = "testpkg" provider.class.expects(:installpkgdmg).with("/tmp/test.pkg", "testpkg").returns "" provider.install end end end diff --git a/spec/unit/provider/package/pkgutil_spec.rb b/spec/unit/provider/package/pkgutil_spec.rb index cceeb8297..313e784ff 100755 --- a/spec/unit/provider/package/pkgutil_spec.rb +++ b/spec/unit/provider/package/pkgutil_spec.rb @@ -1,235 +1,235 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider = Puppet::Type.type(:package).provider(:pkgutil) describe provider do before(:each) do @resource = Puppet::Type.type(:package).new( :name => "TESTpkg", :ensure => :present, :provider => :pkgutil ) @provider = provider.new(@resource) # Stub all file and config tests provider.stubs(:healthcheck) end it "should have an install method" do @provider.should respond_to(:install) end it "should have a latest method" do @provider.should respond_to(:uninstall) end it "should have an update method" do @provider.should respond_to(:update) end it "should have a latest method" do @provider.should respond_to(:latest) end describe "when installing" do it "should use a command without versioned package" do @resource[:ensure] = :latest @provider.expects(:pkguti).with('-y', '-i', 'TESTpkg') @provider.install end it "should support a single temp repo URL" do @resource[:ensure] = :latest @resource[:source] = "http://example.net/repo" @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-y', '-i', 'TESTpkg') @provider.install end it "should support multiple temp repo URLs as array" do @resource[:ensure] = :latest @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ] @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-i', 'TESTpkg') @provider.install end end describe "when updating" do it "should use a command without versioned package" do @provider.expects(:pkguti).with('-y', '-u', 'TESTpkg') @provider.update end it "should support a single temp repo URL" do @resource[:source] = "http://example.net/repo" @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-y', '-u', 'TESTpkg') @provider.update end it "should support multiple temp repo URLs as array" do @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ] @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-u', 'TESTpkg') @provider.update end end describe "when uninstalling" do it "should call the remove operation" do @provider.expects(:pkguti).with('-y', '-r', 'TESTpkg') @provider.uninstall end it "should support a single temp repo URL" do @resource[:source] = "http://example.net/repo" @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-y', '-r', 'TESTpkg') @provider.uninstall end it "should support multiple temp repo URLs as array" do @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ] @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-r', 'TESTpkg') @provider.uninstall end end describe "when getting latest version" do it "should return TESTpkg's version string" do fake_data = " noisy output here TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.20" end it "should support a temp repo URL" do @resource[:source] = "http://example.net/repo" fake_data = " noisy output here TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-c', '--single', 'TESTpkg').returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.20" end it "should handle TESTpkg's 'SAME' version string" do fake_data = " noisy output here TESTpkg 1.4.5,REV=2007.11.18 SAME" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.18" end it "should handle a non-existent package" do fake_data = "noisy output here Not in catalog" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.latest.should == nil end it "should warn on unknown pkgutil noise" do provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns("testingnoise") @provider.latest.should == nil end it "should ignore pkgutil noise/headers to find TESTpkg" do fake_data = "# stuff => Fetching new catalog and descriptions (http://mirror.opencsw.org/opencsw/unstable/i386/5.11) if available ... 2011-02-19 23:05:46 URL:http://mirror.opencsw.org/opencsw/unstable/i386/5.11/catalog [534635/534635] -> \"/var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11.tmp\" [1] Checking integrity of /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 with gpg. gpg: Signature made February 17, 2011 05:27:53 PM GMT using DSA key ID E12E9D2F gpg: Good signature from \"Distribution Manager \" ==> 2770 packages loaded from /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 package installed catalog TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.20" end it "should find REALpkg via an alias (TESTpkg)" do fake_data = " noisy output here REALpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.query[:name].should == "TESTpkg" end end describe "when querying current version" do it "should return TESTpkg's version string" do fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.query[:ensure].should == "1.4.5,REV=2007.11.18" end it "should handle a package that isn't installed" do fake_data = "TESTpkg notinst 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.query[:ensure].should == :absent end it "should handle a non-existent package" do fake_data = "noisy output here Not in catalog" provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data @provider.query[:ensure].should == :absent end it "should support a temp repo URL" do @resource[:source] = "http://example.net/repo" fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-c', '--single', 'TESTpkg').returns fake_data @provider.query[:ensure].should == "1.4.5,REV=2007.11.18" end end describe "when querying current instances" do it "should warn on unknown pkgutil noise" do provider.expects(:pkguti).with(['-a']).returns("testingnoise") provider.expects(:pkguti).with(['-c']).returns("testingnoise") Puppet.expects(:warning).times(2) provider.expects(:new).never provider.instances.should == [] end it "should return TESTpkg's version string" do fake_data = "TESTpkg TESTpkg 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-a']).returns fake_data fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-c']).returns fake_data testpkg = mock 'pkg1' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg provider.instances.should == [testpkg] end it "should also return both TESTpkg and mypkg alias instances" do fake_data = "mypkg TESTpkg 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-a']).returns fake_data fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-c']).returns fake_data testpkg = mock 'pkg1' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg aliaspkg = mock 'pkg2' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "mypkg", :provider => :pkgutil).returns aliaspkg provider.instances.should == [testpkg,aliaspkg] end it "shouldn't mind noise in the -a output" do fake_data = "noisy output here" provider.expects(:pkguti).with(['-a']).returns fake_data fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-c']).returns fake_data testpkg = mock 'pkg1' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg provider.instances.should == [testpkg] end end end diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb index 9cefda595..5a0b9d6fe 100755 --- a/spec/unit/provider/package/rpm_spec.rb +++ b/spec/unit/provider/package/rpm_spec.rb @@ -1,58 +1,58 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:rpm) describe provider_class do subject { provider_class } let (:packages) do <<-RPM_OUTPUT cracklib-dicts 0 2.8.9 3.3 x86_64 basesystem 0 8.0 5.1.1.el5.centos noarch chkconfig 0 1.3.30.2 2.el5 x86_64 RPM_OUTPUT end describe "self.instances" do it "returns an array of packages" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "--version"], {:custom_environment => {}}).returns("RPM version 5.x") Puppet::Util.stubs(:which).with("rpm").returns("/bin/rpm") subject.stubs(:which).with("rpm").returns("/bin/rpm") Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf '%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\n'").yields(packages) installed_packages = subject.instances installed_packages[0].properties.should == { :provider => :rpm, :name => "cracklib-dicts", :epoch => "0", :version => "2.8.9", :release => "3.3", :arch => "x86_64", :ensure => "2.8.9-3.3" } installed_packages[1].properties.should == { :provider => :rpm, :name => "basesystem", :epoch => "0", :version => "8.0", :release => "5.1.1.el5.centos", :arch => "noarch", :ensure => "8.0-5.1.1.el5.centos" } installed_packages[2].properties.should == { :provider => :rpm, :name => "chkconfig", :epoch => "0", :version => "1.3.30.2", :release => "2.el5", :arch => "x86_64", :ensure => "1.3.30.2-2.el5" } end end end diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index 5148ac079..d9b08f7a9 100755 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -1,110 +1,110 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider = Puppet::Type.type(:package).provider(:yum) describe provider do before do # Create a mock resource @resource = stub 'resource' @resource.stubs(:[]).with(:name).returns 'mypackage' @provider = provider.new(@resource) @provider.stubs(:resource).returns @resource @provider.stubs(:yum).returns 'yum' @provider.stubs(:rpm).returns 'rpm' @provider.stubs(:get).with(:name).returns 'mypackage' @provider.stubs(:get).with(:version).returns '1' @provider.stubs(:get).with(:release).returns '1' @provider.stubs(:get).with(:arch).returns 'i386' end # provider should repond to the following methods [:install, :latest, :update, :purge].each do |method| it "should have a(n) #{method}" do @provider.should respond_to(method) end end describe 'when installing' do it 'should call yum install for :installed' do @resource.stubs(:should).with(:ensure).returns :installed @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage') @provider.install end it 'should use :install to update' do @provider.expects(:install) @provider.update end it 'should be able to set version' do @resource.stubs(:should).with(:ensure).returns '1.2' @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, 'mypackage-1.2') @provider.stubs(:query).returns :ensure => '1.2' @provider.install end it 'should be able to downgrade' do @resource.stubs(:should).with(:ensure).returns '1.0' @provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :downgrade, 'mypackage-1.0') @provider.stubs(:query).returns(:ensure => '1.2').then.returns(:ensure => '1.0') @provider.install end end describe 'when uninstalling' do it 'should use erase to purge' do @provider.expects(:yum).with('-y', :erase, 'mypackage') @provider.purge end it 'should use rpm to uninstall' do @provider.expects(:rpm).with('-e', 'mypackage-1-1.i386') @provider.uninstall end end it 'should be versionable' do provider.should be_versionable end describe '#latest' do describe 'when latest_info is nil' do before :each do @provider.stubs(:latest_info).returns(nil) end it 'raises if ensure is absent and latest_info is nil' do @provider.stubs(:properties).returns({:ensure => :absent}) expect { @provider.latest }.to raise_error( Puppet::DevError, 'Tried to get latest on a missing package' ) end it 'returns the ensure value if the package is not already installed' do @provider.stubs(:properties).returns({:ensure => '3.4.5'}) @provider.latest.should == '3.4.5' end end describe 'when latest_info is populated' do before :each do @provider.stubs(:latest_info).returns({ :name => 'mypackage', :epoch => '1', :version => '2.3.4', :release => '5', :arch => 'i686', :provider => :yum, :ensure => '2.3.4-5' }) end it 'includes the epoch in the version string' do @provider.latest.should == '1:2.3.4-5' end end end end diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb index d6615c5c6..57eedad54 100755 --- a/spec/unit/provider/package/zypper_spec.rb +++ b/spec/unit/provider/package/zypper_spec.rb @@ -1,122 +1,122 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:zypper) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name and source @resource.stubs(:[]).with(:name).returns "mypackage" @resource.stubs(:[]).with(:ensure).returns :installed @resource.stubs(:command).with(:zypper).returns "/usr/bin/zypper" @provider = provider_class.new(@resource) end it "should have an install method" do @provider = provider_class.new @provider.should respond_to(:install) end it "should have an uninstall method" do @provider = provider_class.new @provider.should respond_to(:uninstall) end it "should have an update method" do @provider = provider_class.new @provider.should respond_to(:update) end it "should have a latest method" do @provider = provider_class.new @provider.should respond_to(:latest) end describe "when installing with zypper version >= 1.0" do it "should use a command-line with versioned package'" do @resource.stubs(:should).with(:ensure).returns "1.2.3-4.5.6" @provider.stubs(:zypper_version).returns "1.2.8" @provider.expects(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage-1.2.3-4.5.6') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end it "should use a command-line without versioned package" do @resource.stubs(:should).with(:ensure).returns :latest @provider.stubs(:zypper_version).returns "1.2.8" @provider.expects(:zypper).with('--quiet', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end end describe "when installing with zypper version = 0.6.104" do it "should use a command-line with versioned package'" do @resource.stubs(:should).with(:ensure).returns "1.2.3-4.5.6" @provider.stubs(:zypper_version).returns "0.6.104" @provider.expects(:zypper).with('--terse', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage-1.2.3-4.5.6') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end it "should use a command-line without versioned package" do @resource.stubs(:should).with(:ensure).returns :latest @provider.stubs(:zypper_version).returns "0.6.104" @provider.expects(:zypper).with('--terse', :install, '--auto-agree-with-licenses', '--no-confirm', 'mypackage') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end end describe "when installing with zypper version = 0.6.13" do it "should use a command-line with versioned package'" do @resource.stubs(:should).with(:ensure).returns "1.2.3-4.5.6" @provider.stubs(:zypper_version).returns "0.6.13" @provider.expects(:zypper).with('--terse', :install, '--no-confirm', 'mypackage-1.2.3-4.5.6') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end it "should use a command-line without versioned package" do @resource.stubs(:should).with(:ensure).returns :latest @provider.stubs(:zypper_version).returns "0.6.13" @provider.expects(:zypper).with('--terse', :install, '--no-confirm', 'mypackage') @provider.expects(:query).returns "mypackage 0 1.2.3 4.5.6 x86_64" @provider.install end end describe "when updating" do it "should call install method of instance" do @provider.expects(:install) @provider.update end end describe "when getting latest version" do it "should return a version string with valid list-updates data from SLES11sp1" do fake_data = File.read(my_fixture('zypper-list-updates-SLES11sp1.out')) @resource.stubs(:[]).with(:name).returns "at" @provider.expects(:zypper).with("list-updates").returns fake_data @provider.latest.should == "3.1.8-1069.18.2" end end end diff --git a/spec/unit/provider/parsedfile_spec.rb b/spec/unit/provider/parsedfile_spec.rb index 2ff904b7f..f6fe7d49a 100755 --- a/spec/unit/provider/parsedfile_spec.rb +++ b/spec/unit/provider/parsedfile_spec.rb @@ -1,94 +1,94 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/parsedfile' # Most of the tests for this are still in test/ral/provider/parsedfile.rb. describe Puppet::Provider::ParsedFile do before do @class = Class.new(Puppet::Provider::ParsedFile) end describe "when looking up records loaded from disk" do it "should return nil if no records have been loaded" do @class.record?("foo").should be_nil end end describe "when generating a list of instances" do it "should return an instance for each record parsed from all of the registered targets" do @class.expects(:targets).returns %w{/one /two} @class.stubs(:skip_record?).returns false one = [:uno1, :uno2] two = [:dos1, :dos2] @class.expects(:prefetch_target).with("/one").returns one @class.expects(:prefetch_target).with("/two").returns two results = [] (one + two).each do |inst| results << inst.to_s + "_instance" @class.expects(:new).with(inst).returns(results[-1]) end @class.instances.should == results end it "should skip specified records" do @class.expects(:targets).returns %w{/one} @class.expects(:skip_record?).with(:uno).returns false @class.expects(:skip_record?).with(:dos).returns true one = [:uno, :dos] @class.expects(:prefetch_target).returns one @class.expects(:new).with(:uno).returns "eh" @class.expects(:new).with(:dos).never @class.instances end end describe "when flushing a file's records to disk" do before do # This way we start with some @records, like we would in real life. @class.stubs(:retrieve).returns [] @class.default_target = "/foo/bar" @class.initvars @class.prefetch @filetype = Puppet::Util::FileType.filetype(:flat).new("/my/file") Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file").returns @filetype @filetype.stubs(:write) end it "should back up the file being written if the filetype can be backed up" do @filetype.expects(:backup) @class.flush_target("/my/file") end it "should not try to back up the file if the filetype cannot be backed up" do @filetype = Puppet::Util::FileType.filetype(:ram).new("/my/file") Puppet::Util::FileType.filetype(:flat).expects(:new).returns @filetype @filetype.stubs(:write) @class.flush_target("/my/file") end it "should not back up the file more than once between calls to 'prefetch'" do @filetype.expects(:backup).once @class.flush_target("/my/file") @class.flush_target("/my/file") end it "should back the file up again once the file has been reread" do @filetype.expects(:backup).times(2) @class.flush_target("/my/file") @class.prefetch @class.flush_target("/my/file") end end end diff --git a/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb b/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb index 1bf8b7eb3..14bfdeb10 100644 --- a/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +++ b/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb @@ -1,1571 +1,1571 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'win32/taskscheduler' if Puppet.features.microsoft_windows? shared_examples_for "a trigger that handles start_date and start_time" do let(:trigger) do described_class.new( :name => 'Shared Test Task', :command => 'C:\Windows\System32\notepad.exe' ).translate_hash_to_trigger(trigger_hash) end before :each do Win32::TaskScheduler.any_instance.stubs(:save) end describe 'the given start_date' do before :each do trigger_hash['start_time'] = '00:00' end def date_component { 'start_year' => trigger['start_year'], 'start_month' => trigger['start_month'], 'start_day' => trigger['start_day'] } end it 'should be able to be specified in ISO 8601 calendar date format' do trigger_hash['start_date'] = '2011-12-31' date_component.should == { 'start_year' => 2011, 'start_month' => 12, 'start_day' => 31 } end it 'should fail if before 1753-01-01' do trigger_hash['start_date'] = '1752-12-31' expect { date_component }.to raise_error( Puppet::Error, 'start_date must be on or after 1753-01-01' ) end it 'should succeed if on 1753-01-01' do trigger_hash['start_date'] = '1753-01-01' date_component.should == { 'start_year' => 1753, 'start_month' => 1, 'start_day' => 1 } end it 'should succeed if after 1753-01-01' do trigger_hash['start_date'] = '1753-01-02' date_component.should == { 'start_year' => 1753, 'start_month' => 1, 'start_day' => 2 } end end describe 'the given start_time' do before :each do trigger_hash['start_date'] = '2011-12-31' end def time_component { 'start_hour' => trigger['start_hour'], 'start_minute' => trigger['start_minute'] } end it 'should be able to be specified as a 24-hour "hh:mm"' do trigger_hash['start_time'] = '17:13' time_component.should == { 'start_hour' => 17, 'start_minute' => 13 } end it 'should be able to be specified as a 12-hour "hh:mm am"' do trigger_hash['start_time'] = '3:13 am' time_component.should == { 'start_hour' => 3, 'start_minute' => 13 } end it 'should be able to be specified as a 12-hour "hh:mm pm"' do trigger_hash['start_time'] = '3:13 pm' time_component.should == { 'start_hour' => 15, 'start_minute' => 13 } end end end describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if => Puppet.features.microsoft_windows? do before :each do Puppet::Type.type(:scheduled_task).stubs(:defaultprovider).returns(described_class) end describe 'when retrieving' do before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) described_class.any_instance.stubs(:task).returns(@mock_task) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end let(:resource) { Puppet::Type.type(:scheduled_task).new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } describe 'the triggers for a task' do describe 'with only one trigger' do before :each do @mock_task.expects(:trigger_count).returns(1) end it 'should handle a single daily trigger' do @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_DAILY, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'days_interval' => 2 }, }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'daily', 'every' => '2', 'enabled' => true, 'index' => 0, } end it 'should handle a single weekly trigger' do scheduled_days_of_week = Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::FRIDAY | Win32::TaskScheduler::SUNDAY @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_WEEKLY, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'weeks_interval' => 2, 'days_of_week' => scheduled_days_of_week } }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'weekly', 'every' => '2', 'on' => ['sun', 'mon', 'wed', 'fri'], 'enabled' => true, 'index' => 0, } end it 'should handle a single monthly date-based trigger' do scheduled_months = Win32::TaskScheduler::JANUARY | Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::AUGUST | Win32::TaskScheduler::SEPTEMBER | Win32::TaskScheduler::DECEMBER # 1 3 5 15 'last' scheduled_days = 1 | 1 << 2 | 1 << 4 | 1 << 14 | 1 << 31 @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDATE, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'months' => scheduled_months, 'days' => scheduled_days } }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'monthly', 'months' => [1, 2, 8, 9, 12], 'on' => [1, 3, 5, 15, 'last'], 'enabled' => true, 'index' => 0, } end it 'should handle a single monthly day-of-week-based trigger' do scheduled_months = Win32::TaskScheduler::JANUARY | Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::AUGUST | Win32::TaskScheduler::SEPTEMBER | Win32::TaskScheduler::DECEMBER scheduled_days_of_week = Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::FRIDAY | Win32::TaskScheduler::SUNDAY @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDOW, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'months' => scheduled_months, 'weeks' => Win32::TaskScheduler::FIRST_WEEK, 'days_of_week' => scheduled_days_of_week } }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'monthly', 'months' => [1, 2, 8, 9, 12], 'which_occurrence' => 'first', 'day_of_week' => ['sun', 'mon', 'wed', 'fri'], 'enabled' => true, 'index' => 0, } end it 'should handle a single one-time trigger' do @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'once', 'enabled' => true, 'index' => 0, } end end it 'should handle multiple triggers' do @mock_task.expects(:trigger_count).returns(3) @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @mock_task.expects(:trigger).with(1).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2012, 'start_month' => 11, 'start_day' => 14, 'start_hour' => 15, 'start_minute' => 22, 'flags' => 0, }) @mock_task.expects(:trigger).with(2).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2013, 'start_month' => 12, 'start_day' => 15, 'start_hour' => 16, 'start_minute' => 23, 'flags' => 0, }) resource.provider.trigger.should =~ [ { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, }, { 'start_date' => '2012-11-14', 'start_time' => '15:22', 'schedule' => 'once', 'enabled' => true, 'index' => 1, }, { 'start_date' => '2013-12-15', 'start_time' => '16:23', 'schedule' => 'once', 'enabled' => true, 'index' => 2, } ] end it 'should skip triggers Win32::TaskScheduler cannot handle' do @mock_task.expects(:trigger_count).returns(3) @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @mock_task.expects(:trigger).with(1).raises( Win32::TaskScheduler::Error.new('Unhandled trigger type!') ) @mock_task.expects(:trigger).with(2).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2013, 'start_month' => 12, 'start_day' => 15, 'start_hour' => 16, 'start_minute' => 23, 'flags' => 0, }) resource.provider.trigger.should =~ [ { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, }, { 'start_date' => '2013-12-15', 'start_time' => '16:23', 'schedule' => 'once', 'enabled' => true, 'index' => 2, } ] end it 'should skip trigger types Puppet does not handle' do @mock_task.expects(:trigger_count).returns(3) @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @mock_task.expects(:trigger).with(1).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_EVENT_TRIGGER_AT_LOGON, }) @mock_task.expects(:trigger).with(2).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2013, 'start_month' => 12, 'start_day' => 15, 'start_hour' => 16, 'start_minute' => 23, 'flags' => 0, }) resource.provider.trigger.should =~ [ { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, }, { 'start_date' => '2013-12-15', 'start_time' => '16:23', 'schedule' => 'once', 'enabled' => true, 'index' => 2, } ] end end it 'should get the working directory from the working_directory on the task' do @mock_task.expects(:working_directory).returns('C:\Windows\System32') resource.provider.working_dir.should == 'C:\Windows\System32' end it 'should get the command from the application_name on the task' do @mock_task.expects(:application_name).returns('C:\Windows\System32\notepad.exe') resource.provider.command.should == 'C:\Windows\System32\notepad.exe' end it 'should get the command arguments from the parameters on the task' do @mock_task.expects(:parameters).returns('these are my arguments') resource.provider.arguments.should == 'these are my arguments' end it 'should get the user from the account_information on the task' do @mock_task.expects(:account_information).returns('this is my user') resource.provider.user.should == 'this is my user' end describe 'whether the task is enabled' do it 'should report tasks with the disabled bit set as disabled' do @mock_task.stubs(:flags).returns(Win32::TaskScheduler::DISABLED) resource.provider.enabled.should == :false end it 'should report tasks without the disabled bit set as enabled' do @mock_task.stubs(:flags).returns(~Win32::TaskScheduler::DISABLED) resource.provider.enabled.should == :true end it 'should not consider triggers for determining if the task is enabled' do @mock_task.stubs(:flags).returns(~Win32::TaskScheduler::DISABLED) @mock_task.stubs(:trigger_count).returns(1) @mock_task.stubs(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => Win32::TaskScheduler::TASK_TRIGGER_FLAG_DISABLED, }) resource.provider.enabled.should == :true end end end describe '#exists?' do before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) described_class.any_instance.stubs(:task).returns(@mock_task) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end let(:resource) { Puppet::Type.type(:scheduled_task).new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } it "should delegate to Win32::TaskScheduler using the resource's name" do @mock_task.expects(:exists?).with('Test Task').returns(true) resource.provider.exists?.should == true end end describe '#clear_task' do before :each do @mock_task = mock @new_mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @new_mock_task.responds_like(Win32::TaskScheduler.new) Win32::TaskScheduler.stubs(:new).returns(@mock_task, @new_mock_task) described_class.any_instance.stubs(:exists?).returns(false) end let(:resource) { Puppet::Type.type(:scheduled_task).new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } it 'should clear the cached task object' do resource.provider.task.should == @mock_task resource.provider.task.should == @mock_task resource.provider.clear_task resource.provider.task.should == @new_mock_task end it 'should clear the cached list of triggers for the task' do @mock_task.stubs(:trigger_count).returns(1) @mock_task.stubs(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @new_mock_task.stubs(:trigger_count).returns(1) @new_mock_task.stubs(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2012, 'start_month' => 11, 'start_day' => 14, 'start_hour' => 15, 'start_minute' => 22, 'flags' => 0, }) mock_task_trigger = { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, } resource.provider.trigger.should == mock_task_trigger resource.provider.trigger.should == mock_task_trigger resource.provider.clear_task resource.provider.trigger.should == { 'start_date' => '2012-11-14', 'start_time' => '15:22', 'schedule' => 'once', 'enabled' => true, 'index' => 0, } end end describe '.instances' do it 'should use the list of .job files to construct the list of scheduled_tasks' do job_files = ['foo.job', 'bar.job', 'baz.job'] Win32::TaskScheduler.any_instance.stubs(:tasks).returns(job_files) job_files.each do |job| job = File.basename(job, '.job') described_class.expects(:new).with(:provider => :win32_taskscheduler, :name => job) end described_class.instances end end describe '#user_insync?' do let(:resource) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') } before :each do Puppet::Util::ADSI.stubs(:sid_for_account).with('system').returns('SYSTEM SID') Puppet::Util::ADSI.stubs(:sid_for_account).with('joe').returns('SID A') Puppet::Util::ADSI.stubs(:sid_for_account).with('MACHINE\joe').returns('SID A') Puppet::Util::ADSI.stubs(:sid_for_account).with('bob').returns('SID B') end it 'should consider the user as in sync if the name matches' do resource.should be_user_insync('joe', ['joe']) end it 'should consider the user as in sync if the current user is fully qualified' do resource.should be_user_insync('MACHINE\joe', ['joe']) end it 'should consider a current user of the empty string to be the same as the system user' do resource.should be_user_insync('', ['system']) end it 'should consider different users as being different' do resource.should_not be_user_insync('joe', ['bob']) end end describe '#trigger_insync?' do let(:resource) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') } it 'should not consider any extra current triggers as in sync' do current = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] desired = {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'} resource.should_not be_trigger_insync(current, desired) end it 'should not consider any extra desired triggers as in sync' do current = {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'} desired = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] resource.should_not be_trigger_insync(current, desired) end it 'should consider triggers to be in sync if the sets of current and desired triggers are equal' do current = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] desired = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] resource.should be_trigger_insync(current, desired) end end describe '#triggers_same?' do let(:provider) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') } it "should not consider a disabled 'current' trigger to be the same" do current = {'schedule' => 'once', 'enabled' => false} desired = {'schedule' => 'once'} provider.should_not be_triggers_same(current, desired) end it 'should not consider triggers with different schedules to be the same' do current = {'schedule' => 'once'} desired = {'schedule' => 'weekly'} provider.should_not be_triggers_same(current, desired) end describe 'comparing daily triggers' do it "should consider 'desired' triggers not specifying 'every' to have the same value as the 'current' trigger" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30'} provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2012-09-12', 'start_time' => '15:30', 'every' => 3} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:31', 'every' => 3} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'weekly', 'start_date' => '2011-9-12', 'start_time' => '15:30', 'every' => 3} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '5:30', 'every' => 3} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '05:30', 'every' => 3} provider.should be_triggers_same(current, desired) end it "should consider different 'every' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 1} provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30', 'every' => 1} provider.should be_triggers_same(trigger, trigger) end end describe 'comparing one-time triggers' do it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30'} desired = {'schedule' => 'daily', 'start_date' => '2012-09-12', 'start_time' => '15:30'} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30'} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:31'} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30'} desired = {'schedule' => 'weekly', 'start_date' => '2011-9-12', 'start_time' => '15:30'} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '1:30'} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30'} provider.should be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30'} provider.should be_triggers_same(trigger, trigger) end end describe 'comparing monthly date-based triggers' do it "should consider 'desired' triggers not specifying 'months' to have the same value as the 'current' trigger" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'on' => [1,'last']} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'on' => [1, 'last']} provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-10-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '22:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-9-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '5:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '05:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should be_triggers_same(current, desired) end it "should consider different 'months' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1], 'on' => [1, 3, 5, 7]} provider.should_not be_triggers_same(current, desired) end it "should consider different 'on' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 5, 7]} provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should be_triggers_same(trigger, trigger) end end describe 'comparing monthly day-of-week-based triggers' do it "should consider 'desired' triggers not specifying 'months' to have the same value as the 'current' trigger" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-10-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '22:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'months' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3, 5, 7, 9], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'which_occurrence' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'last', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'day_of_week' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['fri'] } provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should be_triggers_same(trigger, trigger) end end describe 'comparing weekly triggers' do it "should consider 'desired' triggers not specifying 'day_of_week' to have the same value as the 'current' trigger" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-10-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '22:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-9-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '1:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should be_triggers_same(current, desired) end it "should consider different 'every' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 1, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should_not be_triggers_same(current, desired) end it "should consider different 'day_of_week' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['fri']} provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should be_triggers_same(trigger, trigger) end end end describe '#normalized_date' do it 'should format the date without leading zeros' do described_class.normalized_date('2011-01-01').should == '2011-1-1' end end describe '#normalized_time' do it 'should format the time as {24h}:{minutes}' do described_class.normalized_time('8:37 PM').should == '20:37' end end describe '#translate_hash_to_trigger' do before :each do @puppet_trigger = { 'start_date' => '2011-1-1', 'start_time' => '01:10' } end let(:provider) { described_class.new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } let(:trigger) { provider.translate_hash_to_trigger(@puppet_trigger) } describe 'when given a one-time trigger' do before :each do @puppet_trigger['schedule'] = 'once' end it 'should set the trigger_type to Win32::TaskScheduler::ONCE' do trigger['trigger_type'].should == Win32::TaskScheduler::ONCE end it 'should not set a type' do trigger.should_not be_has_key('type') end it "should require 'start_date'" do @puppet_trigger.delete('start_date') expect { trigger }.to raise_error( Puppet::Error, /Must specify 'start_date' when defining a one-time trigger/ ) end it "should require 'start_time'" do @puppet_trigger.delete('start_time') expect { trigger }.to raise_error( Puppet::Error, /Must specify 'start_time' when defining a trigger/ ) end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'once' }} end end describe 'when given a daily trigger' do before :each do @puppet_trigger['schedule'] = 'daily' end it "should default 'every' to 1" do trigger['type']['days_interval'].should == 1 end it "should use the specified value for 'every'" do @puppet_trigger['every'] = 5 trigger['type']['days_interval'].should == 5 end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'daily', 'every' => 1}} end end describe 'when given a weekly trigger' do before :each do @puppet_trigger['schedule'] = 'weekly' end it "should default 'every' to 1" do trigger['type']['weeks_interval'].should == 1 end it "should use the specified value for 'every'" do @puppet_trigger['every'] = 4 trigger['type']['weeks_interval'].should == 4 end it "should default 'day_of_week' to be every day of the week" do trigger['type']['days_of_week'].should == Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::TUESDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::THURSDAY | Win32::TaskScheduler::FRIDAY | Win32::TaskScheduler::SATURDAY | Win32::TaskScheduler::SUNDAY end it "should use the specified value for 'day_of_week'" do @puppet_trigger['day_of_week'] = ['mon', 'wed', 'fri'] trigger['type']['days_of_week'].should == Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::FRIDAY end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'weekly', 'every' => 1, 'day_of_week' => 'mon'}} end end shared_examples_for 'a monthly schedule' do it "should default 'months' to be every month" do trigger['type']['months'].should == Win32::TaskScheduler::JANUARY | Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::MARCH | Win32::TaskScheduler::APRIL | Win32::TaskScheduler::MAY | Win32::TaskScheduler::JUNE | Win32::TaskScheduler::JULY | Win32::TaskScheduler::AUGUST | Win32::TaskScheduler::SEPTEMBER | Win32::TaskScheduler::OCTOBER | Win32::TaskScheduler::NOVEMBER | Win32::TaskScheduler::DECEMBER end it "should use the specified value for 'months'" do @puppet_trigger['months'] = [2, 8] trigger['type']['months'].should == Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::AUGUST end end describe 'when given a monthly date-based trigger' do before :each do @puppet_trigger['schedule'] = 'monthly' @puppet_trigger['on'] = [7, 14] end it_behaves_like 'a monthly schedule' it "should not allow 'which_occurrence' to be specified" do @puppet_trigger['which_occurrence'] = 'first' expect {trigger}.to raise_error( Puppet::Error, /Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger/ ) end it "should not allow 'day_of_week' to be specified" do @puppet_trigger['day_of_week'] = 'mon' expect {trigger}.to raise_error( Puppet::Error, /Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger/ ) end it "should require 'on'" do @puppet_trigger.delete('on') expect {trigger}.to raise_error( Puppet::Error, /Don't know how to create a 'monthly' schedule with the options: schedule, start_date, start_time/ ) end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'monthly', 'months' => 1, 'on' => 1}} end end describe 'when given a monthly day-of-week-based trigger' do before :each do @puppet_trigger['schedule'] = 'monthly' @puppet_trigger['which_occurrence'] = 'first' @puppet_trigger['day_of_week'] = 'mon' end it_behaves_like 'a monthly schedule' it "should not allow 'on' to be specified" do @puppet_trigger['on'] = 15 expect {trigger}.to raise_error( Puppet::Error, /Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger/ ) end it "should require 'which_occurrence'" do @puppet_trigger.delete('which_occurrence') expect {trigger}.to raise_error( Puppet::Error, /which_occurrence must be specified when creating a monthly day-of-week based trigger/ ) end it "should require 'day_of_week'" do @puppet_trigger.delete('day_of_week') expect {trigger}.to raise_error( Puppet::Error, /day_of_week must be specified when creating a monthly day-of-week based trigger/ ) end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'monthly', 'months' => 1, 'which_occurrence' => 'first', 'day_of_week' => 'mon'}} end end end describe '#validate_trigger' do let(:provider) { described_class.new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } it 'should succeed if all passed triggers translate from hashes to triggers' do triggers_to_validate = [ {'schedule' => 'once', 'start_date' => '2011-09-13', 'start_time' => '13:50'}, {'schedule' => 'weekly', 'start_date' => '2011-09-13', 'start_time' => '13:50', 'day_of_week' => 'mon'} ] provider.validate_trigger(triggers_to_validate).should == true end it 'should use the exception from translate_hash_to_trigger when it fails' do triggers_to_validate = [ {'schedule' => 'once', 'start_date' => '2011-09-13', 'start_time' => '13:50'}, {'schedule' => 'monthly', 'this is invalid' => true} ] expect {provider.validate_trigger(triggers_to_validate)}.to raise_error( Puppet::Error, /#{Regexp.escape("Unknown trigger option(s): ['this is invalid']")}/ ) end end describe '#flush' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :ensure => @ensure ) end before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) @command = 'C:\Windows\System32\notepad.exe' end describe 'when :ensure is :present' do before :each do @ensure = :present end it 'should save the task' do @mock_task.expects(:save) resource.provider.flush end it 'should fail if the command is not specified' do resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :ensure => @ensure ) expect { resource.provider.flush }.to raise_error( Puppet::Error, 'Parameter command is required.' ) end end describe 'when :ensure is :absent' do before :each do @ensure = :absent @mock_task.stubs(:activate) end it 'should not save the task if :ensure is :absent' do @mock_task.expects(:save).never resource.provider.flush end it 'should not fail if the command is not specified' do @mock_task.stubs(:save) resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :ensure => @ensure ) resource.provider.flush end end end describe 'property setter methods' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\dummy_task.exe' ) end before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end describe '#command=' do it 'should set the application_name on the task' do @mock_task.expects(:application_name=).with('C:\Windows\System32\notepad.exe') resource.provider.command = 'C:\Windows\System32\notepad.exe' end end describe '#arguments=' do it 'should set the parameters on the task' do @mock_task.expects(:parameters=).with(['/some /arguments /here']) resource.provider.arguments = ['/some /arguments /here'] end end describe '#working_dir=' do it 'should set the working_directory on the task' do @mock_task.expects(:working_directory=).with('C:\Windows\System32') resource.provider.working_dir = 'C:\Windows\System32' end end describe '#enabled=' do it 'should set the disabled flag if the task should be disabled' do @mock_task.stubs(:flags).returns(0) @mock_task.expects(:flags=).with(Win32::TaskScheduler::DISABLED) resource.provider.enabled = :false end it 'should clear the disabled flag if the task should be enabled' do @mock_task.stubs(:flags).returns(Win32::TaskScheduler::DISABLED) @mock_task.expects(:flags=).with(0) resource.provider.enabled = :true end end describe '#trigger=' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :trigger => @trigger ) end before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end it 'should not consider all duplicate current triggers in sync with a single desired trigger' do @trigger = {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10'} current_triggers = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 0}, {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 1}, {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 2}, ] resource.provider.stubs(:trigger).returns(current_triggers) @mock_task.expects(:delete_trigger).with(1) @mock_task.expects(:delete_trigger).with(2) resource.provider.trigger = @trigger end it 'should remove triggers not defined in the resource' do @trigger = {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10'} current_triggers = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 0}, {'schedule' => 'once', 'start_date' => '2012-09-15', 'start_time' => '15:10', 'index' => 1}, {'schedule' => 'once', 'start_date' => '2013-09-15', 'start_time' => '15:10', 'index' => 2}, ] resource.provider.stubs(:trigger).returns(current_triggers) @mock_task.expects(:delete_trigger).with(1) @mock_task.expects(:delete_trigger).with(2) resource.provider.trigger = @trigger end it 'should add triggers defined in the resource, but not found on the system' do @trigger = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10'}, {'schedule' => 'once', 'start_date' => '2012-09-15', 'start_time' => '15:10'}, {'schedule' => 'once', 'start_date' => '2013-09-15', 'start_time' => '15:10'}, ] current_triggers = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 0}, ] resource.provider.stubs(:trigger).returns(current_triggers) @mock_task.expects(:trigger=).with(resource.provider.translate_hash_to_trigger(@trigger[1])) @mock_task.expects(:trigger=).with(resource.provider.translate_hash_to_trigger(@trigger[2])) resource.provider.trigger = @trigger end end describe '#user=' do before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end it 'should use nil for user and password when setting the user to the SYSTEM account' do Puppet::Util::ADSI.stubs(:sid_for_account).with('system').returns('SYSTEM SID') resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\dummy_task.exe', :user => 'system' ) @mock_task.expects(:set_account_information).with(nil, nil) resource.provider.user = 'system' end it 'should use the specified user and password when setting the user to anything other than SYSTEM' do Puppet::Util::ADSI.stubs(:sid_for_account).with('my_user_name').returns('SID A') resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\dummy_task.exe', :user => 'my_user_name', :password => 'my password' ) @mock_task.expects(:set_account_information).with('my_user_name', 'my password') resource.provider.user = 'my_user_name' end end end describe '#create' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :enabled => @enabled, :command => @command, :arguments => @arguments, :working_dir => @working_dir, :trigger => { 'schedule' => 'once', 'start_date' => '2011-09-27', 'start_time' => '17:00' } ) end before :each do @enabled = :true @command = 'C:\Windows\System32\notepad.exe' @arguments = '/a /list /of /arguments' @working_dir = 'C:\Windows\Some\Directory' @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) @mock_task.stubs(:application_name=) @mock_task.stubs(:parameters=) @mock_task.stubs(:working_directory=) @mock_task.stubs(:set_account_information) @mock_task.stubs(:flags) @mock_task.stubs(:flags=) @mock_task.stubs(:trigger_count).returns(0) @mock_task.stubs(:trigger=) @mock_task.stubs(:save) Win32::TaskScheduler.stubs(:new).returns(@mock_task) described_class.any_instance.stubs(:sync_triggers) end it 'should set the command' do resource.provider.expects(:command=).with(@command) resource.provider.create end it 'should set the arguments' do resource.provider.expects(:arguments=).with([@arguments]) resource.provider.create end it 'should set the working_dir' do resource.provider.expects(:working_dir=).with(@working_dir) resource.provider.create end it "should set the user" do resource.provider.expects(:user=).with(:system) resource.provider.create end it 'should set the enabled property' do resource.provider.expects(:enabled=) resource.provider.create end it 'should sync triggers' do resource.provider.expects(:trigger=) resource.provider.create end end end diff --git a/spec/unit/provider/selboolean_spec.rb b/spec/unit/provider/selboolean_spec.rb index 64f925e1e..54c7b50ef 100755 --- a/spec/unit/provider/selboolean_spec.rb +++ b/spec/unit/provider/selboolean_spec.rb @@ -1,36 +1,36 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:selboolean).provider(:getsetsebool) describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" @provider = provider_class.new(@resource) end it "should return :on when getsebool returns on" do @provider.expects(:getsebool).with("foo").returns "foo --> on\n" @provider.value.should == :on end it "should return :off when getsebool returns on" do @provider.expects(:getsebool).with("foo").returns "foo --> off\n" @provider.value.should == :off end it "should call execpipe when updating boolean setting" do @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" @provider.expects(:execpipe).with("/usr/sbin/setsebool foo off") @provider.value = :off end it "should call execpipe with -P when updating persistent boolean setting" do @resource.stubs(:[]).with(:persistent).returns :true @provider.expects(:command).with(:setsebool).returns "/usr/sbin/setsebool" @provider.expects(:execpipe).with("/usr/sbin/setsebool -P foo off") @provider.value = :off end end diff --git a/spec/unit/provider/selmodule-example.pp b/spec/unit/provider/selmodule-example.pp index 28166ca40..e83782a64 100644 Binary files a/spec/unit/provider/selmodule-example.pp and b/spec/unit/provider/selmodule-example.pp differ diff --git a/spec/unit/provider/selmodule_spec.rb b/spec/unit/provider/selmodule_spec.rb index 199f2edf7..703b8ea4a 100755 --- a/spec/unit/provider/selmodule_spec.rb +++ b/spec/unit/provider/selmodule_spec.rb @@ -1,73 +1,73 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # Note: This unit test depends on having a sample SELinux policy file # in the same directory as this test called selmodule-example.pp # with version 1.5.0. The provided selmodule-example.pp is the first # 256 bytes taken from /usr/share/selinux/targeted/nagios.pp on Fedora 9 require 'spec_helper' require 'stringio' provider_class = Puppet::Type.type(:selmodule).provider(:semodule) describe provider_class do before :each do @resource = stub("resource", :name => "foo") @resource.stubs(:[]).returns "foo" @provider = provider_class.new(@resource) end describe "exists? method" do it "should find a module if it is already loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("bar\t1.2.3\nfoo\t4.4.4\nbang\t1.0.0\n") @provider.exists?.should == :true end it "should return nil if not loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("bar\t1.2.3\nbang\t1.0.0\n") @provider.exists?.should be_nil end it "should return nil if no modules are loaded" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("") @provider.exists?.should be_nil end end describe "selmodversion_file" do it "should return 1.5.0 for the example policy file" do @provider.expects(:selmod_name_to_filename).returns "#{File.dirname(__FILE__)}/selmodule-example.pp" @provider.selmodversion_file.should == "1.5.0" end end describe "syncversion" do it "should return :true if loaded and file modules are in sync" do @provider.expects(:selmodversion_loaded).returns "1.5.0" @provider.expects(:selmodversion_file).returns "1.5.0" @provider.syncversion.should == :true end it "should return :false if loaded and file modules are not in sync" do @provider.expects(:selmodversion_loaded).returns "1.4.0" @provider.expects(:selmodversion_file).returns "1.5.0" @provider.syncversion.should == :false end it "should return before checking file version if no loaded policy" do @provider.expects(:selmodversion_loaded).returns nil @provider.syncversion.should == :false end end describe "selmodversion_loaded" do it "should return the version of a loaded module" do @provider.expects(:command).with(:semodule).returns "/usr/sbin/semodule" @provider.expects(:execpipe).with("/usr/sbin/semodule --list").yields StringIO.new("bar\t1.2.3\nfoo\t4.4.4\nbang\t1.0.0\n") @provider.selmodversion_loaded.should == "4.4.4" end end end diff --git a/spec/unit/provider/service/base_spec.rb b/spec/unit/provider/service/base_spec.rb index 03a33e259..a26beb20f 100755 --- a/spec/unit/provider/service/base_spec.rb +++ b/spec/unit/provider/service/base_spec.rb @@ -1,77 +1,77 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'rbconfig' require 'fileutils' provider_class = Puppet::Type.type(:service).provider(:init) describe "base service provider" do include PuppetSpec::Files let :type do Puppet::Type.type(:service) end let :provider do type.provider(:base) end subject { provider } context "basic operations" do # Cross-platform file interactions. Fun times. Ruby = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["RUBY_INSTALL_NAME"] + RbConfig::CONFIG["EXEEXT"]) Start = [Ruby, '-rfileutils', '-e', 'FileUtils.touch(ARGV[0])'] Status = [Ruby, '-e' 'exit File.file?(ARGV[0])'] Stop = [Ruby, '-e', 'File.exist?(ARGV[0]) and File.unlink(ARGV[0])'] let :flag do tmpfile('base-service-test') end subject do type.new(:name => "test", :provider => :base, :start => Start + [flag], :status => Status + [flag], :stop => Stop + [flag] ).provider end before :each do File.unlink(flag) if File.exist?(flag) end it { should be } it "should invoke the start command if not running" do File.should_not be_file flag subject.start File.should be_file flag end it "should be stopped before being started" do subject.status.should == :stopped end it "should be running after being started" do subject.start subject.status.should == :running end it "should invoke the stop command when asked" do subject.start subject.status.should == :running subject.stop subject.status.should == :stopped File.should_not be_file flag end it "should start again even if already running" do subject.start subject.expects(:ucommand).with(:start) subject.start end it "should stop again even if already stopped" do subject.stop subject.expects(:ucommand).with(:stop) subject.stop end end end diff --git a/spec/unit/provider/service/daemontools_spec.rb b/spec/unit/provider/service/daemontools_spec.rb index 64eeb9fa4..d9d9a3f60 100755 --- a/spec/unit/provider/service/daemontools_spec.rb +++ b/spec/unit/provider/service/daemontools_spec.rb @@ -1,166 +1,166 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the Daemontools service Provider # # author Brice Figureau # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:daemontools) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @servicedir = "/etc/service" @provider.servicedir=@servicedir @daemondir = "/var/lib/service" @provider.class.defpath=@daemondir # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, source and path (because we won't run # the thing that will fetch the resource path from the provider) @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:path).returns @daemondir @resource.stubs(:ref).returns "Service[myservice]" @provider.resource = @resource @provider.stubs(:command).with(:svc).returns "svc" @provider.stubs(:command).with(:svstat).returns "svstat" @provider.stubs(:svc) @provider.stubs(:svstat) end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when starting" do it "should use 'svc' to start the service" do @provider.stubs(:enabled?).returns :true @provider.expects(:svc).with("-u", "/etc/service/myservice") @provider.start end it "should enable the service if it is not enabled" do @provider.stubs(:svc) @provider.expects(:enabled?).returns :false @provider.expects(:enable) @provider.start end end describe "when stopping" do it "should use 'svc' to stop the service" do @provider.stubs(:disable) @provider.expects(:svc).with("-d", "/etc/service/myservice") @provider.stop end end describe "when restarting" do it "should use 'svc' to restart the service" do @provider.expects(:svc).with("-t", "/etc/service/myservice") @provider.restart end end describe "when enabling" do it "should create a symlink between daemon dir and service dir" do FileTest.stubs(:symlink?).returns(false) File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) @provider.enable end end describe "when disabling" do it "should remove the symlink between daemon dir and service dir" do FileTest.stubs(:directory?).returns(false) FileTest.stubs(:symlink?).returns(true) File.expects(:unlink).with(File.join(@servicedir,"myservice")) @provider.stubs(:texecute).returns("") @provider.disable end it "should stop the service" do FileTest.stubs(:directory?).returns(false) FileTest.stubs(:symlink?).returns(true) File.stubs(:unlink) @provider.expects(:stop) @provider.disable end end describe "when checking if the service is enabled?" do it "should return true if it is running" do @provider.stubs(:status).returns(:running) @provider.enabled?.should == :true end [true, false].each do |t| it "should return #{t} if the symlink exists" do @provider.stubs(:status).returns(:stopped) FileTest.stubs(:symlink?).returns(t) @provider.enabled?.should == "#{t}".to_sym end end end describe "when checking status" do it "should call the external command 'svstat /etc/service/myservice'" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")) @provider.status end end describe "when checking status" do it "and svstat fails, properly raise a Puppet::Error" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).raises(Puppet::ExecutionFailure, "failure") lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: failure') end it "and svstat returns up, then return :running" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: up (pid 454) 954326 seconds") @provider.status.should == :running end it "and svstat returns not running, then return :stopped" do @provider.expects(:svstat).with(File.join(@servicedir,"myservice")).returns("/etc/service/myservice: supervise not running") @provider.status.should == :stopped end end end diff --git a/spec/unit/provider/service/debian_spec.rb b/spec/unit/provider/service/debian_spec.rb index f858568dc..6bb34e753 100755 --- a/spec/unit/provider/service/debian_spec.rb +++ b/spec/unit/provider/service/debian_spec.rb @@ -1,116 +1,116 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the debian service provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:debian) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, source and path @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:ref).returns "Service[myservice]" @provider.resource = @resource @provider.stubs(:command).with(:update_rc).returns "update_rc" @provider.stubs(:command).with(:invoke_rc).returns "invoke_rc" @provider.stubs(:update_rc) @provider.stubs(:invoke_rc) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when enabling" do it "should call update-rc.d twice" do @provider.expects(:update_rc).twice @provider.enable end end describe "when disabling" do it "should be able to disable services with newer sysv-rc versions" do @provider.stubs(:`).with("dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?").returns "0" @provider.expects(:update_rc).with(@resource[:name], "disable") @provider.disable end it "should be able to enable services with older sysv-rc versions" do @provider.stubs(:`).with("dpkg --compare-versions $(dpkg-query -W --showformat '${Version}' sysv-rc) ge 2.88 ; echo $?").returns "1" @provider.expects(:update_rc).with("-f", @resource[:name], "remove") @provider.expects(:update_rc).with(@resource[:name], "stop", "00", "1", "2", "3", "4", "5", "6", ".") @provider.disable end end describe "when checking whether it is enabled" do it "should call Kernel.system() with the appropriate parameters" do @provider.expects(:system).with("/usr/sbin/invoke-rc.d", "--quiet", "--query", @resource[:name], "start").once @provider.enabled? end it "should return true when invoke-rc.d exits with 104 status" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(104) @provider.enabled?.should == :true end it "should return true when invoke-rc.d exits with 106 status" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(106) @provider.enabled?.should == :true end context "when invoke-rc.d exits with 105 status" do it "links count is 4" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(105) @provider.stubs(:get_start_link_count).returns(4) @provider.enabled?.should == :true end it "links count is less than 4" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(105) @provider.stubs(:get_start_link_count).returns(3) @provider.enabled?.should == :false end end # pick a range of non-[104.106] numbers, strings and booleans to test with. [-100, -1, 0, 1, 100, "foo", "", :true, :false].each do |exitstatus| it "should return false when invoke-rc.d exits with #{exitstatus} status" do @provider.stubs(:system) $CHILD_STATUS.stubs(:exitstatus).returns(exitstatus) @provider.enabled?.should == :false end end end end diff --git a/spec/unit/provider/service/freebsd_spec.rb b/spec/unit/provider/service/freebsd_spec.rb index c1a6d26f7..92276e0cb 100755 --- a/spec/unit/provider/service/freebsd_spec.rb +++ b/spec/unit/provider/service/freebsd_spec.rb @@ -1,49 +1,49 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:freebsd) describe provider_class do before :each do @provider = provider_class.new @provider.stubs(:initscript) end it "should correctly parse rcvar for FreeBSD < 7" do @provider.stubs(:execute).returns <= 8.1" do @provider.stubs(:execute).returns <= 7" do @provider.stubs(:rcvar).returns(['# ntpd', 'ntpd_enable="YES"', '# (default: "")']) @provider.rcvar_value.should == "YES" end end diff --git a/spec/unit/provider/service/gentoo_spec.rb b/spec/unit/provider/service/gentoo_spec.rb index 0a2f52ea6..268bef46e 100755 --- a/spec/unit/provider/service/gentoo_spec.rb +++ b/spec/unit/provider/service/gentoo_spec.rb @@ -1,238 +1,238 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:service).provider(:gentoo) do before :each do Puppet::Type.type(:service).stubs(:defaultprovider).returns described_class FileTest.stubs(:file?).with('/sbin/rc-update').returns true FileTest.stubs(:executable?).with('/sbin/rc-update').returns true Facter.stubs(:value).with(:operatingsystem).returns 'Gentoo' # The initprovider (parent of the gentoo provider) does a stat call # before it even tries to execute an initscript. We use sshd in all the # tests so make sure it is considered present. File.stubs(:stat).with('/etc/init.d/sshd') end let :initscripts do [ 'alsasound', 'bootmisc', 'functions.sh', 'hwclock', 'reboot.sh', 'rsyncd', 'shutdown.sh', 'sshd', 'vixie-cron', 'wpa_supplicant', 'xdm-setup' ] end let :helperscripts do [ 'functions.sh', 'reboot.sh', 'shutdown.sh' ] end describe ".instances" do it "should have an instances method" do described_class.should respond_to :instances end it "should get a list of services from /etc/init.d but exclude helper scripts" do FileTest.expects(:directory?).with('/etc/init.d').returns true File.stubs(:symlink?).returns(false) Dir.expects(:entries).with('/etc/init.d').returns initscripts (initscripts - helperscripts).each do |script| FileTest.expects(:executable?).with("/etc/init.d/#{script}").returns true end helperscripts.each do |script| FileTest.expects(:executable?).with("/etc/init.d/#{script}").never end described_class.instances.map(&:name).should == [ 'alsasound', 'bootmisc', 'hwclock', 'rsyncd', 'sshd', 'vixie-cron', 'wpa_supplicant', 'xdm-setup' ] end end describe "#start" do it "should use the supplied start command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo')) provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :squelch => true) provider.start end it "should start the service with start otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) provider.expects(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :squelch => true) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.start end end describe "#stop" do it "should use the supplied stop command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo')) provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :squelch => true) provider.stop end it "should stop the service with stop otherwise" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) provider.expects(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :squelch => true) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.stop end end describe "#enabled?" do before :each do described_class.any_instance.stubs(:update).with(:show).returns File.read(my_fixture('rc_update_show')) end it "should run rc-update show to get a list of enabled services" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) provider.expects(:update).with(:show).returns "\n" provider.enabled? end ['hostname', 'net.lo', 'procfs'].each do |service| it "should consider service #{service} in runlevel boot as enabled" do provider = described_class.new(Puppet::Type.type(:service).new(:name => service)) provider.enabled?.should == :true end end ['alsasound', 'xdm', 'netmount'].each do |service| it "should consider service #{service} in runlevel default as enabled" do provider = described_class.new(Puppet::Type.type(:service).new(:name => service)) provider.enabled?.should == :true end end ['rsyncd', 'lighttpd', 'mysql'].each do |service| it "should consider unused service #{service} as disabled" do provider = described_class.new(Puppet::Type.type(:service).new(:name => service)) provider.enabled?.should == :false end end end describe "#enable" do it "should run rc-update add to enable a service" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) provider.expects(:update).with(:add, 'sshd', :default) provider.enable end end describe "#disable" do it "should run rc-update del to disable a service" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd')) provider.expects(:update).with(:del, 'sshd', :default) provider.disable end end describe "#status" do describe "when a special status command is specified" do it "should use the status command from the resource" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true).never provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :squelch => true) provider.status end it "should return :stopped when the status command returns with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true).never provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :squelch => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end it "should return :running when the status command returns with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo')) provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true).never provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :squelch => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end end describe "when hasstatus is false" do it "should return running if a pid can be found" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false)) provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true).never provider.expects(:getpid).returns 1000 provider.status.should == :running end it "should return stopped if no pid can be found" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => false)) provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true).never provider.expects(:getpid).returns nil provider.status.should == :stopped end end describe "when hasstatus is true" do it "should return running if status exits with a zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true) $CHILD_STATUS.stubs(:exitstatus).returns 0 provider.status.should == :running end it "should return stopped if status exits with a non-zero exitcode" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasstatus => true)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.expects(:execute).with(['/etc/init.d/sshd',:status], :failonfail => false, :squelch => true) $CHILD_STATUS.stubs(:exitstatus).returns 3 provider.status.should == :stopped end end end describe "#restart" do it "should use the supplied restart command if specified" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo')) provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :squelch => true).never provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :squelch => true) provider.restart end it "should restart the service with restart if hasrestart is true" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :squelch => true) provider.restart end it "should restart the service with stop/start if hasrestart is false" do provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false)) provider.expects(:search).with('sshd').returns('/etc/init.d/sshd') provider.expects(:execute).with(['/etc/init.d/sshd',:restart], :failonfail => true, :squelch => true).never provider.expects(:execute).with(['/etc/init.d/sshd',:stop], :failonfail => true, :squelch => true) provider.expects(:execute).with(['/etc/init.d/sshd',:start], :failonfail => true, :squelch => true) provider.restart end end end diff --git a/spec/unit/provider/service/init_spec.rb b/spec/unit/provider/service/init_spec.rb index 60c7e806d..61dcf5db2 100755 --- a/spec/unit/provider/service/init_spec.rb +++ b/spec/unit/provider/service/init_spec.rb @@ -1,191 +1,191 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the Init service Provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:init) describe provider_class do before :each do @class = Puppet::Type.type(:service).provider(:init) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:path).returns ["/service/path","/alt/service/path"] File.stubs(:directory?).returns(true) @provider = provider_class.new @provider.resource = @resource end describe "when getting all service instances" do before :each do @services = ['one', 'two', 'three', 'four'] Dir.stubs(:entries).returns @services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) @class.stubs(:defpath).returns('tmp') end it "should return instances for all services" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = @services.collect {|x| "#{x}_instance"} @class.instances.should == results end it "should omit an array of services from exclude list" do exclude = ['two', 'four'] (@services - exclude).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = (@services-exclude).collect {|x| "#{x}_instance"} @class.get_services(@class.defpath, exclude).should == results end it "should omit a single service from the exclude list" do exclude = 'two' (@services - [exclude]).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst}.returns("#{inst}_instance") end results = @services.reject{|x| x == exclude }.collect {|x| "#{x}_instance"} @class.get_services(@class.defpath, exclude).should == results end it "should use defpath" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:path] == @class.defpath}.returns("#{inst}_instance") end results = @services.sort.collect {|x| "#{x}_instance"} @class.instances.sort.should == results end it "should set hasstatus to true for providers" do @services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:hasstatus] == true}.returns("#{inst}_instance") end results = @services.collect {|x| "#{x}_instance"} @class.instances.should == results end it "should discard upstart jobs" do not_init_service, *valid_services = @services valid_services.each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:hasstatus] == true}.returns("#{inst}_instance") end File.stubs(:symlink?).returns(false) File.stubs(:symlink?).with("tmp/#{not_init_service}").returns(true) File.stubs(:readlink).with("tmp/#{not_init_service}").returns("/lib/init/upstart-job") results = valid_services.collect {|x| "#{x}_instance"} @class.instances.should == results end end describe "when searching for the init script" do it "should discard paths that do not exist" do File.stubs(:exist?).returns(false) File.stubs(:directory?).returns(false) @provider.paths.should be_empty end it "should discard paths that are not directories" do File.stubs(:exist?).returns(true) File.stubs(:directory?).returns(false) @provider.paths.should be_empty end it "should be able to find the init script in the service path" do File.stubs(:stat).raises(Errno::ENOENT.new('No such file or directory')) File.expects(:stat).with("/service/path/myservice").returns true @provider.initscript.should == "/service/path/myservice" end it "should be able to find the init script in the service path" do File.stubs(:stat).raises(Errno::ENOENT.new('No such file or directory')) File.expects(:stat).with("/alt/service/path/myservice").returns true @provider.initscript.should == "/alt/service/path/myservice" end it "should fail if the service isn't there" do lambda { @provider.initscript }.should raise_error(Puppet::Error, "Could not find init script for 'myservice'") end end describe "if the init script is present" do before :each do File.stubs(:stat).with("/service/path/myservice").returns true end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do @resource.stubs(:[]).with(method).returns "/user/specified/command" @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } @provider.send(method) end it "should pass #{method} to the init script when no explicit command is provided" do @resource.stubs(:[]).with("has#{method}".intern).returns :true @provider.expects(:execute).with { |command, *args| command == ["/service/path/myservice",method]} @provider.send(method) end end end describe "when checking status" do describe "when hasstatus is :true" do before :each do @resource.stubs(:[]).with(:hasstatus).returns :true end it "should execute the command" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") @provider.status end it "should consider the process running if the command returns 0" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.status.should == :running end [-10,-1,1,10].each { |ec| it "should consider the process stopped if the command returns something non-0" do @provider.expects(:texecute).with(:status, ['/service/path/myservice', :status], false).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(ec) @provider.status.should == :stopped end } end describe "when hasstatus is not :true" do it "should consider the service :running if it has a pid" do @provider.expects(:getpid).returns "1234" @provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do @provider.expects(:getpid).returns nil @provider.status.should == :stopped end end end describe "when restarting and hasrestart is not :true" do it "should stop and restart the process" do @provider.expects(:texecute).with(:stop, ['/service/path/myservice', :stop ], true).returns("") @provider.expects(:texecute).with(:start,['/service/path/myservice', :start], true).returns("") $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.restart end end end end diff --git a/spec/unit/provider/service/redhat_spec.rb b/spec/unit/provider/service/redhat_spec.rb index c93932403..d7dda21ce 100755 --- a/spec/unit/provider/service/redhat_spec.rb +++ b/spec/unit/provider/service/redhat_spec.rb @@ -1,130 +1,130 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the RedHat service Provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:redhat) describe provider_class, :as_platform => :posix do before :each do @class = Puppet::Type.type(:service).provider(:redhat) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" @provider = provider_class.new @resource.stubs(:provider).returns @provider @provider.resource = @resource @provider.stubs(:get).with(:hasstatus).returns false FileTest.stubs(:file?).with('/sbin/service').returns true FileTest.stubs(:executable?).with('/sbin/service').returns true end osfamily = [ 'redhat', 'suse' ] osfamily.each do |osfamily| it "should be the default provider on #{osfamily}" do Facter.expects(:value).with(:osfamily).returns(osfamily) provider_class.default?.should be_true end end # test self.instances describe "when getting all service instances" do before :each do @services = ['one', 'two', 'three', 'four', 'kudzu', 'functions', 'halt', 'killall', 'single', 'linuxconf', 'boot', 'reboot'] @not_services = ['functions', 'halt', 'killall', 'single', 'linuxconf', 'reboot', 'boot'] Dir.stubs(:entries).returns @services FileTest.stubs(:directory?).returns(true) FileTest.stubs(:executable?).returns(true) end it "should return instances for all services" do (@services-@not_services).each do |inst| @class.expects(:new).with{|hash| hash[:name] == inst && hash[:path] == '/etc/init.d'}.returns("#{inst}_instance") end results = (@services-@not_services).collect {|x| "#{x}_instance"} @class.instances.should == results end it "should call service status when initialized from provider" do @resource.stubs(:[]).with(:status).returns nil @provider.stubs(:get).with(:hasstatus).returns true @provider.expects(:execute).with{|command, *args| command == ['/sbin/service', 'myservice', 'status']} @provider.send(:status) end end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end describe "when running #{method}" do it "should use any provided explicit command" do @resource.stubs(:[]).with(method).returns "/user/specified/command" @provider.expects(:execute).with { |command, *args| command == ["/user/specified/command"] } @provider.send(method) end it "should execute the service script with #{method} when no explicit command is provided" do @resource.stubs(:[]).with("has#{method}".intern).returns :true @provider.expects(:execute).with { |command, *args| command == ['/sbin/service', 'myservice', method.to_s]} @provider.send(method) end end end describe "when checking status" do describe "when hasstatus is :true" do before :each do @resource.stubs(:[]).with(:hasstatus).returns :true end it "should execute the service script with fail_on_failure false" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) @provider.status end it "should consider the process running if the command returns 0" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) $CHILD_STATUS.stubs(:exitstatus).returns(0) @provider.status.should == :running end [-10,-1,1,10].each { |ec| it "should consider the process stopped if the command returns something non-0" do @provider.expects(:texecute).with(:status, ['/sbin/service', 'myservice', 'status'], false) $CHILD_STATUS.stubs(:exitstatus).returns(ec) @provider.status.should == :stopped end } end describe "when hasstatus is not :true" do it "should consider the service :running if it has a pid" do @provider.expects(:getpid).returns "1234" @provider.status.should == :running end it "should consider the service :stopped if it doesn't have a pid" do @provider.expects(:getpid).returns nil @provider.status.should == :stopped end end end describe "when restarting and hasrestart is not :true" do it "should stop and restart the process with the server script" do @provider.expects(:texecute).with(:stop, ['/sbin/service', 'myservice', 'stop'], true) @provider.expects(:texecute).with(:start, ['/sbin/service', 'myservice', 'start'], true) @provider.restart end end end diff --git a/spec/unit/provider/service/runit_spec.rb b/spec/unit/provider/service/runit_spec.rb index 38855a451..1b0848b5b 100755 --- a/spec/unit/provider/service/runit_spec.rb +++ b/spec/unit/provider/service/runit_spec.rb @@ -1,140 +1,140 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the Runit service Provider # # author Brice Figureau # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:runit) describe provider_class do before(:each) do # Create a mock resource @resource = stub 'resource' @provider = provider_class.new @servicedir = "/etc/service" @provider.servicedir=@servicedir @daemondir = "/etc/sv" @provider.class.defpath=@daemondir # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, source and path (because we won't run # the thing that will fetch the resource path from the provider) @resource.stubs(:[]).with(:name).returns "myservice" @resource.stubs(:[]).with(:ensure).returns :enabled @resource.stubs(:[]).with(:path).returns @daemondir @resource.stubs(:ref).returns "Service[myservice]" @provider.stubs(:sv) @provider.stubs(:resource).returns @resource end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a restartcmd method" do @provider.should respond_to(:restartcmd) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when starting" do it "should enable the service if it is not enabled" do @provider.stubs(:sv) @provider.expects(:enabled?).returns :false @provider.expects(:enable) @provider.start end it "should execute external command 'sv start /etc/service/myservice'" do @provider.stubs(:enabled?).returns :true @provider.expects(:sv).with("start", "/etc/service/myservice") @provider.start end end describe "when stopping" do it "should execute external command 'sv stop /etc/service/myservice'" do @provider.expects(:sv).with("stop", "/etc/service/myservice") @provider.stop end end describe "when restarting" do it "should call 'sv restart /etc/service/myservice'" do @provider.expects(:sv).with("restart","/etc/service/myservice") @provider.restart end end describe "when enabling" do it "should create a symlink between daemon dir and service dir" do FileTest.stubs(:symlink?).returns(false) File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0) @provider.enable end end describe "when disabling" do it "should remove the '/etc/service/myservice' symlink" do FileTest.stubs(:directory?).returns(false) FileTest.stubs(:symlink?).returns(true) File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0) @provider.disable end end describe "when checking status" do it "should call the external command 'sv status /etc/sv/myservice'" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")) @provider.status end end describe "when checking status" do it "and sv status fails, properly raise a Puppet::Error" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).raises(Puppet::ExecutionFailure, "fail: /etc/sv/myservice: file not found") lambda { @provider.status }.should raise_error(Puppet::Error, 'Could not get status for service Service[myservice]: fail: /etc/sv/myservice: file not found') end it "and sv status returns up, then return :running" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("run: /etc/sv/myservice: (pid 9029) 6s") @provider.status.should == :running end it "and sv status returns not running, then return :stopped" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("fail: /etc/sv/myservice: runsv not running") @provider.status.should == :stopped end it "and sv status returns a warning, then return :stopped" do @provider.expects(:sv).with('status',File.join(@daemondir,"myservice")).returns("warning: /etc/sv/myservice: unable to open supervise/ok: file does not exist") @provider.status.should == :stopped end end end diff --git a/spec/unit/provider/service/smf_spec.rb b/spec/unit/provider/service/smf_spec.rb index 2a33ed958..d5f3e4d69 100755 --- a/spec/unit/provider/service/smf_spec.rb +++ b/spec/unit/provider/service/smf_spec.rb @@ -1,138 +1,138 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the SMF service Provider # # author Dominic Cleal # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:smf) describe provider_class, :as_platform => :posix do before(:each) do # Create a mock resource @resource = Puppet::Type.type(:service).new( :name => "/system/myservice", :ensure => :running, :enable => :true) @provider = provider_class.new(@resource) FileTest.stubs(:file?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:executable?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:file?).with('/usr/bin/svcs').returns true FileTest.stubs(:executable?).with('/usr/bin/svcs').returns true end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a restartcmd method" do @provider.should respond_to(:restartcmd) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when checking status" do it "should call the external command 'svcs /system/myservice' once" do @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.status end it "should return stopped if svcs can't find the service" do @provider.stubs(:svcs).raises(Puppet::ExecutionFailure.new("no svc found")) @provider.status.should == :stopped end it "should return running if online in svcs output" do @provider.stubs(:svcs).returns("online\t-") @provider.status.should == :running end it "should return stopped if disabled in svcs output" do @provider.stubs(:svcs).returns("disabled\t-") @provider.status.should == :stopped end it "should return maintenance if in maintenance in svcs output" do @provider.stubs(:svcs).returns("maintenance\t-") @provider.status.should == :maintenance end it "should return target state if transitioning in svcs output" do @provider.stubs(:svcs).returns("online\tdisabled") @provider.status.should == :stopped end it "should throw error if it's a legacy service in svcs output" do @provider.stubs(:svcs).returns("legacy_run\t-") lambda { @provider.status }.should raise_error(Puppet::Error, "Cannot manage legacy services through SMF") end end describe "when starting" do it "should enable the service if it is not enabled" do @provider.expects(:status).returns :stopped @provider.expects(:texecute) @provider.start end it "should always execute external command 'svcadm enable /system/myservice'" do @provider.stubs(:status).returns :running @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "-s", "/system/myservice"], true) @provider.start end it "should execute external command 'svcadm clear /system/myservice' if in maintenance" do @provider.stubs(:status).returns :maintenance @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :clear, "/system/myservice"], true) @provider.start end end describe "when starting a service with a manifest" do before(:each) do @resource = Puppet::Type.type(:service).new(:name => "/system/myservice", :ensure => :running, :enable => :true, :manifest => "/tmp/myservice.xml") @provider = provider_class.new(@resource) $CHILD_STATUS.stubs(:exitstatus).returns(1) end it "should import the manifest if service is missing" do @provider.expects(:svccfg).with(:import, "/tmp/myservice.xml") @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "-s", "/system/myservice"], true) @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.start end it "should handle failures if importing a manifest" do @provider.expects(:svccfg).raises(Puppet::ExecutionFailure.new("can't svccfg import")) lambda { @provider.start }.should raise_error(Puppet::Error, "Cannot config /system/myservice to enable it: can't svccfg import") end end describe "when stopping" do it "should execute external command 'svcadm disable /system/myservice'" do @provider.expects(:texecute).with(:stop, ["/usr/sbin/svcadm", :disable, "-s", "/system/myservice"], true) @provider.stop end end describe "when restarting" do it "should call 'svcadm restart /system/myservice'" do @provider.expects(:texecute).with(:restart, ["/usr/sbin/svcadm", :restart, "/system/myservice"], true) @provider.restart end end end diff --git a/spec/unit/provider/service/src_spec.rb b/spec/unit/provider/service/src_spec.rb index 17f49994e..fe935a58b 100755 --- a/spec/unit/provider/service/src_spec.rb +++ b/spec/unit/provider/service/src_spec.rb @@ -1,97 +1,97 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the AIX System Resource Controller (src) provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:src) describe provider_class do before :each do @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice" @provider = provider_class.new @provider.resource = @resource @provider.stubs(:command).with(:stopsrc).returns "/usr/bin/stopsrc" @provider.stubs(:command).with(:startsrc).returns "/usr/bin/startsrc" @provider.stubs(:command).with(:lssrc).returns "/usr/bin/lssrc" @provider.stubs(:command).with(:refresh).returns "/usr/bin/refresh" @provider.stubs(:stopsrc) @provider.stubs(:startsrc) @provider.stubs(:lssrc) @provider.stubs(:refresh) end [:start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end end it "should execute the startsrc command" do @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.start end it "should execute the stopsrc command" do @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.stop end it "should execute status and return running if the subsystem is active" do sample_output = <<_EOF_ Subsystem Group PID Status myservice tcpip 1234 active _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output @provider.status.should == :running end it "should execute status and return stopped if the subsystem is inoperative" do sample_output = <<_EOF_ Subsystem Group PID Status myservice tcpip inoperative _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output @provider.status.should == :stopped end it "should execute status and return nil if the status is not known" do sample_output = <<_EOF_ Subsystem Group PID Status myservice tcpip randomdata _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-s', "myservice"]).returns sample_output @provider.status.should == nil end it "should execute restart which runs refresh" do sample_output = <<_EOF_ #subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: myservice:::/usr/sbin/inetd:0:0:/dev/console:/dev/console:/dev/console:-O:-Q:-K:0:0:20:0:0:-d:20:tcpip: _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output @provider.expects(:execute).with(['/usr/bin/refresh', '-s', "myservice"]) @provider.restart end it "should execute restart which runs stopsrc then startsrc" do sample_output = <<_EOF_ #subsysname:synonym:cmdargs:path:uid:auditid:standin:standout:standerr:action:multi:contact:svrkey:svrmtype:priority:signorm:sigforce:display:waittime:grpname: myservice::--no-daemonize:/usr/sbin/puppetd:0:0:/dev/null:/var/log/puppet.log:/var/log/puppet.log:-O:-Q:-S:0:0:20:15:9:-d:20::" _EOF_ @provider.expects(:execute).with(['/usr/bin/lssrc', '-Ss', "myservice"]).returns sample_output @provider.expects(:execute).with(['/usr/bin/stopsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.expects(:execute).with(['/usr/bin/startsrc', '-s', "myservice"], {:squelch => true, :failonfail => true}) @provider.restart end end diff --git a/spec/unit/provider/service/systemd_spec.rb b/spec/unit/provider/service/systemd_spec.rb index 184b4ad58..ce2d6371e 100755 --- a/spec/unit/provider/service/systemd_spec.rb +++ b/spec/unit/provider/service/systemd_spec.rb @@ -1,42 +1,42 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the RedHat service Provider # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:systemd) describe provider_class do before :each do @class = Puppet::Type.type(:service).provider(:redhat) @resource = stub 'resource' @resource.stubs(:[]).returns(nil) @resource.stubs(:[]).with(:name).returns "myservice.service" @provider = provider_class.new @resource.stubs(:provider).returns @provider @provider.resource = @resource end osfamily = [ 'redhat', 'suse' ] osfamily.each do |osfamily| it "should be the default provider on #{osfamily}" do pending "This test is pending the change in RedHat-related Linuxes to systemd for service management" end end [:enabled?, :enable, :disable, :start, :stop, :status, :restart].each do |method| it "should have a #{method} method" do @provider.should respond_to(method) end end it 'should return resources from self.instances' do provider_class.expects(:systemctl).with('list-units', '--full', '--all', '--no-pager').returns( "my_service loaded active running\nmy_other_service loaded active running" ) provider_class.instances.map {|provider| provider.name}.should =~ ["my_service","my_other_service"] end end diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb index 02a7a36e4..1460ba16f 100755 --- a/spec/unit/provider/service/upstart_spec.rb +++ b/spec/unit/provider/service/upstart_spec.rb @@ -1,514 +1,514 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:service).provider(:upstart) do let(:manual) { "\nmanual" } let(:start_on_default_runlevels) { "\nstart on runlevel [2,3,4,5]" } let(:provider_class) { Puppet::Type.type(:service).provider(:upstart) } def given_contents_of(file, content) File.open(file, 'w') do |file| file.write(content) end end def then_contents_of(file) File.open(file).read end def lists_processes_as(output) Puppet::Util::Execution.stubs(:execpipe).with("/sbin/initctl list").yields(output) provider_class.stubs(:which).with("/sbin/initctl").returns("/sbin/initctl") end describe "#instances" do it "should be able to find all instances" do lists_processes_as("rc stop/waiting\nssh start/running, process 712") provider_class.instances.map {|provider| provider.name}.should =~ ["rc","ssh"] end it "should attach the interface name for network interfaces" do lists_processes_as("network-interface (eth0)") provider_class.instances.first.name.should == "network-interface INTERFACE=eth0" end it "should attach the job name for network interface security" do processes = "network-interface-security (network-interface/eth0)" provider_class.stubs(:execpipe).yields(processes) provider_class.instances.first.name.should == "network-interface-security JOB=network-interface/eth0" end it "should not find excluded services" do processes = "wait-for-state stop/waiting" provider_class.stubs(:execpipe).yields(processes) provider_class.instances.should be_empty end end describe "#search" do it "searches through paths to find a matching conf file" do File.stubs(:directory?).returns(true) File.stubs(:exists?).returns(false) File.expects(:exists?).with("/etc/init/foo-bar.conf").returns(true) resource = Puppet::Type.type(:service).new(:name => "foo-bar", :provider => :upstart) provider = provider_class.new(resource) provider.initscript.should == "/etc/init/foo-bar.conf" end it "searches for just the name of a compound named service" do File.stubs(:directory?).returns(true) File.stubs(:exists?).returns(false) File.expects(:exists?).with("/etc/init/network-interface.conf").returns(true) resource = Puppet::Type.type(:service).new(:name => "network-interface INTERFACE=lo", :provider => :upstart) provider = provider_class.new(resource) provider.initscript.should == "/etc/init/network-interface.conf" end end describe "#status" do it "should use the default status command if none is specified" do resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) provider = provider_class.new(resource) provider.stubs(:is_upstart?).returns(true) provider.expects(:status_exec).with(["foo"]).returns("foo start/running, process 1000") Process::Status.any_instance.stubs(:exitstatus).returns(0) provider.status.should == :running end it "should properly handle services with 'start' in their name" do resource = Puppet::Type.type(:service).new(:name => "foostartbar", :provider => :upstart) provider = provider_class.new(resource) provider.stubs(:is_upstart?).returns(true) provider.expects(:status_exec).with(["foostartbar"]).returns("foostartbar stop/waiting") Process::Status.any_instance.stubs(:exitstatus).returns(0) provider.status.should == :stopped end end describe "inheritance" do let :resource do resource = Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) end let :provider do provider = provider_class.new(resource) end describe "when upstart job" do before(:each) do provider.stubs(:is_upstart?).returns(true) end ["start", "stop"].each do |command| it "should return the #{command}cmd of its parent provider" do provider.send("#{command}cmd".to_sym).should == [provider.command(command.to_sym), resource.name] end end it "should return nil for the statuscmd" do provider.statuscmd.should be_nil end end end describe "should be enableable" do let :resource do Puppet::Type.type(:service).new(:name => "foo", :provider => :upstart) end let :provider do provider_class.new(resource) end let :init_script do PuppetSpec::Files.tmpfile("foo.conf") end let :over_script do PuppetSpec::Files.tmpfile("foo.override") end let :disabled_content do "\t # \t start on\nother file stuff" end let :multiline_disabled do "# \t start on other file stuff (\n" + "# more stuff ( # )))))inline comment\n" + "# finishing up )\n" + "# and done )\n" + "this line shouldn't be touched\n" end let :multiline_disabled_bad do "# \t start on other file stuff (\n" + "# more stuff ( # )))))inline comment\n" + "# finishing up )\n" + "# and done )\n" + "# this is a comment i want to be a comment\n" + "this line shouldn't be touched\n" end let :multiline_enabled_bad do " \t start on other file stuff (\n" + " more stuff ( # )))))inline comment\n" + " finishing up )\n" + " and done )\n" + "# this is a comment i want to be a comment\n" + "this line shouldn't be touched\n" end let :multiline_enabled do " \t start on other file stuff (\n" + " more stuff ( # )))))inline comment\n" + " finishing up )\n" + " and done )\n" + "this line shouldn't be touched\n" end let :multiline_enabled_standalone do " \t start on other file stuff (\n" + " more stuff ( # )))))inline comment\n" + " finishing up )\n" + " and done )\n" end let :enabled_content do "\t \t start on\nother file stuff" end let :content do "just some text" end describe "Upstart version < 0.6.7" do before(:each) do provider.stubs(:is_upstart?).returns(true) provider.stubs(:upstart_version).returns("0.6.5") provider.stubs(:search).returns(init_script) end [:enabled?,:enable,:disable].each do |enableable| it "should respond to #{enableable}" do provider.should respond_to(enableable) end end describe "when enabling" do it "should open and uncomment the '#start on' line" do given_contents_of(init_script, disabled_content) provider.enable then_contents_of(init_script).should == enabled_content end it "should add a 'start on' line if none exists" do given_contents_of(init_script, "this is a file") provider.enable then_contents_of(init_script).should == "this is a file" + start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_disabled) provider.enable then_contents_of(init_script).should == multiline_enabled end it "should leave not 'start on' comments alone" do given_contents_of(init_script, multiline_disabled_bad) provider.enable then_contents_of(init_script).should == multiline_enabled_bad end end describe "when disabling" do it "should open and comment the 'start on' line" do given_contents_of(init_script, enabled_content) provider.disable then_contents_of(init_script).should == "#" + enabled_content end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_disabled end end describe "when checking whether it is enabled" do it "should consider 'start on ...' to be enabled" do given_contents_of(init_script, enabled_content) provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do given_contents_of(init_script, disabled_content) provider.enabled?.should == :false end it "should consider no start on line to be disabled" do given_contents_of(init_script, content) provider.enabled?.should == :false end end end describe "Upstart version < 0.9.0" do before(:each) do provider.stubs(:is_upstart?).returns(true) provider.stubs(:upstart_version).returns("0.7.0") provider.stubs(:search).returns(init_script) end [:enabled?,:enable,:disable].each do |enableable| it "should respond to #{enableable}" do provider.should respond_to(enableable) end end describe "when enabling" do it "should open and uncomment the '#start on' line" do given_contents_of(init_script, disabled_content) provider.enable then_contents_of(init_script).should == enabled_content end it "should add a 'start on' line if none exists" do given_contents_of(init_script, "this is a file") provider.enable then_contents_of(init_script).should == "this is a file" + start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_disabled) provider.enable then_contents_of(init_script).should == multiline_enabled end it "should remove manual stanzas" do given_contents_of(init_script, multiline_enabled + manual) provider.enable then_contents_of(init_script).should == multiline_enabled end it "should leave not 'start on' comments alone" do given_contents_of(init_script, multiline_disabled_bad) provider.enable then_contents_of(init_script).should == multiline_enabled_bad end end describe "when disabling" do it "should add a manual stanza" do given_contents_of(init_script, enabled_content) provider.disable then_contents_of(init_script).should == enabled_content + manual end it "should remove manual stanzas before adding new ones" do given_contents_of(init_script, multiline_enabled + manual + "\n" + multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_enabled + "\n" + multiline_enabled + manual end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_enabled + manual end end describe "when checking whether it is enabled" do describe "with no manual stanza" do it "should consider 'start on ...' to be enabled" do given_contents_of(init_script, enabled_content) provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do given_contents_of(init_script, disabled_content) provider.enabled?.should == :false end it "should consider no start on line to be disabled" do given_contents_of(init_script, content) provider.enabled?.should == :false end end describe "with manual stanza" do it "should consider 'start on ...' to be disabled if there is a trailing manual stanza" do given_contents_of(init_script, enabled_content + manual + "\nother stuff") provider.enabled?.should == :false end it "should consider two start on lines with a manual in the middle to be enabled" do given_contents_of(init_script, enabled_content + manual + "\n" + enabled_content) provider.enabled?.should == :true end end end end describe "Upstart version > 0.9.0" do before(:each) do provider.stubs(:is_upstart?).returns(true) provider.stubs(:upstart_version).returns("0.9.5") provider.stubs(:search).returns(init_script) provider.stubs(:overscript).returns(over_script) end [:enabled?,:enable,:disable].each do |enableable| it "should respond to #{enableable}" do provider.should respond_to(enableable) end end describe "when enabling" do it "should add a 'start on' line if none exists" do given_contents_of(init_script, "this is a file") provider.enable then_contents_of(init_script).should == "this is a file" then_contents_of(over_script).should == start_on_default_runlevels end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_disabled) provider.enable then_contents_of(init_script).should == multiline_disabled then_contents_of(over_script).should == start_on_default_runlevels end it "should remove any manual stanzas from the override file" do given_contents_of(over_script, manual) given_contents_of(init_script, enabled_content) provider.enable then_contents_of(init_script).should == enabled_content then_contents_of(over_script).should == "" end it "should copy existing start on from conf file if conf file is disabled" do given_contents_of(init_script, multiline_enabled_standalone + manual) provider.enable then_contents_of(init_script).should == multiline_enabled_standalone + manual then_contents_of(over_script).should == multiline_enabled_standalone end it "should leave not 'start on' comments alone" do given_contents_of(init_script, multiline_disabled_bad) given_contents_of(over_script, "") provider.enable then_contents_of(init_script).should == multiline_disabled_bad then_contents_of(over_script).should == start_on_default_runlevels end end describe "when disabling" do it "should add a manual stanza to the override file" do given_contents_of(init_script, enabled_content) provider.disable then_contents_of(init_script).should == enabled_content then_contents_of(over_script).should == manual end it "should handle multiline 'start on' stanzas" do given_contents_of(init_script, multiline_enabled) provider.disable then_contents_of(init_script).should == multiline_enabled then_contents_of(over_script).should == manual end end describe "when checking whether it is enabled" do describe "with no override file" do it "should consider 'start on ...' to be enabled" do given_contents_of(init_script, enabled_content) provider.enabled?.should == :true end it "should consider '#start on ...' to be disabled" do given_contents_of(init_script, disabled_content) provider.enabled?.should == :false end it "should consider no start on line to be disabled" do given_contents_of(init_script, content) provider.enabled?.should == :false end end describe "with override file" do it "should consider 'start on ...' to be disabled if there is manual in override file" do given_contents_of(init_script, enabled_content) given_contents_of(over_script, manual + "\nother stuff") provider.enabled?.should == :false end it "should consider '#start on ...' to be enabled if there is a start on in the override file" do given_contents_of(init_script, disabled_content) given_contents_of(over_script, "start on stuff") provider.enabled?.should == :true end end end end end end diff --git a/spec/unit/provider/service/windows_spec.rb b/spec/unit/provider/service/windows_spec.rb index 07e4f4d1a..46d081a44 100755 --- a/spec/unit/provider/service/windows_spec.rb +++ b/spec/unit/provider/service/windows_spec.rb @@ -1,166 +1,166 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # # Unit testing for the Windows service Provider # require 'spec_helper' require 'win32/service' if Puppet.features.microsoft_windows? describe Puppet::Type.type(:service).provider(:windows), :if => Puppet.features.microsoft_windows? do before :each do @resource = Puppet::Type.type(:service).new(:name => 'snmptrap', :provider => :windows) @config = Struct::ServiceConfigInfo.new @status = Struct::ServiceStatus.new Win32::Service.stubs(:config_info).with(@resource[:name]).returns(@config) Win32::Service.stubs(:status).with(@resource[:name]).returns(@status) end describe ".instances" do it "should enumerate all services" do list_of_services = ['snmptrap', 'svchost', 'sshd'].map { |s| stub('service', :service_name => s) } Win32::Service.expects(:services).returns(list_of_services) described_class.instances.map(&:name).should =~ ['snmptrap', 'svchost', 'sshd'] end end describe "#start" do it "should call out to the Win32::Service API to start the service" do @config.start_type = Win32::Service.get_start_type(Win32::Service::SERVICE_AUTO_START) Win32::Service.expects(:start).with( @resource[:name] ) @resource.provider.start end it "should handle when Win32::Service.start raises a Win32::Service::Error" do @config.start_type = Win32::Service.get_start_type(Win32::Service::SERVICE_AUTO_START) Win32::Service.expects(:start).with( @resource[:name] ).raises( Win32::Service::Error.new("The service cannot be started, either because it is disabled or because it has no enabled devices associated with it.") ) expect { @resource.provider.start }.to raise_error( Puppet::Error, /Cannot start .*, error was: The service cannot be started, either/ ) end describe "when the service is disabled" do before :each do @config.start_type = Win32::Service.get_start_type(Win32::Service::SERVICE_DISABLED) Win32::Service.stubs(:start).with(@resource[:name]) end it "should refuse to start if not managing enable" do expect { @resource.provider.start }.to raise_error(Puppet::Error, /Will not start disabled service/) end it "should enable if managing enable and enable is true" do @resource[:enable] = :true Win32::Service.expects(:configure).with('service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_AUTO_START).returns(Win32::Service) @resource.provider.start end it "should manual start if managing enable and enable is false" do @resource[:enable] = :false Win32::Service.expects(:configure).with('service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DEMAND_START).returns(Win32::Service) @resource.provider.start end end end describe "#stop" do it "should call out to the Win32::Service API to stop the service" do Win32::Service.expects(:stop).with( @resource[:name] ) @resource.provider.stop end it "should handle when Win32::Service.stop raises a Win32::Service::Error" do Win32::Service.expects(:stop).with( @resource[:name] ).raises( Win32::Service::Error.new("should not try to stop an already stopped service.") ) expect { @resource.provider.stop }.to raise_error( Puppet::Error, /Cannot stop .*, error was: should not try to stop an already stopped service/ ) end end describe "#status" do ['stopped', 'paused', 'stop pending', 'pause pending'].each do |state| it "should report a #{state} service as stopped" do @status.current_state = state @resource.provider.status.should == :stopped end end ["running", "continue pending", "start pending" ].each do |state| it "should report a #{state} service as running" do @status.current_state = state @resource.provider.status.should == :running end end end describe "#enabled?" do it "should report a service with a startup type of manual as manual" do @config.start_type = Win32::Service.get_start_type(Win32::Service::SERVICE_DEMAND_START) @resource.provider.enabled?.should == :manual end it "should report a service with a startup type of disabled as false" do @config.start_type = Win32::Service.get_start_type(Win32::Service::SERVICE_DISABLED) @resource.provider.enabled?.should == :false end # We need to guard this section explicitly since rspec will always # construct all examples, even if it isn't going to run them. if Puppet.features.microsoft_windows? [Win32::Service::SERVICE_AUTO_START, Win32::Service::SERVICE_BOOT_START, Win32::Service::SERVICE_SYSTEM_START].each do |start_type_const| start_type = Win32::Service.get_start_type(start_type_const) it "should report a service with a startup type of '#{start_type}' as true" do @config.start_type = start_type @resource.provider.enabled?.should == :true end end end end describe "#enable" do it "should set service start type to Service_Auto_Start when enabled" do Win32::Service.expects(:configure).with('service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_AUTO_START).returns(Win32::Service) @resource.provider.enable end end describe "#disable" do it "should set service start type to Service_Disabled when disabled" do Win32::Service.expects(:configure).with('service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DISABLED).returns(Win32::Service) @resource.provider.disable end end describe "#manual_start" do it "should set service start type to Service_Demand_Start (manual) when manual" do Win32::Service.expects(:configure).with('service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DEMAND_START).returns(Win32::Service) @resource.provider.manual_start end end end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index 1b4576492..fc4c8f560 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -1,257 +1,257 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' require 'puppet_spec/files' provider_class = Puppet::Type.type(:ssh_authorized_key).provider(:parsed) describe provider_class, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before :each do @keyfile = tmpfile('authorized_keys') @provider_class = provider_class @provider_class.initvars @provider_class.any_instance.stubs(:target).returns @keyfile @user = 'random_bob' Puppet::Util.stubs(:uid).with(@user).returns 12345 end def mkkey(args) args[:target] = @keyfile args[:user] = @user resource = Puppet::Type.type(:ssh_authorized_key).new(args) key = @provider_class.new(resource) args.each do |p,v| key.send(p.to_s + "=", v) end key end def genkey(key) @provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields key.flush @provider_class.target_object(@keyfile).read end it_should_behave_like "all parsedfile providers", provider_class it "should be able to generate a basic authorized_keys file" do key = mkkey(:name => "Just_Testing", :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", :type => "ssh-dss", :ensure => :present, :options => [:absent] ) genkey(key).should == "ssh-dss AAAAfsfddsjldjgksdflgkjsfdlgkj Just_Testing\n" end it "should be able to generate a authorized_keys file with options" do key = mkkey(:name => "root@localhost", :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", :type => "ssh-rsa", :ensure => :present, :options => ['from="192.168.1.1"', "no-pty", "no-X11-forwarding"] ) genkey(key).should == "from=\"192.168.1.1\",no-pty,no-X11-forwarding ssh-rsa AAAAfsfddsjldjgksdflgkjsfdlgkj root@localhost\n" end it "should parse comments" do result = [{ :record_type => :comment, :line => "# hello" }] @provider_class.parse("# hello\n").should == result end it "should parse comments with leading whitespace" do result = [{ :record_type => :comment, :line => " # hello" }] @provider_class.parse(" # hello\n").should == result end it "should skip over lines with only whitespace" do result = [{ :record_type => :comment, :line => "#before" }, { :record_type => :blank, :line => " " }, { :record_type => :comment, :line => "#after" }] @provider_class.parse("#before\n \n#after\n").should == result end it "should skip over completely empty lines" do result = [{ :record_type => :comment, :line => "#before"}, { :record_type => :blank, :line => ""}, { :record_type => :comment, :line => "#after"}] @provider_class.parse("#before\n\n#after\n").should == result end it "should be able to parse name if it includes whitespace" do @provider_class.parse_line('ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7pHZ1XRj3tXbFpPFhMGU1bVwz7jr13zt/wuE+pVIJA8GlmHYuYtIxHPfDHlkixdwLachCpSQUL9NbYkkRFRn9m6PZ7125ohE4E4m96QS6SGSQowTiRn4Lzd9LV38g93EMHjPmEkdSq7MY4uJEd6DUYsLvaDYdIgBiLBIWPA3OrQ== fancy user')[:name].should == 'fancy user' @provider_class.parse_line('from="host1.reductlivelabs.com,host.reductivelabs.com",command="/usr/local/bin/run",ssh-pty ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7pHZ1XRj3tXbFpPFhMGU1bVwz7jr13zt/wuE+pVIJA8GlmHYuYtIxHPfDHlkixdwLachCpSQUL9NbYkkRFRn9m6PZ7125ohE4E4m96QS6SGSQowTiRn4Lzd9LV38g93EMHjPmEkdSq7MY4uJEd6DUYsLvaDYdIgBiLBIWPA3OrQ== fancy user')[:name].should == 'fancy user' end it "should be able to parse options containing commas via its parse_options method" do options = %w{from="host1.reductlivelabs.com,host.reductivelabs.com" command="/usr/local/bin/run" ssh-pty} optionstr = options.join(", ") @provider_class.parse_options(optionstr).should == options end it "should use '' as name for entries that lack a comment" do line = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAut8aOSxenjOqF527dlsdHWV4MNoAsX14l9M297+SQXaQ5Z3BedIxZaoQthkDALlV/25A1COELrg9J2MqJNQc8Xe9XQOIkBQWWinUlD/BXwoOTWEy8C8zSZPHZ3getMMNhGTBO+q/O+qiJx3y5cA4MTbw2zSxukfWC87qWwcZ64UUlegIM056vPsdZWFclS9hsROVEa57YUMrehQ1EGxT4Z5j6zIopufGFiAPjZigq/vqgcAqhAKP6yu4/gwO6S9tatBeEjZ8fafvj1pmvvIplZeMr96gHE7xS3pEEQqnB3nd4RY7AF6j9kFixnsytAUO7STPh/M3pLiVQBN89TvWPQ==" @provider_class.parse(line)[0][:name].should == "" end ['ssh-dss', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521'].each do |keytype| it "should be able to parse a #{keytype} key entry" do # use some real world examples generated with ssh-keygen key = case keytype when 'ssh-dss' # ssh-keygen -t dsa -b 1024 'AAAAB3NzaC1kc3MAAACBANGTefWMXS780qLMMgysq3GNMKzg55LXZODif6Tqv1vtTh4Wuk3J5X5u644jTyNdAIn1RiBI9MnwnZMZ6nXKvucMcMQWMibYS9W2MhkRj3oqsLWMMsdGXJL18SWM5A6oC3oIRC4JHJZtkm0OctR2trKxmX+MGhdCd+Xpsh9CNK8XAAAAFQD4olFiwv+QQUFdaZbWUy1CLEG9xQAAAIByCkXKgoriZF8bQ0OX1sKuR69M/6n5ngmQGVBKB7BQkpUjbK/OggB6iJgst5utKkDcaqYRnrTYG9q3jJ/flv7yYePuoSreS0nCMMx9gpEYuq+7Sljg9IecmN/IHrNd9qdYoASy5iuROQMvEZM7KFHA8vBv0tWdBOsp4hZKyiL1DAAAAIEAjkZlOps9L+cD/MTzxDj7toYYypdLOvjlcPBaglkPZoFZ0MAKTI0zXlVX1cWAnkd0Yfo4EpP+6XAjlZkod+QXKXM4Tb4PnR34ASMeU6sEjM61Na24S7JD3gpPKataFU/oH3hzXsBdK2ttKYmoqvf61h32IA/3Z5PjCCD9pPLPpAY' when 'ssh-rsa' # ssh-keygen -t rsa -b 2048 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDYtEaWa1mlxaAh9vtiz6RCVKDiJHDY15nsqqWU7F7A1+U1498+sWDyRDkZ8vXWQpzyOMBzBSHIxhsprlKhkjomy8BuJP+bHDBIKx4zgSFDrklrPIf467Iuug8J0qqDLxO4rOOjeAiLEyC0t2ZGnsTEea+rmat0bJ2cv3g5L4gH/OFz2pI4ZLp1HGN83ipl5UH8CjXQKwo3Db1E3WJCqKgszVX0Z4/qjnBRxFMoqky/1mGb/mX1eoT9JyQ8OhU9uENZOShkksSpgUqjlrjpj0Yd14hBlnE3M18pE4ivxjzectA/XRKNZaxOL1YREtU8sXusAwmlEY4aJ64aR0JrXfgx' when 'ecdsa-sha2-nistp256' # ssh-keygen -t ecdsa -b 256 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBO5PfBf0c2jAuqD+Lj3j+SuXOXNT2uqESLVOn5jVQfEF9GzllOw+CMOpUvV1CiOOn+F1ET15vcsfmD7z05WUTA=' when 'ecdsa-sha2-nistp384' # ssh-keygen -t ecdsa -b 384 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJIfxNoVK4FX3RuMlkHOwwxXwAh6Fqx5uAp4ftXrJ+64qYuIzb+/zSAkJV698Sre1b1lb0G4LyDdVAvXwaYK9kN25vy8umV3WdfZeHKXJGCcrplMCbbOERWARlpiPNEblg==' when 'ecdsa-sha2-nistp521' #ssh-keygen -t ecdsa -b 521 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADLK+u12xwB0JOwpmaxYXv8KnPK4p+SE2405qoo+vpAQ569fMwPMgKzltd770amdeuFogw/MJu17PN9LDdrD3o0uwHMjWee6TpHQDkuEetaxiou6K0WAzgbxx9QsY0MsJgXf1BuMLqdK+xT183wOSXwwumv99G7T32dOJZ5tYrH0y4XMw==' else pending("No sample key for #{keytype} yet") end comment = 'sample_key' record = @provider_class.parse_line("#{keytype} #{key} #{comment}") record.should_not be_nil record[:name].should == comment record[:key].should == key record[:type].should == keytype end end end describe provider_class, :unless => Puppet.features.microsoft_windows? do before :each do @resource = Puppet::Type.type(:ssh_authorized_key).new(:name => "foo", :user => "random_bob") @provider = provider_class.new(@resource) provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) Puppet::Util::SUIDManager.stubs(:asuser).yields provider_class.initvars end describe "when flushing" do before :each do # Stub file and directory operations Dir.stubs(:mkdir) File.stubs(:chmod) File.stubs(:chown) end describe "and both a user and a target have been specified" do before :each do Puppet::Util.stubs(:uid).with("random_bob").returns 12345 @resource[:user] = "random_bob" target = "/tmp/.ssh_dir/place_to_put_authorized_keys" @resource[:target] = target end it "should create the directory" do File.stubs(:exist?).with("/tmp/.ssh_dir").returns false Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700) @provider.flush end it "should absolutely not chown the directory to the user" do uid = Puppet::Util.uid("random_bob") File.expects(:chown).never @provider.flush end it "should absolutely not chown the key file to the user" do uid = Puppet::Util.uid("random_bob") File.expects(:chown).never @provider.flush end it "should chmod the key file to 0600" do File.expects(:chmod).with(0600, "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider.flush end end describe "and a user has been specified with no target" do before :each do @resource[:user] = "nobody" # # I'd like to use random_bob here and something like # # File.stubs(:expand_path).with("~random_bob/.ssh").returns "/users/r/random_bob/.ssh" # # but mocha objects strenuously to stubbing File.expand_path # so I'm left with using nobody. @dir = File.expand_path("~nobody/.ssh") end it "should create the directory if it doesn't exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).with(@dir,0700) @provider.flush end it "should not create or chown the directory if it already exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never @provider.flush end it "should absolutely not chown the directory to the user if it creates it" do File.stubs(:exist?).with(@dir).returns false Dir.stubs(:mkdir).with(@dir,0700) uid = Puppet::Util.uid("nobody") File.expects(:chown).never @provider.flush end it "should not create or chown the directory if it already exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never File.expects(:chown).never @provider.flush end it "should absolutely not chown the key file to the user" do uid = Puppet::Util.uid("nobody") File.expects(:chown).never @provider.flush end it "should chmod the key file to 0600" do File.expects(:chmod).with(0600, File.expand_path("~nobody/.ssh/authorized_keys")) @provider.flush end end describe "and a target has been specified with no user" do it "should raise an error" do @resource = Puppet::Type.type(:ssh_authorized_key).new(:name => "foo", :target => "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider = provider_class.new(@resource) proc { @provider.flush }.should raise_error end end describe "and a invalid user has been specified with no target" do it "should catch an exception and raise a Puppet error" do @resource[:user] = "thisusershouldnotexist" lambda { @provider.flush }.should raise_error(Puppet::Error) end end end end diff --git a/spec/unit/provider/sshkey/parsed_spec.rb b/spec/unit/provider/sshkey/parsed_spec.rb index 1cf91c50d..e751e45db 100755 --- a/spec/unit/provider/sshkey/parsed_spec.rb +++ b/spec/unit/provider/sshkey/parsed_spec.rb @@ -1,67 +1,67 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:sshkey).provider(:parsed) describe "sshkey parsed provider" do let :type do Puppet::Type.type(:sshkey) end let :provider do type.provider(:parsed) end subject { provider } def key 'AAAAB3NzaC1yc2EAAAABIwAAAQEAzwHhxXvIrtfIwrudFqc8yQcIfMudrgpnuh1F3AV6d2BrLgu/yQE7W5UyJMUjfj427sQudRwKW45O0Jsnr33F4mUw+GIMlAAmp9g24/OcrTiB8ZUKIjoPy/cO4coxGi8/NECtRzpD/ZUPFh6OEpyOwJPMb7/EC2Az6Otw4StHdXUYw22zHazBcPFnv6zCgPx1hA7QlQDWTu4YcL0WmTYQCtMUb3FUqrcFtzGDD0ytosgwSd+JyN5vj5UwIABjnNOHPZ62EY1OFixnfqX/+dUwrFSs5tPgBF/KkC6R7tmbUfnBON6RrGEmu+ajOTOLy23qUZB4CQ53V7nyAWhzqSK+hw==' end it "should parse the name from the first field" do subject.parse_line('test ssh-rsa '+key)[:name].should == "test" end it "should parse the first component of the first field as the name" do subject.parse_line('test,alias ssh-rsa '+key)[:name].should == "test" end it "should parse host_aliases from the remaining components of the first field" do subject.parse_line('test,alias ssh-rsa '+key)[:host_aliases].should == ["alias"] end it "should parse multiple host_aliases" do subject.parse_line('test,alias1,alias2,alias3 ssh-rsa '+key)[:host_aliases].should == ["alias1","alias2","alias3"] end it "should not drop an empty host_alias" do subject.parse_line('test,alias, ssh-rsa '+key)[:host_aliases].should == ["alias",""] end it "should recognise when there are no host aliases" do subject.parse_line('test ssh-rsa '+key)[:host_aliases].should == [] end context "with the sample file" do let :fixture do my_fixture('sample') end before :each do subject.stubs(:default_target).returns(fixture) end it "should parse to records on prefetch" do subject.target_records(fixture).should be_empty subject.prefetch records = subject.target_records(fixture) records.should be_an Array records.should be_all {|x| x.should be_an Hash } end it "should reconstitute the file from records" do subject.prefetch records = subject.target_records(fixture) text = subject.to_file(records).gsub(/^# HEADER.+\n/, '') oldlines = File.readlines(fixture).map(&:chomp) newlines = text.chomp.split("\n") oldlines.length.should == newlines.length oldlines.zip(newlines).each do |old, new| old.gsub(/\s+/, '').should == new.gsub(/\s+/, '') end end end end diff --git a/spec/unit/provider/user/hpux_spec.rb b/spec/unit/provider/user/hpux_spec.rb index f7779a98d..9cf4627a1 100755 --- a/spec/unit/provider/user/hpux_spec.rb +++ b/spec/unit/provider/user/hpux_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:hpuxuseradd) describe provider_class do # left from the useradd test... I have no clue what I'm doing. before do @resource = stub("resource", :name => "myuser", :managehome? => nil, :should => "fakeval", :[] => "fakeval") @provider = provider_class.new(@resource) end it "should add -F when modifying a user" do @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-F") } @provider.uid = 1000 end it "should add -F when deleting a user" do @provider.stubs(:exists?).returns(true) @provider.expects(:execute).with { |args| args.include?("-F") } @provider.delete end end diff --git a/spec/unit/provider/user/ldap_spec.rb b/spec/unit/provider/user/ldap_spec.rb index 8888d85ed..16164c3b3 100755 --- a/spec/unit/provider/user/ldap_spec.rb +++ b/spec/unit/provider/user/ldap_spec.rb @@ -1,275 +1,275 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:ldap) describe provider_class do it "should have the Ldap provider class as its baseclass" do provider_class.superclass.should equal(Puppet::Provider::Ldap) end it "should manage :posixAccount and :person objectclasses" do provider_class.manager.objectclasses.should == [:posixAccount, :person] end it "should use 'ou=People' as its relative base" do provider_class.manager.location.should == "ou=People" end it "should use :uid as its rdn" do provider_class.manager.rdn.should == :uid end it "should be able to manage passwords" do provider_class.should be_manages_passwords end it "should use the ldap group provider to convert group names to numbers" do provider = provider_class.new(:name => "foo") Puppet::Type.type(:group).provider(:ldap).expects(:name2id).with("bar").returns 10 provider.gid = 'bar' provider.gid.should == 10 end {:name => "uid", :password => "userPassword", :comment => "cn", :uid => "uidNumber", :gid => "gidNumber", :home => "homeDirectory", :shell => "loginShell" }.each do |puppet, ldap| it "should map :#{puppet.to_s} to '#{ldap}'" do provider_class.manager.ldap_name(puppet).should == ldap end end describe "when being created" do before do # So we don't try to actually talk to ldap @connection = mock 'connection' provider_class.manager.stubs(:connect).yields @connection end it "should generate the sn as the last field of the cn" do resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:comment).returns ["Luke Kanies"] resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["sn"] == ["Kanies"] } instance.create instance.flush end describe "with no uid specified" do it "should pick the first available UID after the largest existing UID" do low = {:name=>["luke"], :shell=>:absent, :uid=>["600"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["l k"]} high = {:name=>["testing"], :shell=>:absent, :uid=>["640"], :home=>["/h"], :gid=>["1000"], :password=>["blah"], :comment=>["t u"]} provider_class.manager.expects(:search).returns([low, high]) resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:uid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["641"] } instance.create instance.flush end it "should pick 501 of no users exist" do provider_class.manager.expects(:search).returns nil resource = stub 'resource', :should => %w{whatever} resource.stubs(:should).with(:uid).returns nil resource.stubs(:should).with(:ensure).returns :present instance = provider_class.new(:name => "luke", :ensure => :absent) instance.stubs(:resource).returns resource @connection.expects(:add).with { |dn, attrs| attrs["uidNumber"] == ["501"] } instance.create instance.flush end end end describe "when flushing" do before do provider_class.stubs(:suitable?).returns true @instance = provider_class.new(:name => "myname", :groups => %w{whatever}, :uid => "400") end it "should remove the :groups value before updating" do @instance.class.manager.expects(:update).with { |name, ldap, puppet| puppet[:groups].nil? } @instance.flush end it "should empty the property hash" do @instance.class.manager.stubs(:update) @instance.flush @instance.uid.should == :absent end it "should empty the ldap property hash" do @instance.class.manager.stubs(:update) @instance.flush @instance.ldap_properties[:uid].should be_nil end end describe "when checking group membership" do before do @groups = Puppet::Type.type(:group).provider(:ldap) @group_manager = @groups.manager provider_class.stubs(:suitable?).returns true @instance = provider_class.new(:name => "myname") end it "should show its group membership as the sorted list of all groups returned by an ldap query of group memberships" do one = {:name => "one"} two = {:name => "two"} @group_manager.expects(:search).with("memberUid=myname").returns([two, one]) @instance.groups.should == "one,two" end it "should show its group membership as :absent if no matching groups are found in ldap" do @group_manager.expects(:search).with("memberUid=myname").returns(nil) @instance.groups.should == :absent end it "should cache the group value" do @group_manager.expects(:search).with("memberUid=myname").once.returns nil @instance.groups @instance.groups.should == :absent end end describe "when modifying group membership" do before do @groups = Puppet::Type.type(:group).provider(:ldap) @group_manager = @groups.manager provider_class.stubs(:suitable?).returns true @one = {:name => "one", :gid => "500"} @group_manager.stubs(:find).with("one").returns(@one) @two = {:name => "one", :gid => "600"} @group_manager.stubs(:find).with("two").returns(@two) @instance = provider_class.new(:name => "myname") @instance.stubs(:groups).returns :absent end it "should fail if the group does not exist" do @group_manager.expects(:find).with("mygroup").returns nil lambda { @instance.groups = "mygroup" }.should raise_error(Puppet::Error) end it "should only pass the attributes it cares about to the group manager" do @group_manager.expects(:update).with { |name, attrs| attrs[:gid].nil? } @instance.groups = "one" end it "should always include :ensure => :present in the current values" do @group_manager.expects(:update).with { |name, is, should| is[:ensure] == :present } @instance.groups = "one" end it "should always include :ensure => :present in the desired values" do @group_manager.expects(:update).with { |name, is, should| should[:ensure] == :present } @instance.groups = "one" end it "should always pass the group's original member list" do @one[:members] = %w{yay ness} @group_manager.expects(:update).with { |name, is, should| is[:members] == %w{yay ness} } @instance.groups = "one" end it "should find the group again when resetting its member list, so it has the full member list" do @group_manager.expects(:find).with("one").returns(@one) @group_manager.stubs(:update) @instance.groups = "one" end describe "for groups that have no members" do it "should create a new members attribute with its value being the user's name" do @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{myname} } @instance.groups = "one" end end describe "for groups it is being removed from" do it "should replace the group's member list with one missing the user's name" do @one[:members] = %w{myname a} @two[:members] = %w{myname b} @group_manager.expects(:update).with { |name, is, should| name == "two" and should[:members] == %w{b} } @instance.stubs(:groups).returns "one,two" @instance.groups = "one" end it "should mark the member list as empty if there are no remaining members" do @one[:members] = %w{myname} @two[:members] = %w{myname b} @group_manager.expects(:update).with { |name, is, should| name == "one" and should[:members] == :absent } @instance.stubs(:groups).returns "one,two" @instance.groups = "two" end end describe "for groups that already have members" do it "should replace each group's member list with a new list including the user's name" do @one[:members] = %w{a b} @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } @two[:members] = %w{b c} @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{b c myname} } @instance.groups = "one,two" end end describe "for groups of which it is a member" do it "should do nothing" do @one[:members] = %w{a b} @group_manager.expects(:update).with { |name, is, should| should[:members] == %w{a b myname} } @two[:members] = %w{c myname} @group_manager.expects(:update).with { |name, *other| name == "two" }.never @instance.stubs(:groups).returns "two" @instance.groups = "one,two" end end end end diff --git a/spec/unit/provider/user/pw_spec.rb b/spec/unit/provider/user/pw_spec.rb index e90989a00..629e37029 100755 --- a/spec/unit/provider/user/pw_spec.rb +++ b/spec/unit/provider/user/pw_spec.rb @@ -1,202 +1,202 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:pw) describe provider_class do let :resource do Puppet::Type.type(:user).new(:name => "testuser", :provider => :pw) end describe "when creating users" do let :provider do prov = resource.provider prov.expects(:exists?).returns nil prov end it "should run pw with no additional flags when no properties are given" do provider.addcmd.must == [provider_class.command(:pw), "useradd", "testuser"] provider.expects(:execute).with([provider_class.command(:pw), "useradd", "testuser"]) provider.create end it "should use -o when allowdupe is enabled" do resource[:allowdupe] = true provider.expects(:execute).with(includes("-o")) provider.create end it "should use -c with the correct argument when the comment property is set" do resource[:comment] = "Testuser Name" provider.expects(:execute).with(all_of(includes("-c"), includes("Testuser Name"))) provider.create end it "should use -e with the correct argument when the expiry property is set" do resource[:expiry] = "2010-02-19" provider.expects(:execute).with(all_of(includes("-e"), includes("19-02-2010"))) provider.create end it "should use -g with the correct argument when the gid property is set" do resource[:gid] = 12345 provider.expects(:execute).with(all_of(includes("-g"), includes(12345))) provider.create end it "should use -G with the correct argument when the groups property is set" do resource[:groups] = "group1" provider.expects(:execute).with(all_of(includes("-G"), includes("group1"))) provider.create end it "should use -G with all the given groups when the groups property is set to an array" do resource[:groups] = ["group1", "group2"] provider.expects(:execute).with(all_of(includes("-G"), includes("group1,group2"))) provider.create end it "should use -d with the correct argument when the home property is set" do resource[:home] = "/home/testuser" provider.expects(:execute).with(all_of(includes("-d"), includes("/home/testuser"))) provider.create end it "should use -m when the managehome property is enabled" do resource[:managehome] = true provider.expects(:execute).with(includes("-m")) provider.create end it "should call the password set function with the correct argument when the password property is set" do resource[:password] = "*" provider.expects(:execute) provider.expects(:password=).with("*") provider.create end it "should use -s with the correct argument when the shell property is set" do resource[:shell] = "/bin/sh" provider.expects(:execute).with(all_of(includes("-s"), includes("/bin/sh"))) provider.create end it "should use -u with the correct argument when the uid property is set" do resource[:uid] = 12345 provider.expects(:execute).with(all_of(includes("-u"), includes(12345))) provider.create end # (#7500) -p should not be used to set a password (it means something else) it "should not use -p when a password is given" do resource[:password] = "*" provider.addcmd.should_not include("-p") provider.expects(:password=) provider.expects(:execute).with(Not(includes("-p"))) provider.create end end describe "when deleting users" do it "should run pw with no additional flags" do provider = resource.provider provider.expects(:exists?).returns true provider.deletecmd.must == [provider_class.command(:pw), "userdel", "testuser"] provider.expects(:execute).with([provider_class.command(:pw), "userdel", "testuser"]) provider.delete end # The above test covers this, but given the consequences of # accidently deleting a user's home directory it seems better to # have an explicit test. it "should not use -r when managehome is not set" do provider = resource.provider provider.expects(:exists?).returns true resource[:managehome] = false provider.expects(:execute).with(Not(includes("-r"))) provider.delete end it "should use -r when managehome is set" do provider = resource.provider provider.expects(:exists?).returns true resource[:managehome] = true provider.expects(:execute).with(includes("-r")) provider.delete end end describe "when modifying users" do let :provider do resource.provider end it "should run pw with the correct arguments" do provider.modifycmd("uid", 12345).must == [provider_class.command(:pw), "usermod", "testuser", "-u", 12345] provider.expects(:execute).with([provider_class.command(:pw), "usermod", "testuser", "-u", 12345]) provider.uid = 12345 end it "should use -c with the correct argument when the comment property is changed" do resource[:comment] = "Testuser Name" provider.expects(:execute).with(all_of(includes("-c"), includes("Testuser New Name"))) provider.comment = "Testuser New Name" end it "should use -e with the correct argument when the expiry property is changed" do resource[:expiry] = "2010-02-19" provider.expects(:execute).with(all_of(includes("-e"), includes("19-02-2011"))) provider.expiry = "2011-02-19" end it "should use -g with the correct argument when the gid property is changed" do resource[:gid] = 12345 provider.expects(:execute).with(all_of(includes("-g"), includes(54321))) provider.gid = 54321 end it "should use -G with the correct argument when the groups property is changed" do resource[:groups] = "group1" provider.expects(:execute).with(all_of(includes("-G"), includes("group2"))) provider.groups = "group2" end it "should use -G with all the given groups when the groups property is changed with an array" do resource[:groups] = ["group1", "group2"] provider.expects(:execute).with(all_of(includes("-G"), includes("group3,group4"))) provider.groups = "group3,group4" end it "should use -d with the correct argument when the home property is changed" do resource[:home] = "/home/testuser" provider.expects(:execute).with(all_of(includes("-d"), includes("/newhome/testuser"))) provider.home = "/newhome/testuser" end it "should use -m and -d with the correct argument when the home property is changed and managehome is enabled" do resource[:home] = "/home/testuser" resource[:managehome] = true provider.expects(:execute).with(all_of(includes("-d"), includes("/newhome/testuser"), includes("-m"))) provider.home = "/newhome/testuser" end it "should call the password set function with the correct argument when the password property is changed" do resource[:password] = "*" provider.expects(:password=).with("!") provider.password = "!" end it "should use -s with the correct argument when the shell property is changed" do resource[:shell] = "/bin/sh" provider.expects(:execute).with(all_of(includes("-s"), includes("/bin/tcsh"))) provider.shell = "/bin/tcsh" end it "should use -u with the correct argument when the uid property is changed" do resource[:uid] = 12345 provider.expects(:execute).with(all_of(includes("-u"), includes(54321))) provider.uid = 54321 end end end diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index 724fc12c0..7c4ee3dfb 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -1,215 +1,215 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:useradd) describe provider_class do before do @resource = stub("resource", :name => "myuser", :managehome? => nil) @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @provider = provider_class.new(@resource) end # #1360 it "should add -o when allowdupe is enabled and the user is being created" do @resource.expects(:allowdupe?).returns true @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } @provider.create end it "should add -o when allowdupe is enabled and the uid is being modified" do @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.uid = 150 end it "should add -r when system is enabled" do @resource.expects(:allowdupe?).returns true @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-r") } @provider.create end it "should set password age rules" do provider_class.has_feature :manages_password_age @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :useradd @provider = provider_class.new(@resource) @provider.stubs(:execute) @provider.expects(:execute).with { |cmd, *args| args == ["-m", 5, "-M", 10, "myuser"] } @provider.create end describe "when checking to add allow dup" do it "should check allow dup" do @resource.expects(:allowdupe?) @provider.check_allow_dup end it "should return an array with a flag if dup is allowed" do @resource.stubs(:allowdupe?).returns true @provider.check_allow_dup.must == ["-o"] end it "should return an empty array if no dup is allowed" do @resource.stubs(:allowdupe?).returns false @provider.check_allow_dup.must == [] end end describe "when checking to add system users" do it "should check system users" do @resource.expects(:system?) @provider.check_system_users end it "should return an array with a flag if it's a system user" do @resource.stubs(:system?).returns true @provider.check_system_users.must == ["-r"] end it "should return an empty array if it's not a system user" do @resource.stubs(:system?).returns false @provider.check_system_users.must == [] end end describe "when checking manage home" do it "should check manage home" do @resource.expects(:managehome?) @provider.check_manage_home end it "should return an array with -m flag if home is managed" do @resource.stubs(:managehome?).returns true @provider.check_manage_home.must == ["-m"] end it "should return an array with -M if home is not managed and on Redhat" do Facter.stubs(:value).with("operatingsystem").returns("RedHat") @resource.stubs(:managehome?).returns false @provider.check_manage_home.must == ["-M"] end it "should return an empty array if home is not managed and not on Redhat" do Facter.stubs(:value).with("operatingsystem").returns("some OS") @resource.stubs(:managehome?).returns false @provider.check_manage_home.must == [] end end describe "when adding properties" do it "should get the valid properties" it "should not add the ensure property" it "should add the flag and value to an array" it "should return and array of flags and values" end describe "when calling addcmd" do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true @resource.stubs(:system?).returns true end it "should call command with :add" do @provider.expects(:command).with(:add) @provider.addcmd end it "should add properties" do @provider.expects(:add_properties).returns([]) @provider.addcmd end it "should check and add if dup allowed" do @provider.expects(:check_allow_dup).returns([]) @provider.addcmd end it "should check and add if home is managed" do @provider.expects(:check_manage_home).returns([]) @provider.addcmd end it "should add the resource :name" do @resource.expects(:[]).with(:name) @provider.addcmd end it "should return an array with -r if system? is true" do resource = Puppet::Type.type(:user).new( :name => "bob", :system => true) provider_class.new( resource ).addcmd.should include("-r") end it "should return an array without -r if system? is false" do resource = Puppet::Type.type(:user).new( :name => "bob", :system => false) provider_class.new( resource ).addcmd.should_not include("-r") end it "should return an array with full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns("somedate") @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", '-e somedate', "-r", "someuser"] end it "should return an array without -e if expiry is undefined full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns nil @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "-r", "someuser"] end end describe "when calling passcmd" do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true @resource.stubs(:system?).returns true end it "should call command with :pass" do @provider.expects(:command).with(:password) @provider.passcmd end it "should return nil if neither min nor max is set" do @resource.stubs(:should).with(:password_min_age).returns nil @resource.stubs(:should).with(:password_max_age).returns nil @provider.passcmd.must == nil end it "should return a chage command array with -m and the user name if password_min_age is set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns 123 @resource.stubs(:should).with(:password_max_age).returns nil @provider.passcmd.must == ['chage','-m',123,'someuser'] end it "should return a chage command array with -M if password_max_age is set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns nil @resource.stubs(:should).with(:password_max_age).returns 999 @provider.passcmd.must == ['chage','-M',999,'someuser'] end it "should return a chage command array with -M -m if both password_min_age and password_max_age are set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns 123 @resource.stubs(:should).with(:password_max_age).returns 999 @provider.passcmd.must == ['chage','-m',123,'-M',999,'someuser'] end end end diff --git a/spec/unit/provider/vlan/cisco_spec.rb b/spec/unit/provider/vlan/cisco_spec.rb index 4753cea21..c8731c094 100755 --- a/spec/unit/provider/vlan/cisco_spec.rb +++ b/spec/unit/provider/vlan/cisco_spec.rb @@ -1,55 +1,55 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/provider/vlan/cisco' provider_class = Puppet::Type.type(:vlan).provider(:cisco) describe provider_class do before do @device = stub_everything 'device' @resource = stub("resource", :name => "200") @provider = provider_class.new(@device, @resource) end it "should have a parent of Puppet::Provider::Cisco" do provider_class.should < Puppet::Provider::Cisco end it "should have an instances method" do provider_class.should respond_to(:instances) end describe "when looking up instances at prefetch" do before do @device.stubs(:command).yields(@device) end it "should delegate to the device vlans" do @device.expects(:parse_vlans) provider_class.lookup(@device, "200") end it "should return only the given vlan" do @device.expects(:parse_vlans).returns({"200" => { :description => "thisone" }, "1" => { :description => "nothisone" }}) provider_class.lookup(@device, "200").should == {:description => "thisone" } end end describe "when an instance is being flushed" do it "should call the device update_vlan method with its vlan id, current attributes, and desired attributes" do @instance = provider_class.new(@device, :ensure => :present, :name => "200", :description => "myvlan") @instance.description = "myvlan2" @instance.resource = @resource @resource.stubs(:[]).with(:name).returns("200") device = stub_everything 'device' @instance.stubs(:device).returns(device) device.expects(:command).yields(device) device.expects(:update_vlan).with(@instance.name, {:ensure => :present, :name => "200", :description => "myvlan"}, {:ensure => :present, :name => "200", :description => "myvlan2"}) @instance.flush end end end diff --git a/spec/unit/provider/zfs/solaris_spec.rb b/spec/unit/provider/zfs/solaris_spec.rb index 8a0cd23b1..8676d765c 100755 --- a/spec/unit/provider/zfs/solaris_spec.rb +++ b/spec/unit/provider/zfs/solaris_spec.rb @@ -1,99 +1,99 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:zfs).provider(:solaris) describe provider_class do before do @resource = stub("resource", :name => "myzfs") @resource.stubs(:[]).with(:name).returns "myzfs" @resource.stubs(:[]).returns "shouldvalue" @provider = provider_class.new(@resource) end it "should have a create method" do @provider.should respond_to(:create) end it "should have a destroy method" do @provider.should respond_to(:destroy) end it "should have an exists? method" do @provider.should respond_to(:exists?) end describe "when calling add_properties" do it "should add -o and the key=value for each properties with a value" do @resource.stubs(:[]).with(:quota).returns "" @resource.stubs(:[]).with(:refquota).returns "" @resource.stubs(:[]).with(:mountpoint).returns "/foo" properties = @provider.add_properties properties.include?("-o").should == true properties.include?("mountpoint=/foo").should == true properties.detect { |a| a.include?("quota") }.should == nil end end describe "when calling create" do it "should call add_properties" do @provider.stubs(:zfs) @provider.expects(:add_properties).returns([]) @provider.create end it "should call zfs with create, properties and this zfs" do @provider.stubs(:add_properties).returns(%w{a b}) @provider.expects(:zfs).with(:create, "a", "b", @resource[:name]) @provider.create end end describe "when calling destroy" do it "should call zfs with :destroy and this zfs" do @provider.expects(:zfs).with(:destroy, @resource[:name]) @provider.destroy end end describe "when calling exist?" do it "should call zfs with :list" do #return stuff because we have to slice and dice it @provider.expects(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\nmyzfs 100K 27.4M /myzfs") @provider.exists? end it "should return true if returned values match the name" do @provider.stubs(:zfs).with(:list).returns("NAME USED AVAIL REFER MOUNTPOINT\n#{@resource[:name]} 100K 27.4M /myzfs") @provider.exists?.should == true end it "should return false if returned values don't match the name" do @provider.stubs(:zfs).with(:list).returns("no soup for you") @provider.exists?.should == false end end [:mountpoint, :recordsize, :aclmode, :aclinherit, :primarycache, :secondarycache, :compression, :copies, :quota, :reservation, :sharenfs, :snapdir].each do |prop| describe "when getting the #{prop} value" do it "should call zfs with :get, #{prop} and this zfs" do @provider.expects(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") @provider.send(prop) end it "should get the third value of the second line from the output" do @provider.stubs(:zfs).with(:get, "-H", "-o", "value", prop, @resource[:name]).returns("value\n") @provider.send(prop).should == "value" end end describe "when setting the #{prop} value" do it "should call zfs with :set, #{prop}=value and this zfs" do @provider.expects(:zfs).with(:set, "#{prop}=value", @resource[:name]) @provider.send("#{prop}=".intern, "value") end end end end diff --git a/spec/unit/provider/zone/solaris_spec.rb b/spec/unit/provider/zone/solaris_spec.rb index 17ec8f68f..9c80e32ec 100755 --- a/spec/unit/provider/zone/solaris_spec.rb +++ b/spec/unit/provider/zone/solaris_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:zone).provider(:solaris) describe provider_class do before do @resource = stub("resource", :name => "mypool") @resource.stubs(:[]).returns "shouldvalue" @provider = provider_class.new(@resource) end describe "when calling configure" do it "should add the create args to the create str" do @resource.stubs(:properties).returns([]) @resource.stubs(:[]).with(:create_args).returns("create_args") @provider.expects(:setconfig).with("create -b create_args\nset zonepath=shouldvalue\ncommit\n") @provider.configure end end describe "when installing" do it "should call zoneadm" do @provider.expects(:zoneadm) @provider.install end describe "when cloning" do before { @resource.stubs(:[]).with(:clone).returns(:clone_argument) } it "sohuld clone with the resource's clone attribute" do @provider.expects(:zoneadm).with(:clone, :clone_argument) @provider.install end end describe "when not cloning" do before { @resource.stubs(:[]).with(:clone).returns(nil)} it "should just install if there are no install args" do @resource.stubs(:[]).with(:install_args).returns(nil) @provider.expects(:zoneadm).with(:install) @provider.install end it "should add the install args to the command if they exist" do @resource.stubs(:[]).with(:install_args).returns("install args") @provider.expects(:zoneadm).with(:install, ["install", "args"]) @provider.install end end end end diff --git a/spec/unit/provider/zpool/solaris_spec.rb b/spec/unit/provider/zpool/solaris_spec.rb index 48302253e..69c2b6a40 100755 --- a/spec/unit/provider/zpool/solaris_spec.rb +++ b/spec/unit/provider/zpool/solaris_spec.rb @@ -1,208 +1,208 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' provider_class = Puppet::Type.type(:zpool).provider(:solaris) describe provider_class do before do @resource = stub("resource", :name => "mypool") @resource.stubs(:[]).returns ["shouldvalue"] @provider = provider_class.new(@resource) end describe "when getting the instance" do it "should call process_zpool_data with the result of get_pool_data only once" do @provider.stubs(:get_pool_data).returns(["foo", "disk"]) @provider.expects(:process_zpool_data).with(["foo", "disk"]).returns("stuff").once @provider.current_pool @provider.current_pool end end describe "when calling flush" do it "should need to reload the pool" do @provider.stubs(:get_pool_data) @provider.expects(:process_zpool_data).returns("stuff").times(2) @provider.current_pool @provider.flush @provider.current_pool end end describe "when procesing zpool data" do before do @zpool_data = ["foo", "disk"] end describe "when there is no data" do it "should return a hash with ensure=>:absent" do @provider.process_zpool_data([])[:ensure].should == :absent end end describe "when there is a spare" do it "should add the spare disk to the hash" do @zpool_data += ["spares", "spare_disk"] @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk"] end end describe "when there are two spares" do it "should add the spare disk to the hash as a single string" do @zpool_data += ["spares", "spare_disk", "spare_disk2"] @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] end end describe "when there is a log" do it "should add the log disk to the hash" do @zpool_data += ["logs", "log_disk"] @provider.process_zpool_data(@zpool_data)[:log].should == ["log_disk"] end end describe "when there are two logs" do it "should add the log disks to the hash as a single string" do @zpool_data += ["spares", "spare_disk", "spare_disk2"] @provider.process_zpool_data(@zpool_data)[:spare].should == ["spare_disk spare_disk2"] end end describe "when the vdev is a single mirror" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2"] end end describe "when the vdev is a single mirror on solaris 10u9 or later" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror-0", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2"] end end describe "when the vdev is a double mirror" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror", "disk1", "disk2", "mirror", "disk3", "disk4"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"] end end describe "when the vdev is a double mirror on solaris 10u9 or later" do it "should call create_multi_array with mirror" do @zpool_data = ["mirrorpool", "mirror-0", "disk1", "disk2", "mirror-1", "disk3", "disk4"] @provider.process_zpool_data(@zpool_data)[:mirror].should == ["disk1 disk2", "disk3 disk4"] end end describe "when the vdev is a raidz1" do it "should call create_multi_array with raidz1" do @zpool_data = ["mirrorpool", "raidz1", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"] end end describe "when the vdev is a raidz1 on solaris 10u9 or later" do it "should call create_multi_array with raidz1" do @zpool_data = ["mirrorpool", "raidz1-0", "disk1", "disk2"] @provider.process_zpool_data(@zpool_data)[:raidz].should == ["disk1 disk2"] end end describe "when the vdev is a raidz2" do it "should call create_multi_array with raidz2 and set the raid_parity" do @zpool_data = ["mirrorpool", "raidz2", "disk1", "disk2"] pool = @provider.process_zpool_data(@zpool_data) pool[:raidz].should == ["disk1 disk2"] pool[:raid_parity].should == "raidz2" end end describe "when the vdev is a raidz2 on solaris 10u9 or later" do it "should call create_multi_array with raidz2 and set the raid_parity" do @zpool_data = ["mirrorpool", "raidz2-0", "disk1", "disk2"] pool = @provider.process_zpool_data(@zpool_data) pool[:raidz].should == ["disk1 disk2"] pool[:raid_parity].should == "raidz2" end end end describe "when calling the getters and setters" do [:disk, :mirror, :raidz, :log, :spare].each do |field| describe "when calling #{field}" do it "should get the #{field} value from the current_pool hash" do pool_hash = mock "pool hash" pool_hash.expects(:[]).with(field) @provider.stubs(:current_pool).returns(pool_hash) @provider.send(field) end end describe "when setting the #{field}" do it "should warn the #{field} values were not in sync" do Puppet.expects(:warning).with("NO CHANGES BEING MADE: zpool #{field} does not match, should be 'shouldvalue' currently is 'currentvalue'") @provider.stubs(:current_pool).returns(Hash.new("currentvalue")) @provider.send((field.to_s + "=").intern, "shouldvalue") end end end end describe "when calling create" do before do @resource.stubs(:[]).with(:pool).returns("mypool") @provider.stubs(:zpool) end it "should call build_vdevs" do @provider.expects(:build_vdevs).returns([]) @provider.create end it "should call build_named with 'spares' and 'log" do @provider.expects(:build_named).with("spare").returns([]) @provider.expects(:build_named).with("log").returns([]) @provider.create end it "should call zpool with arguments from build_vdevs and build_named" do @provider.expects(:zpool).with(:create, 'mypool', 'shouldvalue', 'spare', 'shouldvalue', 'log', 'shouldvalue') @provider.create end end describe "when calling delete" do it "should call zpool with destroy and the pool name" do @resource.stubs(:[]).with(:pool).returns("poolname") @provider.expects(:zpool).with(:destroy, "poolname") @provider.delete end end describe "when calling exists?" do before do @current_pool = Hash.new(:absent) @provider.stubs(:get_pool_data).returns([]) @provider.stubs(:process_zpool_data).returns(@current_pool) end it "should get the current pool" do @provider.expects(:process_zpool_data).returns(@current_pool) @provider.exists? end it "should return false if the current_pool is absent" do #the before sets it up @provider.exists?.should == false end it "should return true if the current_pool has values" do @current_pool[:pool] = "mypool" @provider.exists?.should == true end end end diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index e71a6b42b..63b841d0a 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -1,667 +1,667 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' def existing_command Puppet.features.microsoft_windows? ? "cmd" : "echo" end describe Puppet::Provider do before :each do Puppet::Type.newtype(:test) do newparam(:name) { isnamevar } end end after :each do Puppet::Type.rmtype(:test) end let :type do Puppet::Type.type(:test) end let :provider do type.provide(:default) {} end subject { provider } describe "has command" do it "installs a method to run the command specified by the path" do echo_command = expect_command_executed(:echo, "/bin/echo", "an argument") allow_creation_of(echo_command) provider = provider_of do has_command(:echo, "/bin/echo") end provider.echo("an argument") end it "installs a command that is run with a given environment" do echo_command = expect_command_executed(:echo, "/bin/echo", "an argument") allow_creation_of(echo_command, { :EV => "value", :OTHER => "different" }) provider = provider_of do has_command(:echo, "/bin/echo") do environment :EV => "value", :OTHER => "different" end end provider.echo("an argument") end it "is required by default" do provider = provider_of do has_command(:does_not_exist, "/does/not/exist") end provider.should_not be_suitable end it "is required by default" do provider = provider_of do has_command(:does_exist, File.expand_path("/exists/somewhere")) end file_exists_and_is_executable(File.expand_path("/exists/somewhere")) provider.should be_suitable end it "can be specified as optional" do provider = provider_of do has_command(:does_not_exist, "/does/not/exist") do is_optional end end provider.should be_suitable end end describe "has required commands" do it "installs methods to run executables by path" do echo_command = expect_command_executed(:echo, "/bin/echo", "an argument") ls_command = expect_command_executed(:ls, "/bin/ls") allow_creation_of(echo_command) allow_creation_of(ls_command) provider = provider_of do commands :echo => "/bin/echo", :ls => "/bin/ls" end provider.echo("an argument") provider.ls end it "allows the provider to be suitable if the executable is present" do provider = provider_of do commands :always_exists => File.expand_path("/this/command/exists") end file_exists_and_is_executable(File.expand_path("/this/command/exists")) provider.should be_suitable end it "does not allow the provider to be suitable if the executable is not present" do provider = provider_of do commands :does_not_exist => "/this/command/does/not/exist" end provider.should_not be_suitable end end describe "has optional commands" do it "installs methods to run executables" do echo_command = expect_command_executed(:echo, "/bin/echo", "an argument") ls_command = expect_command_executed(:ls, "/bin/ls") allow_creation_of(echo_command) allow_creation_of(ls_command) provider = provider_of do optional_commands :echo => "/bin/echo", :ls => "/bin/ls" end provider.echo("an argument") provider.ls end it "allows the provider to be suitable even if the executable is not present" do provider = provider_of do optional_commands :does_not_exist => "/this/command/does/not/exist" end provider.should be_suitable end end it "makes command methods on demand (deprecated)" do Puppet::Util.expects(:which).with("/not/a/command").returns("/not/a/command") Puppet::Util::Execution.expects(:execute).with(["/not/a/command"], {}) provider = provider_of do @commands[:echo] = "/not/a/command" end provider.stubs(:which).with("/not/a/command").returns("/not/a/command") provider.make_command_methods(:echo) provider.echo end it "should have a specifity class method" do Puppet::Provider.should respond_to(:specificity) end it "should consider two defaults to be higher specificity than one default" do one = provider_of do defaultfor :operatingsystem => "solaris" end two = provider_of do defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" end two.specificity.should > one.specificity end it "should be Comparable" do res = Puppet::Type.type(:notify).new(:name => "res") # Normally I wouldn't like the stubs, but the only way to name a class # otherwise is to assign it to a constant, and that hurts more here in # testing world. --daniel 2012-01-29 a = Class.new(Puppet::Provider).new(res) a.class.stubs(:name).returns "Puppet::Provider::Notify::A" b = Class.new(Puppet::Provider).new(res) b.class.stubs(:name).returns "Puppet::Provider::Notify::B" c = Class.new(Puppet::Provider).new(res) c.class.stubs(:name).returns "Puppet::Provider::Notify::C" [[a, b, c], [a, c, b], [b, a, c], [b, c, a], [c, a, b], [c, b, a]].each do |this| this.sort.should == [a, b, c] end a.should be < b a.should be < c b.should be > a b.should be < c c.should be > a c.should be > b [a, b, c].each {|x| a.should be <= x } [a, b, c].each {|x| c.should be >= x } b.should be_between(a, c) end context "when creating instances" do context "with a resource" do let :resource do type.new(:name => "fred") end subject { provider.new(resource) } it "should set the resource correctly" do subject.resource.must equal resource end it "should set the name from the resource" do subject.name.should == resource.name end end context "with a hash" do subject { provider.new(:name => "fred") } it "should set the name" do subject.name.should == "fred" end it "should not have a resource" do subject.resource.should be_nil end end context "with no arguments" do subject { provider.new } it "should raise an internal error if asked for the name" do expect { subject.name }.to raise_error Puppet::DevError end it "should not have a resource" do subject.resource.should be_nil end end end context "when confining" do it "should be suitable by default" do subject.should be_suitable end it "should not be default by default" do subject.should_not be_default end { { :true => true } => true, { :true => false } => false, { :false => false } => true, { :false => true } => false, { :operatingsystem => Facter.value(:operatingsystem) } => true, { :operatingsystem => :yayness } => false, { :nothing => :yayness } => false, { :exists => Puppet::Util.which(existing_command) } => true, { :exists => "/this/file/does/not/exist" } => false, { :true => true, :exists => Puppet::Util.which(existing_command) } => true, { :true => true, :exists => "/this/file/does/not/exist" } => false, { :operatingsystem => Facter.value(:operatingsystem), :exists => Puppet::Util.which(existing_command) } => true, { :operatingsystem => :yayness, :exists => Puppet::Util.which(existing_command) } => false, { :operatingsystem => Facter.value(:operatingsystem), :exists => "/this/file/does/not/exist" } => false, { :operatingsystem => :yayness, :exists => "/this/file/does/not/exist" } => false, }.each do |confines, result| it "should confine #{confines.inspect} to #{result}" do confines.each {|test, value| subject.confine test => value } subject.send(result ? :should : :should_not, be_suitable) end end it "should not override a confine even if a second has the same type" do subject.confine :true => false subject.should_not be_suitable subject.confine :true => true subject.should_not be_suitable end it "should not be suitable if any confine fails" do subject.confine :true => false subject.should_not be_suitable 10.times do subject.confine :true => true subject.should_not be_suitable end end end context "default providers" do let :os do Facter.value(:operatingsystem) end it { should respond_to :specificity } it "should find the default provider" do type.provide(:nondefault) {} subject.defaultfor :operatingsystem => os subject.name.should == type.defaultprovider.name end it "should consider any true value enough to be default" do alternate = type.provide(:alternate) {} subject.defaultfor :operatingsystem => [:one, :two, :three, os] subject.name.should == type.defaultprovider.name subject.should be_default alternate.should_not be_default end it "should not be default if the confine doesn't match" do subject.should_not be_default subject.defaultfor :operatingsystem => :one subject.should_not be_default end it "should consider two defaults to be higher specificity than one default" do one = type.provide(:one) do defaultfor :operatingsystem => "solaris" end two = type.provide(:two) do defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" end two.specificity.should > one.specificity end it "should consider a subclass more specific than its parent class" do parent = type.provide(:parent) child = type.provide(:child, :parent => parent) child.specificity.should > parent.specificity end end context "provider commands" do it "should raise for unknown commands" do expect { subject.command(:something) }.to raise_error Puppet::DevError end it "should handle command inheritance" do parent = type.provide("parent") child = type.provide("child", :parent => parent.name) command = Puppet::Util.which('sh') || Puppet::Util.which('cmd.exe') parent.commands :sh => command FileTest.should be_exists parent.command(:sh) parent.command(:sh).should =~ /#{Regexp.escape(command)}$/ FileTest.should be_exists child.command(:sh) child.command(:sh).should =~ /#{Regexp.escape(command)}$/ end it "#1197: should find commands added in the same run" do subject.commands :testing => "puppet-bug-1197" subject.command(:testing).should be_nil subject.stubs(:which).with("puppet-bug-1197").returns("/puppet-bug-1197") subject.command(:testing).should == "/puppet-bug-1197" # Ideally, we would also test that `suitable?` returned the right thing # here, but it is impossible to get access to the methods that do that # without digging way down into the implementation. --daniel 2012-03-20 end context "with optional commands" do before :each do subject.optional_commands :cmd => "/no/such/binary/exists" end it { should be_suitable } it "should not be suitable if a mandatory command is also missing" do subject.commands :foo => "/no/such/binary/either" subject.should_not be_suitable end it "should define a wrapper for the command" do subject.should respond_to :cmd end it "should return nil if the command is requested" do subject.command(:cmd).should be_nil end it "should raise if the command is invoked" do expect { subject.cmd }.to raise_error Puppet::Error, /Command cmd is missing/ end end end context "execution" do before :each do Puppet.expects(:deprecation_warning).never end it "delegates instance execute to Puppet::Util::Execution" do Puppet::Util::Execution.expects(:execute).with("a_command", { :option => "value" }) provider.new.send(:execute, "a_command", { :option => "value" }) end it "delegates class execute to Puppet::Util::Execution" do Puppet::Util::Execution.expects(:execute).with("a_command", { :option => "value" }) provider.send(:execute, "a_command", { :option => "value" }) end it "delegates instance execpipe to Puppet::Util::Execution" do block = Proc.new { } Puppet::Util::Execution.expects(:execpipe).with("a_command", true, block) provider.new.send(:execpipe, "a_command", true, block) end it "delegates class execpipe to Puppet::Util::Execution" do block = Proc.new { } Puppet::Util::Execution.expects(:execpipe).with("a_command", true, block) provider.send(:execpipe, "a_command", true, block) end it "delegates instance execfail to Puppet::Util::Execution" do Puppet::Util::Execution.expects(:execfail).with("a_command", "an exception to raise") provider.new.send(:execfail, "a_command", "an exception to raise") end it "delegates class execfail to Puppet::Util::Execution" do Puppet::Util::Execution.expects(:execfail).with("a_command", "an exception to raise") provider.send(:execfail, "a_command", "an exception to raise") end end context "mk_resource_methods" do before :each do type.newproperty(:prop1) type.newproperty(:prop2) type.newparam(:param1) type.newparam(:param2) end fields = %w{prop1 prop2 param1 param2} # This is needed for Ruby 1.8.5, which throws an exception that the # default rescue doesn't catch if the method isn't present. Also, it has # no convenient predicate for them, which equally hurts. def has_method?(object, name) begin return true if object.instance_method(name) rescue Exception return false end end fields.each do |name| it "should add getter methods for #{name}" do expect { subject.mk_resource_methods }. to change { has_method?(subject, name) }. from(false).to(true) end it "should add setter methods for #{name}" do method = name + '=' expect { subject.mk_resource_methods }. to change { has_method?(subject, name) }. from(false).to(true) end end context "with an instance" do subject { provider.mk_resource_methods; provider.new(nil) } fields.each do |name| context name do it "should default to :absent" do subject.send(name).should == :absent end it "should update when set" do expect { subject.send(name + '=', "hello") }. to change { subject.send(name) }. from(:absent).to("hello") end end end end end context "source" do it "should default to the provider name" do subject.source.should == :default end it "should default to the provider name for a child provider" do type.provide(:sub, :parent => subject.name).source.should == :sub end it "should override if requested" do provider = type.provide(:sub, :parent => subject.name, :source => subject.source) provider.source.should == subject.source end it "should override to anything you want" do expect { subject.source = :banana }.to change { subject.source }. from(:default).to(:banana) end end context "features" do before :each do type.feature :numeric, '', :methods => [:one, :two] type.feature :alpha, '', :methods => [:a, :b] type.feature :nomethods, '' end { :no => { :alpha => false, :numeric => false, :methods => [] }, :numeric => { :alpha => false, :numeric => true, :methods => [:one, :two] }, :alpha => { :alpha => true, :numeric => false, :methods => [:a, :b] }, :all => { :alpha => true, :numeric => true, :methods => [:a, :b, :one, :two] }, :alpha_and_partial => { :alpha => true, :numeric => false, :methods => [:a, :b, :one] }, :numeric_and_partial => { :alpha => false, :numeric => true, :methods => [:a, :one, :two] }, :all_partial => { :alpha => false, :numeric => false, :methods => [:a, :one] }, :other_and_none => { :alpha => false, :numeric => false, :methods => [:foo, :bar] }, :other_and_alpha => { :alpha => true, :numeric => false, :methods => [:foo, :bar, :a, :b] }, }.each do |name, setup| context "with #{name.to_s.gsub('_', ' ')} features" do let :provider do provider = type.provide(name) setup[:methods].map do |method| provider.send(:define_method, method) do true end end type.provider(name) end let :numeric? do setup[:numeric] ? :should : :should_not end let :alpha? do setup[:alpha] ? :should : :should_not end subject { provider } it { should respond_to :has_features } it { should respond_to :has_feature } context "provider class" do it { should respond_to :nomethods? } it { should_not be_nomethods } it { should respond_to :numeric? } it { subject.send(numeric?, be_numeric) } it { subject.send(numeric?, be_satisfies(:numeric)) } it { should respond_to :alpha? } it { subject.send(alpha?, be_alpha) } it { subject.send(alpha?, be_satisfies(:alpha)) } end context "provider instance" do subject { provider.new } it { should respond_to :numeric? } it { subject.send(numeric?, be_numeric) } it { subject.send(numeric?, be_satisfies(:numeric)) } it { should respond_to :alpha? } it { subject.send(alpha?, be_alpha) } it { subject.send(alpha?, be_satisfies(:alpha)) } end end end context "feature with no methods" do before :each do type.feature :undemanding, '' end it { should respond_to :undemanding? } context "when the feature is not declared" do it { should_not be_undemanding } it { should_not be_satisfies :undemanding } end context "when the feature is declared" do before :each do subject.has_feature :undemanding end it { should be_undemanding } it { should be_satisfies :undemanding } end end context "supports_parameter?" do before :each do type.newparam(:no_feature) type.newparam(:one_feature, :required_features => :alpha) type.newparam(:two_features, :required_features => [:alpha, :numeric]) end let :providers do { :zero => type.provide(:zero), :one => type.provide(:one) do has_features :alpha end, :two => type.provide(:two) do has_features :alpha, :numeric end } end { :zero => { :yes => [:no_feature], :no => [:one_feature, :two_features] }, :one => { :yes => [:no_feature, :one_feature], :no => [:two_features] }, :two => { :yes => [:no_feature, :one_feature, :two_features], :no => [] } }.each do |name, data| data[:yes].each do |param| it "should support #{param} with provider #{name}" do providers[name].should be_supports_parameter param end end data[:no].each do |param| it "should not support #{param} with provider #{name}" do providers[name].should_not be_supports_parameter param end end end end end def provider_of(options = {}, &block) type = Puppet::Type.newtype(:dummy) do provide(:dummy, options, &block) end type.provider(:dummy) end def expect_command_executed(name, path, *args) command = Puppet::Provider::Command.new(name, path, Puppet::Util, Puppet::Util::Execution) command.expects(:execute).with(*args) command end def allow_creation_of(command, environment = {}) Puppet::Provider::Command.stubs(:new).with(command.name, command.executable, Puppet::Util, Puppet::Util::Execution, { :custom_environment => environment }).returns(command) end def file_exists_and_is_executable(path) FileTest.expects(:file?).with(path).returns(true) FileTest.expects(:executable?).with(path).returns(true) end end diff --git a/spec/unit/puppet_spec.rb b/spec/unit/puppet_spec.rb index afe7a9d7c..fca753e0e 100755 --- a/spec/unit/puppet_spec.rb +++ b/spec/unit/puppet_spec.rb @@ -1,45 +1,45 @@ -#!/usr/bin/env rspec" +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet' require 'puppet_spec/files' require 'semver' describe Puppet do include PuppetSpec::Files context "#version" do it "should be a valid version number" do Puppet.version.should =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ end it "should be valid semver" do SemVer.should be_valid Puppet.version end end Puppet::Util::Log.eachlevel do |level| it "should have a method for sending '#{level}' logs" do Puppet.should respond_to(level) end end it "should be able to change the path" do newpath = ENV["PATH"] + File::PATH_SEPARATOR + "/something/else" Puppet[:path] = newpath ENV["PATH"].should == newpath end it "should change $LOAD_PATH when :libdir changes" do one = tmpdir('load-path-one') two = tmpdir('load-path-two') one.should_not == two Puppet[:libdir] = one $LOAD_PATH.should include one $LOAD_PATH.should_not include two Puppet[:libdir] = two $LOAD_PATH.should_not include one $LOAD_PATH.should include two end end diff --git a/spec/unit/rails/host_spec.rb b/spec/unit/rails/host_spec.rb index f82dfda05..037f4e19c 100755 --- a/spec/unit/rails/host_spec.rb +++ b/spec/unit/rails/host_spec.rb @@ -1,162 +1,162 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/node/environment' describe "Puppet::Rails::Host", :if => can_use_scratch_database? do before do require 'puppet/rails/host' setup_scratch_database @node = Puppet::Node.new("foo") @node.environment = "production" @node.ipaddress = "127.0.0.1" @host = stub 'host', :environment= => nil, :ip= => nil end describe "when converting a Puppet::Node instance into a Rails instance" do it "should modify any existing instance in the database" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host Puppet::Rails::Host.from_puppet(@node) end it "should create a new instance in the database if none can be found" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns nil Puppet::Rails::Host.expects(:new).with(:name => "foo").returns @host Puppet::Rails::Host.from_puppet(@node) end it "should copy the environment from the Puppet instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @node.environment = "production" @host.expects(:environment=).with {|x| x.name.to_s == 'production' } Puppet::Rails::Host.from_puppet(@node) end it "should stringify the environment" do host = Puppet::Rails::Host.new host.environment = Puppet::Node::Environment.new("production") host.environment.class.should == String end it "should copy the ipaddress from the Puppet instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @node.ipaddress = "192.168.0.1" @host.expects(:ip=).with "192.168.0.1" Puppet::Rails::Host.from_puppet(@node) end it "should not save the Rails instance" do Puppet::Rails::Host.expects(:find_by_name).with("foo").returns @host @host.expects(:save).never Puppet::Rails::Host.from_puppet(@node) end end describe "when converting a Puppet::Rails::Host instance into a Puppet::Node instance" do before do @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @node = Puppet::Node.new("foo") Puppet::Node.stubs(:new).with("foo").returns @node end it "should create a new instance with the correct name" do Puppet::Node.expects(:new).with("foo").returns @node @host.to_puppet end it "should copy the environment from the Rails instance" do @host.environment = "prod" @node.expects(:environment=).with "prod" @host.to_puppet end it "should copy the ipaddress from the Rails instance" do @host.ip = "192.168.0.1" @node.expects(:ipaddress=).with "192.168.0.1" @host.to_puppet end end describe "when merging catalog resources and database resources" do before :each do Puppet[:thin_storeconfigs] = false @resource1 = stub_everything 'res1' @resource2 = stub_everything 'res2' @resources = [ @resource1, @resource2 ] @dbresource1 = stub_everything 'dbres1' @dbresource2 = stub_everything 'dbres2' @dbresources = { 1 => @dbresource1, 2 => @dbresource2 } @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @host.stubs(:find_resources).returns(@dbresources) @host.stubs(:find_resources_parameters_tags) @host.stubs(:compare_to_catalog) @host.stubs(:id).returns(1) end it "should find all database resources" do @host.expects(:find_resources) @host.merge_resources(@resources) end it "should find all paramaters and tags for those database resources" do @host.expects(:find_resources_parameters_tags).with(@dbresources) @host.merge_resources(@resources) end it "should compare all database resources to catalog" do @host.expects(:compare_to_catalog).with(@dbresources, @resources) @host.merge_resources(@resources) end it "should compare only exported resources in thin_storeconfigs mode" do Puppet[:thin_storeconfigs] = true @resource1.stubs(:exported?).returns(true) @host.expects(:compare_to_catalog).with(@dbresources, [ @resource1 ]) @host.merge_resources(@resources) end end describe "when searching the database for host resources" do before :each do Puppet[:thin_storeconfigs] = false @resource1 = stub_everything 'res1', :id => 1 @resource2 = stub_everything 'res2', :id => 2 @resources = [ @resource1, @resource2 ] @dbresources = stub 'resources' @dbresources.stubs(:find).returns(@resources) @host = Puppet::Rails::Host.new(:name => "foo", :environment => "production", :ip => "127.0.0.1") @host.stubs(:resources).returns(@dbresources) end it "should return a hash keyed by id of all resources" do @host.find_resources.should == { 1 => @resource1, 2 => @resource2 } end it "should return a hash keyed by id of only exported resources in thin_storeconfigs mode" do Puppet[:thin_storeconfigs] = true @dbresources.expects(:find).with { |*h| h[1][:conditions] == { :exported => true } }.returns([]) @host.find_resources end end end diff --git a/spec/unit/rails/param_value_spec.rb b/spec/unit/rails/param_value_spec.rb index e7f4328b1..5e9528c8d 100755 --- a/spec/unit/rails/param_value_spec.rb +++ b/spec/unit/rails/param_value_spec.rb @@ -1,42 +1,42 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' describe "Puppet::Rails::ParamValue", :if => can_use_scratch_database? do before do require 'puppet/rails/param_value' setup_scratch_database # Stub this so we don't need access to the DB. name = stub 'param_name', :name => "foo" Puppet::Rails::ParamName.stubs(:find_or_create_by_name).returns(name) end describe "when creating initial parameter values" do it "should return an array of hashes" do Puppet::Rails::ParamValue.from_parser_param(:myparam, %w{a b})[0].should be_instance_of(Hash) end it "should return hashes for each value with the parameter name set as the ParamName instance" do name = stub 'param_name', :name => "foo" Puppet::Rails::ParamName.expects(:find_or_create_by_name).returns(name) result = Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0] result[:value].should == "a" result[:param_name].should == name end it "should return an array of hashes even when only one parameter is provided" do Puppet::Rails::ParamValue.from_parser_param(:myparam, "a")[0].should be_instance_of(Hash) end it "should convert all arguments into strings" do Puppet::Rails::ParamValue.from_parser_param(:myparam, 50)[0][:value].should == "50" end it "should not convert Resource References into strings" do ref = Puppet::Resource.new(:file, "/file") Puppet::Rails::ParamValue.from_parser_param(:myparam, ref)[0][:value].should == ref end end end diff --git a/spec/unit/rails/resource_spec.rb b/spec/unit/rails/resource_spec.rb index 4202df4e2..510160452 100755 --- a/spec/unit/rails/resource_spec.rb +++ b/spec/unit/rails/resource_spec.rb @@ -1,115 +1,115 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' describe "Puppet::Rails::Resource", :if => can_use_scratch_database? do before do require 'puppet/rails/resource' setup_scratch_database end describe "when creating initial resource arguments" do it "should set the restype to the resource's type" do Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:restype].should == "File" end it "should set the title to the resource's title" do Puppet::Rails::Resource.rails_resource_initial_args(Puppet::Resource.new(:file, "/file"))[:title].should == "/file" end it "should set the line to the resource's line if one is available" do resource = Puppet::Resource.new(:file, "/file") resource.line = 50 Puppet::Rails::Resource.rails_resource_initial_args(resource)[:line].should == 50 end it "should set 'exported' to true of the resource is exported" do resource = Puppet::Resource.new(:file, "/file") resource.exported = true Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_true end it "should set 'exported' to false of the resource is not exported" do resource = Puppet::Resource.new(:file, "/file") resource.exported = false Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false resource = Puppet::Resource.new(:file, "/file") resource.exported = nil Puppet::Rails::Resource.rails_resource_initial_args(resource)[:exported].should be_false end end describe "when merging in a parser resource" do before do @parser = mock 'parser resource' @resource = Puppet::Rails::Resource.new [:merge_attributes, :merge_parameters, :merge_tags, :save].each { |m| @resource.stubs(m) } end it "should merge the attributes" do @resource.expects(:merge_attributes).with(@parser) @resource.merge_parser_resource(@parser) end it "should merge the parameters" do @resource.expects(:merge_parameters).with(@parser) @resource.merge_parser_resource(@parser) end it "should merge the tags" do @resource.expects(:merge_tags).with(@parser) @resource.merge_parser_resource(@parser) end it "should save itself" do @resource.expects(:save) @resource.merge_parser_resource(@parser) end end describe "merge_parameters" do it "should replace values that have changed" do @resource = Puppet::Rails::Resource.new @resource.params_list = [{"name" => "replace", "value" => 1, "id" => 100 }] Puppet::Rails::ParamValue.expects(:delete).with([100]) param_values = stub "param_values" param_values.expects(:build).with({:value=>nil, :param_name=>nil, :line=>{"replace"=>2}}) @resource.stubs(:param_values).returns(param_values) Puppet::Rails::ParamName.stubs(:accumulate_by_name) merge_resource = stub "merge_resource" merge_resource.expects(:line).returns({ "replace" => 2 }) merge_resource.stubs(:each).yields([["replace", 2]]) @resource.merge_parameters(merge_resource) end end describe "#to_resource" do it "should instantiate a Puppet::Parser::Resource" do scope = stub "scope", :source => nil, :environment => nil, :namespaces => nil @resource = Puppet::Rails::Resource.new @resource.stubs(:attributes).returns({ "restype" => 'notify', "title" => 'hello' }) @resource.stubs(:param_names).returns([]) @resource.to_resource(scope).should be_a(Puppet::Parser::Resource) end end end diff --git a/spec/unit/rails_spec.rb b/spec/unit/rails_spec.rb index 60062db1c..6fbce4d04 100755 --- a/spec/unit/rails_spec.rb +++ b/spec/unit/rails_spec.rb @@ -1,244 +1,244 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/rails' describe Puppet::Rails, "when initializing any connection", :if => Puppet.features.rails? do before do Puppet.settings.stubs(:use) @logger = mock 'logger' @logger.stub_everything Logger.stubs(:new).returns(@logger) ActiveRecord::Base.stubs(:logger).returns(@logger) ActiveRecord::Base.stubs(:connected?).returns(false) end it "should use settings" do Puppet.settings.expects(:use).with(:main, :rails, :master) Puppet::Rails.connect end it "should set up a logger with the appropriate Rails log file" do logger = mock 'logger' Logger.expects(:new).with(Puppet[:railslog]).returns(logger) ActiveRecord::Base.expects(:logger=).with(logger) Puppet::Rails.connect end it "should set the log level to whatever the value is in the settings" do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).with(:rails_loglevel).returns("debug") Puppet.settings.stubs(:value).with(:railslog).returns("/my/file") logger = mock 'logger' Logger.stubs(:new).returns(logger) ActiveRecord::Base.stubs(:logger).returns(logger) logger.expects(:level=).with(Logger::DEBUG) ActiveRecord::Base.stubs(:allow_concurrency=) ActiveRecord::Base.stubs(:verify_active_connections!) ActiveRecord::Base.stubs(:establish_connection) Puppet::Rails.stubs(:database_arguments).returns({}) Puppet::Rails.connect end describe "ActiveRecord Version" do it "should set ActiveRecord::Base.allow_concurrency if ActiveRecord is 2.1" do Puppet::Util.stubs(:activerecord_version).returns(2.1) ActiveRecord::Base.expects(:allow_concurrency=).with(true) Puppet::Rails.connect end it "should not set ActiveRecord::Base.allow_concurrency if ActiveRecord is >= 2.2" do Puppet::Util.stubs(:activerecord_version).returns(2.2) ActiveRecord::Base.expects(:allow_concurrency=).never Puppet::Rails.connect end end it "should call ActiveRecord::Base.verify_active_connections!" do ActiveRecord::Base.expects(:verify_active_connections!) Puppet::Rails.connect end it "should call ActiveRecord::Base.establish_connection with database_arguments" do Puppet::Rails.expects(:database_arguments).returns({}) ActiveRecord::Base.expects(:establish_connection) Puppet::Rails.connect end end describe Puppet::Rails, "when initializing a sqlite3 connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and database arguments" do Puppet.settings.expects(:value).with(:dbadapter).returns("sqlite3") Puppet.settings.expects(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.expects(:value).with(:dblocation).returns("testlocation") Puppet::Rails.database_arguments.should == { :adapter => "sqlite3", :log_level => "testlevel", :database => "testlocation" } end end ['mysql','mysql2','postgresql'].each do |dbadapter| describe Puppet::Rails, "when initializing a #{dbadapter} connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and host, port, username, password, database, and reconnect arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 45).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("") Puppet::Rails.database_arguments.should == { :adapter => dbadapter, :log_level => "testlevel", :host => "testserver", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :reconnect => true } end it "should provide the adapter, log_level, and host, port, username, password, database, socket, connections, and reconnect arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 12).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet::Rails.database_arguments.should == { :adapter => dbadapter, :log_level => "testlevel", :host => "testserver", :port => "9999", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :socket => "testsocket", :reconnect => true } end it "should provide the adapter, log_level, and host, port, username, password, database, socket, and connections arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 23).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet::Rails.database_arguments.should == { :adapter => dbadapter, :log_level => "testlevel", :host => "testserver", :port => "9999", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname", :socket => "testsocket", :reconnect => true } end it "should not provide the pool if dbconnections is 0, '0', or ''" do Puppet.settings.stubs(:value).with(:dbadapter).returns(dbadapter) Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet.settings.stubs(:value).with(:dbconnections).returns(0) Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('0') Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('') Puppet::Rails.database_arguments.should_not be_include(:pool) end end end describe Puppet::Rails, "when initializing an Oracle connection", :if => Puppet.features.rails? do it "should provide the adapter, log_level, and username, password, and database arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 123).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet::Rails.database_arguments.should == { :adapter => "oracle_enhanced", :log_level => "testlevel", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname" } end it "should provide the adapter, log_level, and host, username, password, database and socket arguments" do Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbconnections).returns((pool_size = 124).to_s) Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet::Rails.database_arguments.should == { :adapter => "oracle_enhanced", :log_level => "testlevel", :username => "testuser", :password => "testpassword", :pool => pool_size, :database => "testname" } end it "should not provide the pool if dbconnections is 0, '0', or ''" do Puppet.settings.stubs(:value).with(:dbadapter).returns("oracle_enhanced") Puppet.settings.stubs(:value).with(:rails_loglevel).returns("testlevel") Puppet.settings.stubs(:value).with(:dbserver).returns("testserver") Puppet.settings.stubs(:value).with(:dbport).returns("9999") Puppet.settings.stubs(:value).with(:dbuser).returns("testuser") Puppet.settings.stubs(:value).with(:dbpassword).returns("testpassword") Puppet.settings.stubs(:value).with(:dbname).returns("testname") Puppet.settings.stubs(:value).with(:dbsocket).returns("testsocket") Puppet.settings.stubs(:value).with(:dbconnections).returns(0) Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('0') Puppet::Rails.database_arguments.should_not be_include(:pool) Puppet.settings.stubs(:value).with(:dbconnections).returns('') Puppet::Rails.database_arguments.should_not be_include(:pool) end end diff --git a/spec/unit/relationship_spec.rb b/spec/unit/relationship_spec.rb index 1a748beff..06ce1d2fb 100755 --- a/spec/unit/relationship_spec.rb +++ b/spec/unit/relationship_spec.rb @@ -1,228 +1,228 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/relationship' describe Puppet::Relationship do before do @edge = Puppet::Relationship.new(:a, :b) end it "should have a :source attribute" do @edge.should respond_to(:source) end it "should have a :target attribute" do @edge.should respond_to(:target) end it "should have a :callback attribute" do @edge.callback = :foo @edge.callback.should == :foo end it "should have an :event attribute" do @edge.event = :NONE @edge.event.should == :NONE end it "should require a callback if a non-NONE event is specified" do proc { @edge.event = :something }.should raise_error(ArgumentError) end it "should have a :label attribute" do @edge.should respond_to(:label) end it "should provide a :ref method that describes the edge" do @edge = Puppet::Relationship.new("a", "b") @edge.ref.should == "a => b" end it "should be able to produce a label as a hash with its event and callback" do @edge.callback = :foo @edge.event = :bar @edge.label.should == {:callback => :foo, :event => :bar} end it "should work if nil options are provided" do lambda { Puppet::Relationship.new("a", "b", nil) }.should_not raise_error end end describe Puppet::Relationship, " when initializing" do before do @edge = Puppet::Relationship.new(:a, :b) end it "should use the first argument as the source" do @edge.source.should == :a end it "should use the second argument as the target" do @edge.target.should == :b end it "should set the rest of the arguments as the event and callback" do @edge = Puppet::Relationship.new(:a, :b, :callback => :foo, :event => :bar) @edge.callback.should == :foo @edge.event.should == :bar end it "should accept events specified as strings" do @edge = Puppet::Relationship.new(:a, :b, "event" => :NONE) @edge.event.should == :NONE end it "should accept callbacks specified as strings" do @edge = Puppet::Relationship.new(:a, :b, "callback" => :foo) @edge.callback.should == :foo end end describe Puppet::Relationship, " when matching edges with no specified event" do before do @edge = Puppet::Relationship.new(:a, :b) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should not match :ALL_EVENTS" do @edge.should_not be_match(:NONE) end it "should not match any other events" do @edge.should_not be_match(:whatever) end end describe Puppet::Relationship, " when matching edges with :NONE as the event" do before do @edge = Puppet::Relationship.new(:a, :b, :event => :NONE) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should not match :ALL_EVENTS" do @edge.should_not be_match(:ALL_EVENTS) end it "should not match other events" do @edge.should_not be_match(:yayness) end end describe Puppet::Relationship, " when matching edges with :ALL as the event" do before do @edge = Puppet::Relationship.new(:a, :b, :event => :ALL_EVENTS, :callback => :whatever) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should match :ALL_EVENTS" do @edge.should be_match(:ALLEVENTS) end it "should match all other events" do @edge.should be_match(:foo) end end describe Puppet::Relationship, " when matching edges with a non-standard event" do before do @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) end it "should not match :NONE" do @edge.should_not be_match(:NONE) end it "should not match :ALL_EVENTS" do @edge.should_not be_match(:ALL_EVENTS) end it "should match events with the same name" do @edge.should be_match(:random) end end describe Puppet::Relationship, "when converting to pson", :if => Puppet.features.pson? do before do @edge = Puppet::Relationship.new(:a, :b, :event => :random, :callback => :whatever) end it "should store the stringified source as the source in the data" do PSON.parse(@edge.to_pson)["source"].should == "a" end it "should store the stringified target as the target in the data" do PSON.parse(@edge.to_pson)['target'].should == "b" end it "should store the psonified event as the event in the data" do PSON.parse(@edge.to_pson)["event"].should == "random" end it "should not store an event when none is set" do @edge.event = nil PSON.parse(@edge.to_pson)["event"].should be_nil end it "should store the psonified callback as the callback in the data" do @edge.callback = "whatever" PSON.parse(@edge.to_pson)["callback"].should == "whatever" end it "should not store a callback when none is set in the edge" do @edge.callback = nil PSON.parse(@edge.to_pson)["callback"].should be_nil end end describe Puppet::Relationship, "when converting from pson", :if => Puppet.features.pson? do before do @event = "random" @callback = "whatever" @data = { "source" => "mysource", "target" => "mytarget", "event" => @event, "callback" => @callback } @pson = { "type" => "Puppet::Relationship", "data" => @data } end def pson_result_should Puppet::Relationship.expects(:new).with { |*args| yield args } end it "should be extended with the PSON utility module" do Puppet::Relationship.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end # LAK:NOTE For all of these tests, we convert back to the edge so we can # trap the actual data structure then. it "should pass the source in as the first argument" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").source.should == "mysource" end it "should pass the target in as the second argument" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget").target.should == "mytarget" end it "should pass the event as an argument if it's provided" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "event" => "myevent", "callback" => "eh").event.should == "myevent" end it "should pass the callback as an argument if it's provided" do Puppet::Relationship.from_pson("source" => "mysource", "target" => "mytarget", "callback" => "mycallback").callback.should == "mycallback" end end diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb index 62b4289cf..9d97e6f24 100755 --- a/spec/unit/reports/http_spec.rb +++ b/spec/unit/reports/http_spec.rb @@ -1,79 +1,79 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/reports' processor = Puppet::Reports.report(:http) describe processor do subject { Puppet::Transaction::Report.new("apply").extend(processor) } it "should use the reporturl setting's host, port and ssl option" do uri = URI.parse(Puppet[:reporturl]) ssl = (uri.scheme == 'https') Puppet::Network::HttpPool.expects(:http_instance).with(uri.host, uri.port, use_ssl=ssl).returns(stub_everything('http')) subject.process end it "should use ssl if requested" do Puppet[:reporturl] = Puppet[:reporturl].sub(/^http:\/\//, 'https://') uri = URI.parse(Puppet[:reporturl]) Puppet::Network::HttpPool.expects(:http_instance).with(uri.host, uri.port, use_ssl=true).returns(stub_everything('http')) subject.process end describe "when making a request" do let(:http) { mock "http" } let(:httpok) { Net::HTTPOK.new('1.1', 200, '') } before :each do Net::HTTP.any_instance.expects(:start).yields(http) end it "should use the path specified by the 'reporturl' setting" do http.expects(:request).with {|req| req.path.should == URI.parse(Puppet[:reporturl]).path }.returns(httpok) subject.process end it "should give the body as the report as YAML" do http.expects(:request).with {|req| req.body.should == subject.to_yaml }.returns(httpok) subject.process end it "should set content-type to 'application/x-yaml'" do http.expects(:request).with {|req| req.content_type.should == "application/x-yaml" }.returns(httpok) subject.process end Net::HTTPResponse::CODE_TO_OBJ.each do |code, klass| if code.to_i >= 200 and code.to_i < 300 it "should succeed on http code #{code}" do response = klass.new('1.1', code, '') http.expects(:request).returns(response) Puppet.expects(:err).never subject.process end end if code.to_i >= 300 it "should log error on http code #{code}" do response = klass.new('1.1', code, '') http.expects(:request).returns(response) Puppet.expects(:err) subject.process end end end end end diff --git a/spec/unit/reports/rrdgraph_spec.rb b/spec/unit/reports/rrdgraph_spec.rb index 3c2704a7a..d0ef9be7c 100755 --- a/spec/unit/reports/rrdgraph_spec.rb +++ b/spec/unit/reports/rrdgraph_spec.rb @@ -1,30 +1,30 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/reports' processor = Puppet::Reports.report(:rrdgraph) describe processor do include PuppetSpec::Files before do Puppet[:rrddir] = tmpdir('rrdgraph') Puppet.settings.use :master end after do FileUtils.rm_rf(Puppet[:rrddir]) end it "should not error on 0.25.x report format" do report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report0.25.x.yaml')).extend processor report.expects(:mkhtml) lambda{ report.process }.should_not raise_error end it "should not error on 2.6.x report format" do report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor report.expects(:mkhtml) lambda{ report.process }.should_not raise_error end end diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb index 5b752c4b7..113671ef7 100755 --- a/spec/unit/reports/store_spec.rb +++ b/spec/unit/reports/store_spec.rb @@ -1,48 +1,48 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/reports' require 'time' require 'pathname' require 'tempfile' require 'fileutils' processor = Puppet::Reports.report(:store) describe processor do describe "#process" do include PuppetSpec::Files before :each do Puppet[:reportdir] = File.join(tmpdir('reports'), 'reports') @report = YAML.load_file(File.join(PuppetSpec::FIXTURE_DIR, 'yaml/report2.6.x.yaml')).extend processor end it "should create a report directory for the client if one doesn't exist" do @report.process File.should be_directory(File.join(Puppet[:reportdir], @report.host)) end it "should write the report to the file in YAML" do Time.stubs(:now).returns(Time.parse("2011-01-06 12:00:00 UTC")) @report.process File.read(File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")).should == @report.to_yaml end it "should write to the report directory in the correct sequence" do # By doing things in this sequence we should protect against race # conditions Time.stubs(:now).returns(Time.parse("2011-01-06 12:00:00 UTC")) writeseq = sequence("write") file = mock "file" Tempfile.expects(:new).in_sequence(writeseq).returns(file) file.expects(:chmod).in_sequence(writeseq).with(0640) file.expects(:print).with(@report.to_yaml).in_sequence(writeseq) file.expects(:close).in_sequence(writeseq) file.stubs(:path).returns(File.join(Dir.tmpdir, "foo123")) FileUtils.expects(:mv).in_sequence(writeseq).with(File.join(Dir.tmpdir, "foo123"), File.join(Puppet[:reportdir], @report.host, "201101061200.yaml")) @report.process end end end diff --git a/spec/unit/reports/tagmail_spec.rb b/spec/unit/reports/tagmail_spec.rb index 7fd209828..49bc42d32 100755 --- a/spec/unit/reports/tagmail_spec.rb +++ b/spec/unit/reports/tagmail_spec.rb @@ -1,218 +1,218 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/reports' tagmail = Puppet::Reports.report(:tagmail) describe tagmail do before do @processor = Puppet::Transaction::Report.new("apply") @processor.extend(Puppet::Reports.report(:tagmail)) end passers = my_fixture "tagmail_passers.conf" File.readlines(passers).each do |line| it "should be able to parse '#{line.inspect}'" do @processor.parse(line) end end failers = my_fixture "tagmail_failers.conf" File.readlines(failers).each do |line| it "should not be able to parse '#{line.inspect}'" do lambda { @processor.parse(line) }.should raise_error(ArgumentError) end end { "tag: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, []], "tag.localhost: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag.localhost}, []], "tag, other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag other}, []], "tag-other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag-other}, []], "tag, !other: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag}, %w{other}], "tag, !other, one, !two: abuse@domain.com" => [%w{abuse@domain.com}, %w{tag one}, %w{other two}], "tag: abuse@domain.com, other@domain.com" => [%w{abuse@domain.com other@domain.com}, %w{tag}, []] }.each do |line, results| it "should parse '#{line}' as #{results.inspect}" do @processor.parse(line).shift.should == results end end describe "when matching logs" do before do @processor << Puppet::Util::Log.new(:level => :notice, :message => "first", :tags => %w{one}) @processor << Puppet::Util::Log.new(:level => :notice, :message => "second", :tags => %w{one two}) @processor << Puppet::Util::Log.new(:level => :notice, :message => "third", :tags => %w{one two three}) end def match(pos = [], neg = []) pos = Array(pos) neg = Array(neg) result = @processor.match([[%w{abuse@domain.com}, pos, neg]]) actual_result = result.shift if actual_result actual_result[1] else nil end end it "should match all messages when provided the 'all' tag as a positive matcher" do results = match("all") %w{first second third}.each do |str| results.should be_include(str) end end it "should remove messages that match a negated tag" do match("all", "three").should_not be_include("third") end it "should find any messages tagged with a provided tag" do results = match("two") results.should be_include("second") results.should be_include("third") results.should_not be_include("first") end it "should allow negation of specific tags from a specific tag list" do results = match("two", "three") results.should be_include("second") results.should_not be_include("third") end it "should allow a tag to negate all matches" do results = match([], "one") results.should be_nil end end describe "the behavior of tagmail.process" do before do Puppet[:tagmap] = my_fixture "tagmail_email.conf" end let(:processor) do processor = Puppet::Transaction::Report.new("apply") processor.extend(Puppet::Reports.report(:tagmail)) processor end context "when any messages match a positive tag" do before do processor << log_entry end let(:log_entry) do Puppet::Util::Log.new( :level => :notice, :message => "Secure change", :tags => %w{secure}) end let(:message) do "#{log_entry.time} Puppet (notice): Secure change" end it "should send email if there are changes" do processor.expects(:send).with([[['user@domain.com'], message]]) processor.expects(:raw_summary).returns({ "resources" => { "changed" => 1, "out_of_sync" => 0 } }) processor.process end it "should send email if there are resources out of sync" do processor.expects(:send).with([[['user@domain.com'], message]]) processor.expects(:raw_summary).returns({ "resources" => { "changed" => 0, "out_of_sync" => 1 } }) processor.process end it "should not send email if no changes or resources out of sync" do processor.expects(:send).never processor.expects(:raw_summary).returns({ "resources" => { "changed" => 0, "out_of_sync" => 0 } }) processor.process end it "should log a message if no changes or resources out of sync" do processor.expects(:send).never processor.expects(:raw_summary).returns({ "resources" => { "changed" => 0, "out_of_sync" => 0 } }) Puppet.expects(:notice).with("Not sending tagmail report; no changes") processor.process end it "should send email if raw_summary is not defined" do processor.expects(:send).with([[['user@domain.com'], message]]) processor.expects(:raw_summary).returns(nil) processor.process end it "should send email if there are no resource metrics" do processor.expects(:send).with([[['user@domain.com'], message]]) processor.expects(:raw_summary).returns({'resources' => nil}) processor.process end end context "when no message match a positive tag" do before do processor << log_entry end let(:log_entry) do Puppet::Util::Log.new( :level => :notice, :message => 'Unnotices change', :tags => %w{not_present_in_tagmail.conf} ) end it "should send no email if there are changes" do processor.expects(:send).never processor.expects(:raw_summary).returns({ "resources" => { "changed" => 1, "out_of_sync" => 0 } }) processor.process end it "should send no email if there are resources out of sync" do processor.expects(:send).never processor.expects(:raw_summary).returns({ "resources" => { "changed" => 0, "out_of_sync" => 1 } }) processor.process end it "should send no email if no changes or resources out of sync" do processor.expects(:send).never processor.expects(:raw_summary).returns({ "resources" => { "changed" => 0, "out_of_sync" => 0 } }) processor.process end it "should send no email if raw_summary is not defined" do processor.expects(:send).never processor.expects(:raw_summary).returns(nil) processor.process end it "should send no email if there are no resource metrics" do processor.expects(:send).never processor.expects(:raw_summary).returns({'resources' => nil}) processor.process end end end end diff --git a/spec/unit/reports_spec.rb b/spec/unit/reports_spec.rb index a4b2e04a9..155f01d53 100755 --- a/spec/unit/reports_spec.rb +++ b/spec/unit/reports_spec.rb @@ -1,60 +1,60 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/reports' describe Puppet::Reports do it "should instance-load report types" do Puppet::Reports.instance_loader(:report).should be_instance_of(Puppet::Util::Autoload) end it "should have a method for registering report types" do Puppet::Reports.should respond_to(:register_report) end it "should have a method for retrieving report types by name" do Puppet::Reports.should respond_to(:report) end it "should provide a method for returning documentation for all reports" do Puppet::Reports.expects(:loaded_instances).with(:report).returns([:one, :two]) one = mock 'one', :doc => "onedoc" two = mock 'two', :doc => "twodoc" Puppet::Reports.expects(:report).with(:one).returns(one) Puppet::Reports.expects(:report).with(:two).returns(two) doc = Puppet::Reports.reportdocs doc.include?("onedoc").should be_true doc.include?("twodoc").should be_true end end describe Puppet::Reports, " when loading report types" do it "should use the instance loader to retrieve report types" do Puppet::Reports.expects(:loaded_instance).with(:report, :myreporttype) Puppet::Reports.report(:myreporttype) end end describe Puppet::Reports, " when registering report types" do it "should evaluate the supplied block as code for a module" do Puppet::Reports.expects(:genmodule).returns(Module.new) Puppet::Reports.register_report(:testing) { } end it "should extend the report type with the Puppet::Util::Docs module" do mod = stub 'module', :define_method => true Puppet::Reports.expects(:genmodule).with { |name, options, block| options[:extend] == Puppet::Util::Docs }.returns(mod) Puppet::Reports.register_report(:testing) { } end it "should define a :report_name method in the module that returns the name of the report" do mod = mock 'module' mod.expects(:define_method).with(:report_name) Puppet::Reports.expects(:genmodule).returns(mod) Puppet::Reports.register_report(:testing) { } end end diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb index 33794dafc..a52f1ba8f 100755 --- a/spec/unit/resource/catalog_spec.rb +++ b/spec/unit/resource/catalog_spec.rb @@ -1,965 +1,965 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Resource::Catalog, "when compiling" do include PuppetSpec::Files before do @basepath = make_absolute("/somepath") # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end # audit only resources are unmanaged # as are resources without properties with should values it "should write its managed resources' types, namevars" do catalog = Puppet::Resource::Catalog.new("host") resourcefile = tmpfile('resourcefile') Puppet[:resourcefile] = resourcefile res = Puppet::Type.type('file').new(:title => File.expand_path('/tmp/sam'), :ensure => 'present') res.file = 'site.pp' res.line = 21 res2 = Puppet::Type.type('exec').new(:title => 'bob', :command => "#{File.expand_path('/bin/rm')} -rf /") res2.file = File.expand_path('/modules/bob/manifests/bob.pp') res2.line = 42 res3 = Puppet::Type.type('file').new(:title => File.expand_path('/tmp/susan'), :audit => 'all') res3.file = 'site.pp' res3.line = 63 res4 = Puppet::Type.type('file').new(:title => File.expand_path('/tmp/lilly')) res4.file = 'site.pp' res4.line = 84 comp_res = Puppet::Type.type('component').new(:title => 'Class[Main]') catalog.add_resource(res, res2, res3, res4, comp_res) catalog.write_resource_file File.readlines(resourcefile).map(&:chomp).should =~ [ "file[#{File.expand_path('/tmp/sam')}]", "exec[#{File.expand_path('/bin/rm')} -rf /]" ] end it "should log an error if unable to write to the resource file" do catalog = Puppet::Resource::Catalog.new("host") Puppet[:resourcefile] = File.expand_path('/not/writable/file') catalog.add_resource(Puppet::Type.type('file').new(:title => File.expand_path('/tmp/foo'))) catalog.write_resource_file @logs.size.should == 1 @logs.first.message.should =~ /Could not create resource file/ @logs.first.level.should == :err end it "should be able to write its list of classes to the class file" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.add_class "foo", "bar" Puppet.settings.expects(:value).with(:classfile).returns "/class/file" fh = mock 'filehandle' File.expects(:open).with("/class/file", "w").yields fh fh.expects(:puts).with "foo\nbar" @catalog.write_class_file end it "should have a client_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.client_version = 5 @catalog.client_version.should == 5 end it "should have a server_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.server_version = 5 @catalog.server_version.should == 5 end describe "when compiling" do it "should accept tags" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one") config.tags.should == %w{one} end it "should accept multiple tags at once" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", "two") config.tags.should == %w{one two} end it "should convert all tags to strings" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", :two) config.tags.should == %w{one two} end it "should tag with both the qualified name and the split name" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one::two") config.tags.include?("one").should be_true config.tags.include?("one::two").should be_true end it "should accept classes" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.classes.should == %w{one} config.add_class("two", "three") config.classes.should == %w{one two three} end it "should tag itself with passed class names" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.tags.should == %w{one} end end describe "when converting to a RAL catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::Resource.new :class, 'top' @topobject = Puppet::Resource.new :file, @basepath+'/topobject' @middle = Puppet::Resource.new :class, 'middle' @middleobject = Puppet::Resource.new :file, @basepath+'/middleobject' @bottom = Puppet::Resource.new :class, 'bottom' @bottomobject = Puppet::Resource.new :file, @basepath+'/bottomobject' @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_ral end it "should add all resources as RAL instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end # This tests #931. it "should not lose track of resources whose names vary" do changer = Puppet::Resource.new :file, @basepath+'/test/', :parameters => {:ensure => :directory} config = Puppet::Resource::Catalog.new('test') config.add_resource(changer) config.add_resource(@top) config.add_edge(@top, changer) catalog = config.to_ral catalog.resource("File[#{@basepath}/test/]").should equal(changer) catalog.resource("File[#{@basepath}/test]").should equal(changer) end after do # Remove all resource instances. @catalog.clear(true) end end describe "when filtering" do before :each do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @r1 = stub_everything 'r1', :ref => "File[/a]" @r1.stubs(:respond_to?).with(:ref).returns(true) @r1.stubs(:dup).returns(@r1) @r1.stubs(:is_a?).with(Puppet::Resource).returns(true) @r2 = stub_everything 'r2', :ref => "File[/b]" @r2.stubs(:respond_to?).with(:ref).returns(true) @r2.stubs(:dup).returns(@r2) @r2.stubs(:is_a?).with(Puppet::Resource).returns(true) @resources = [@r1,@r2] @original.add_resource(@r1,@r2) end it "should transform the catalog to a resource catalog" do @original.expects(:to_catalog).with { |h,b| h == :to_resource } @original.filter end it "should scan each catalog resource in turn and apply filtering block" do @resources.each { |r| r.expects(:test?) } @original.filter do |r| r.test? end end it "should filter out resources which produce true when the filter block is evaluated" do @original.filter do |r| r == @r1 end.resource("File[/a]").should be_nil end it "should not consider edges against resources that were filtered out" do @original.add_edge(@r1,@r2) @original.filter do |r| r == @r1 end.edge?(@r1,@r2).should_not be end end describe "when functioning as a resource container" do before do @catalog = Puppet::Resource::Catalog.new("host") @one = Puppet::Type.type(:notify).new :name => "one" @two = Puppet::Type.type(:notify).new :name => "two" @dupe = Puppet::Type.type(:notify).new :name => "one" end it "should provide a method to add one or more resources" do @catalog.add_resource @one, @two @catalog.resource(@one.ref).should equal(@one) @catalog.resource(@two.ref).should equal(@two) end it "should add resources to the relationship graph if it exists" do relgraph = @catalog.relationship_graph @catalog.add_resource @one relgraph.should be_vertex(@one) end it "should set itself as the resource's catalog if it is not a relationship graph" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should make all vertices available by resource reference" do @catalog.add_resource(@one) @catalog.resource(@one.ref).should equal(@one) @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) end it "should canonize how resources are referred to during retrieval when both type and title are provided" do @catalog.add_resource(@one) @catalog.resource("notify", "one").should equal(@one) end it "should canonize how resources are referred to during retrieval when just the title is provided" do @catalog.add_resource(@one) @catalog.resource("notify[one]", nil).should equal(@one) end it "should not allow two resources with the same resource reference" do @catalog.add_resource(@one) proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should not store objects that do not respond to :ref" do proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) end it "should remove all resources when asked" do @catalog.add_resource @one @catalog.add_resource @two @one.expects :remove @two.expects :remove @catalog.clear(true) end it "should support a mechanism for finishing resources" do @one.expects :finish @two.expects :finish @catalog.add_resource @one @catalog.add_resource @two @catalog.finalize end it "should make default resources when finalizing" do @catalog.expects(:make_default_resources) @catalog.finalize end it "should add default resources to the catalog upon creation" do @catalog.make_default_resources @catalog.resource(:schedule, "daily").should_not be_nil end it "should optionally support an initialization block and should finalize after such blocks" do @one.expects :finish @two.expects :finish config = Puppet::Resource::Catalog.new("host") do |conf| conf.add_resource @one conf.add_resource @two end end it "should inform the resource that it is the resource's catalog" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should be able to find resources by reference" do @catalog.add_resource @one @catalog.resource(@one.ref).should equal(@one) end it "should be able to find resources by reference or by type/title tuple" do @catalog.add_resource @one @catalog.resource("notify", "one").should equal(@one) end it "should have a mechanism for removing resources" do @catalog.add_resource @one @one.expects :remove @catalog.remove_resource(@one) @catalog.resource(@one.ref).should be_nil @catalog.vertex?(@one).should be_false end it "should have a method for creating aliases for resources" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should create aliases for isomorphic resources whose names do not match their titles" do resource = Puppet::Type::File.new(:title => "testing", :path => @basepath+"/something") @catalog.add_resource(resource) @catalog.resource(:file, @basepath+"/something").should equal(resource) end it "should not create aliases for non-isomorphic resources whose names do not match their titles" do resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) @catalog.add_resource(resource) # Yay, I've already got a 'should' method @catalog.resource(:exec, "echo").object_id.should == nil.object_id end # This test is the same as the previous, but the behaviour should be explicit. it "should alias using the class name from the resource reference, not the resource class name" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should fail to add an alias if the aliased name already exists" do @catalog.add_resource @one proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) end it "should not fail when a resource has duplicate aliases created" do @catalog.add_resource @one proc { @catalog.alias @one, "one" }.should_not raise_error end it "should not create aliases that point back to the resource" do @catalog.alias(@one, "one") @catalog.resource(:notify, "one").should be_nil end it "should be able to look resources up by their aliases" do @catalog.add_resource @one @catalog.alias @one, "two" @catalog.resource(:notify, "two").should equal(@one) end it "should remove resource aliases when the target resource is removed" do @catalog.add_resource @one @catalog.alias(@one, "other") @one.expects :remove @catalog.remove_resource(@one) @catalog.resource("notify", "other").should be_nil end it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(true) @catalog.add_resource(resource) @catalog.resource(:file, "other").should equal(resource) @catalog.resource(:file, @basepath+"/something").ref.should == resource.ref end it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(false) @catalog.add_resource(resource) @catalog.resource(:file, resource.title).should equal(resource) # We can't use .should here, because the resources respond to that method. raise "Aliased non-isomorphic resource" if @catalog.resource(:file, resource.name) end it "should provide a method to create additional resources that also registers the resource" do args = {:name => "/yay", :ensure => :file} resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" Puppet::Type.type(:file).expects(:new).with(args).returns(resource) @catalog.create_resource :file, args @catalog.resource("File[/yay]").should equal(resource) end describe "when adding resources with multiple namevars" do before :each do Puppet::Type.newtype(:multiple) do newparam(:color, :namevar => true) newparam(:designation, :namevar => true) def self.title_patterns [ [ /^(\w+) (\w+)$/, [ [:color, lambda{|x| x}], [:designation, lambda{|x| x}] ] ] ] end end end it "should add an alias using the uniqueness key" do @resource = Puppet::Type.type(:multiple).new(:title => "some resource", :color => "red", :designation => "5") @catalog.add_resource(@resource) @catalog.resource(:multiple, "some resource").must == @resource @catalog.resource("Multiple[some resource]").must == @resource @catalog.resource("Multiple[red 5]").must == @resource end it "should conflict with a resource with the same uniqueness key" do @resource = Puppet::Type.type(:multiple).new(:title => "some resource", :color => "red", :designation => "5") @other = Puppet::Type.type(:multiple).new(:title => "another resource", :color => "red", :designation => "5") @catalog.add_resource(@resource) expect { @catalog.add_resource(@other) }.to raise_error(ArgumentError, /Cannot alias Multiple\[another resource\] to \["red", "5"\].*resource \["Multiple", "red", "5"\] already declared/) end it "should conflict when its uniqueness key matches another resource's title" do path = make_absolute("/tmp/foo") @resource = Puppet::Type.type(:file).new(:title => path) @other = Puppet::Type.type(:file).new(:title => "another file", :path => path) @catalog.add_resource(@resource) expect { @catalog.add_resource(@other) }.to raise_error(ArgumentError, /Cannot alias File\[another file\] to \["#{Regexp.escape(path)}"\].*resource \["File", "#{Regexp.escape(path)}"\] already declared/) end it "should conflict when its uniqueness key matches the uniqueness key derived from another resource's title" do @resource = Puppet::Type.type(:multiple).new(:title => "red leader") @other = Puppet::Type.type(:multiple).new(:title => "another resource", :color => "red", :designation => "leader") @catalog.add_resource(@resource) expect { @catalog.add_resource(@other) }.to raise_error(ArgumentError, /Cannot alias Multiple\[another resource\] to \["red", "leader"\].*resource \["Multiple", "red", "leader"\] already declared/) end end end describe "when applying" do before :each do @catalog = Puppet::Resource::Catalog.new("host") @transaction = Puppet::Transaction.new(@catalog) Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:add_times) @transaction.stubs(:for_network_device=) Puppet.settings.stubs(:use) end it "should create and evaluate a transaction" do @transaction.expects(:evaluate) @catalog.apply end it "should provide the catalog retrieval time to the transaction" do @catalog.retrieval_duration = 5 @transaction.expects(:add_times).with(:config_retrieval => 5) @catalog.apply end it "should use a retrieval time of 0 if none is set in the catalog" do @catalog.retrieval_duration = nil @transaction.expects(:add_times).with(:config_retrieval => 0) @catalog.apply end it "should return the transaction" do @catalog.apply.should equal(@transaction) end it "should yield the transaction if a block is provided" do @catalog.apply do |trans| trans.should equal(@transaction) end end it "should default to being a host catalog" do @catalog.host_config.should be_true end it "should be able to be set to a non-host_config" do @catalog.host_config = false @catalog.host_config.should be_false end it "should pass supplied tags on to the transaction" do @transaction.expects(:tags=).with(%w{one two}) @catalog.apply(:tags => %w{one two}) end it "should set ignoreschedules on the transaction if specified in apply()" do @transaction.expects(:ignoreschedules=).with(true) @catalog.apply(:ignoreschedules => true) end describe "host catalogs" do # super() doesn't work in the setup method for some reason before do @catalog.host_config = true Puppet::Util::Storage.stubs(:store) end it "should initialize the state database before applying a catalog" do Puppet::Util::Storage.expects(:load) # Short-circuit the apply, so we know we're loading before the transaction Puppet::Transaction.expects(:new).raises ArgumentError proc { @catalog.apply }.should raise_error(ArgumentError) end it "should sync the state database after applying" do Puppet::Util::Storage.expects(:store) @transaction.stubs :any_failed? => false @catalog.apply end after { Puppet.settings.clear } end describe "non-host catalogs" do before do @catalog.host_config = false end it "should never send reports" do Puppet[:report] = true Puppet[:summarize] = true @catalog.apply end it "should never modify the state database" do Puppet::Util::Storage.expects(:load).never Puppet::Util::Storage.expects(:store).never @catalog.apply end after { Puppet.settings.clear } end end describe "when creating a relationship graph" do before do Puppet::Type.type(:component) @catalog = Puppet::Resource::Catalog.new("host") @compone = Puppet::Type::Component.new :name => "one" @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]" @file = Puppet::Type.type(:file) @one = @file.new :path => @basepath+"/one" @two = @file.new :path => @basepath+"/two" @sub = @file.new :path => @basepath+"/two/subdir" @catalog.add_edge @compone, @one @catalog.add_edge @comptwo, @two @three = @file.new :path => @basepath+"/three" @four = @file.new :path => @basepath+"/four", :require => "File[#{@basepath}/three]" @five = @file.new :path => @basepath+"/five" @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub @relationships = @catalog.relationship_graph end it "should be able to create a relationship graph" do @relationships.should be_instance_of(Puppet::SimpleGraph) end it "should not have any components" do @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil end it "should have all non-component resources from the catalog" do # The failures print out too much info, so i just do a class comparison @relationships.vertex?(@five).should be_true end it "should have all resource relationships set as edges" do @relationships.edge?(@three, @four).should be_true end it "should copy component relationships to all contained resources" do @relationships.path_between(@one, @two).should be end it "should add automatic relationships to the relationship graph" do @relationships.edge?(@two, @sub).should be_true end it "should get removed when the catalog is cleaned up" do @relationships.expects(:clear) @catalog.clear @catalog.instance_variable_get("@relationship_graph").should be_nil end it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).with(:relationships) graph.expects(:write_graph).with(:expanded_relationships) @catalog.host_config = true @catalog.relationship_graph end it "should not write graph files if the catalog is not a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).never @catalog.host_config = false @catalog.relationship_graph end it "should create a new relationship graph after clearing the old one" do @relationships.expects(:clear) @catalog.clear @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) end it "should remove removed resources from the relationship graph if it exists" do @catalog.remove_resource(@one) @catalog.relationship_graph.vertex?(@one).should be_false end end describe "when writing dot files" do before do @catalog = Puppet::Resource::Catalog.new("host") @name = :test @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") end it "should only write when it is a host catalog" do File.expects(:open).with(@file).never @catalog.host_config = false Puppet[:graph] = true @catalog.write_graph(@name) end after do Puppet.settings.clear end end describe "when indirecting" do before do @real_indirection = Puppet::Resource::Catalog.indirection @indirection = stub 'indirection', :name => :catalog end it "should use the value of the 'catalog_terminus' setting to determine its terminus class" do # Puppet only checks the terminus setting the first time you ask # so this returns the object to the clean state # at the expense of making this test less pure Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet.settings[:catalog_terminus] = "rest" Puppet::Resource::Catalog.indirection.terminus_class.should == :rest end it "should allow the terminus class to be set manually" do Puppet::Resource::Catalog.indirection.terminus_class = :rest Puppet::Resource::Catalog.indirection.terminus_class.should == :rest end after do @real_indirection.reset_terminus_class end end describe "when converting to yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") end it "should be able to be dumped to yaml" do YAML.dump(@catalog).should be_instance_of(String) end end describe "when converting from yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") text = YAML.dump(@catalog) @newcatalog = YAML.load(text) end it "should get converted back to a catalog" do @newcatalog.should be_instance_of(Puppet::Resource::Catalog) end it "should have all vertices" do @newcatalog.vertex?("one").should be_true @newcatalog.vertex?("two").should be_true end it "should have all edges" do @newcatalog.edge?("one", "two").should be_true end end end describe Puppet::Resource::Catalog, "when converting to pson", :if => Puppet.features.pson? do before do @catalog = Puppet::Resource::Catalog.new("myhost") end def pson_output_should @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its document_type to 'Catalog'" do pson_output_should { |hash| hash['document_type'] == "Catalog" } PSON.parse @catalog.to_pson end it "should set its data as a hash" do pson_output_should { |hash| hash['data'].is_a?(Hash) } PSON.parse @catalog.to_pson end [:name, :version, :tags, :classes].each do |param| it "should set its #{param} to the #{param} of the resource" do @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param) pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) } PSON.parse @catalog.to_pson end end it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]" two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]" @catalog.add_resource(one) @catalog.add_resource(two) # TODO this should really guarantee sort order PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort end it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]' two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]' three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]' @catalog.add_edge(one, two) @catalog.add_edge(two, three) @catalog.edges_between(one, two )[0].expects(:to_pson_data_hash).returns "one_two_pson" @catalog.edges_between(two, three)[0].expects(:to_pson_data_hash).returns "two_three_pson" PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort end end describe Puppet::Resource::Catalog, "when converting from pson", :if => Puppet.features.pson? do def pson_result_should Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash } end before do @data = { 'name' => "myhost" } @pson = { 'document_type' => 'Puppet::Resource::Catalog', 'data' => @data, 'metadata' => {} } @catalog = Puppet::Resource::Catalog.new("myhost") Puppet::Resource::Catalog.stubs(:new).returns @catalog end it "should be extended with the PSON utility module" do Puppet::Resource::Catalog.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end it "should create it with the provided name" do Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog PSON.parse @pson.to_pson end it "should set the provided version on the catalog if one is set" do @data['version'] = 50 PSON.parse @pson.to_pson @catalog.version.should == @data['version'] end it "should set any provided tags on the catalog" do @data['tags'] = %w{one two} PSON.parse @pson.to_pson @catalog.tags.should == @data['tags'] end it "should set any provided classes on the catalog" do @data['classes'] = %w{one two} PSON.parse @pson.to_pson @catalog.classes.should == @data['classes'] end it 'should convert the resources list into resources and add each of them' do @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")] @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert resources even if they do not include "type" information' do @data['resources'] = [Puppet::Resource.new(:file, "/foo")] @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}' @catalog.expects(:add_resource).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert the edges list into edges and add each of them' do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh") @data['edges'] = [one, two] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } @catalog.expects(:add_edge).with { |edge| edge.event == "two" } PSON.parse @pson.to_pson end it "should be able to convert relationships that do not include 'type' information" do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}" @data['edges'] = [one] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } PSON.parse @pson.to_pson end it "should set the source and target for each edge to the actual resource" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns("target_resource") @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" } PSON.parse @pson.to_pson end it "should fail if the source resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns(nil) @catalog.stubs(:resource).with("target").returns("target_resource") lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.stubs(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns(nil) lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end describe "#title_key_for_ref" do it "should parse a resource ref string into a pair" do @catalog.title_key_for_ref("Title[name]").should == ["Title", "name"] end it "should parse a resource ref string into a pair, even if there's a newline inside the name" do @catalog.title_key_for_ref("Title[na\nme]").should == ["Title", "na\nme"] end end end diff --git a/spec/unit/resource/status_spec.rb b/spec/unit/resource/status_spec.rb index 967708432..953f06a26 100755 --- a/spec/unit/resource/status_spec.rb +++ b/spec/unit/resource/status_spec.rb @@ -1,154 +1,154 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/status' describe Puppet::Resource::Status do include PuppetSpec::Files before do @resource = Puppet::Type.type(:file).new :path => make_absolute("/my/file") @status = Puppet::Resource::Status.new(@resource) end it "should compute type and title correctly" do @status.resource_type.should == "File" @status.title.should == make_absolute("/my/file") end [:node, :file, :line, :current_values, :status, :evaluation_time].each do |attr| it "should support #{attr}" do @status.send(attr.to_s + "=", "foo") @status.send(attr).should == "foo" end end [:skipped, :failed, :restarted, :failed_to_restart, :changed, :out_of_sync, :scheduled].each do |attr| it "should support #{attr}" do @status.send(attr.to_s + "=", "foo") @status.send(attr).should == "foo" end it "should have a boolean method for determining whehter it was #{attr}" do @status.send(attr.to_s + "=", "foo") @status.should send("be_#{attr}") end end it "should accept a resource at initialization" do Puppet::Resource::Status.new(@resource).resource.should_not be_nil end it "should set its source description to the resource's path" do @resource.expects(:path).returns "/my/path" Puppet::Resource::Status.new(@resource).source_description.should == "/my/path" end [:file, :line].each do |attr| it "should copy the resource's #{attr}" do @resource.expects(attr).returns "foo" Puppet::Resource::Status.new(@resource).send(attr).should == "foo" end end it "should copy the resource's tags" do @resource.expects(:tags).returns %w{foo bar} Puppet::Resource::Status.new(@resource).tags.should == %w{foo bar} end it "should always convert the resource to a string" do @resource.expects(:to_s).returns "foo" Puppet::Resource::Status.new(@resource).resource.should == "foo" end it "should support tags" do Puppet::Resource::Status.ancestors.should include(Puppet::Util::Tagging) end it "should create a timestamp at its creation time" do @status.time.should be_instance_of(Time) end describe "when sending logs" do before do Puppet::Util::Log.stubs(:new) end it "should set the tags to the event tags" do Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } @status.stubs(:tags).returns %w{one two} @status.send_log :notice, "my message" end [:file, :line].each do |attr| it "should pass the #{attr}" do Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } @status.send(attr.to_s + "=", "my val") @status.send_log :notice, "my message" end end it "should use the source description as the source" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "my source" } @status.stubs(:source_description).returns "my source" @status.send_log :notice, "my message" end end it "should support adding events" do event = Puppet::Transaction::Event.new(:name => :foobar) @status.add_event(event) @status.events.should == [event] end it "should use '<<' to add events" do event = Puppet::Transaction::Event.new(:name => :foobar) (@status << event).should equal(@status) @status.events.should == [event] end it "should count the number of successful events and set changed" do 3.times{ @status << Puppet::Transaction::Event.new(:status => 'success') } @status.change_count.should == 3 @status.changed.should == true @status.out_of_sync.should == true end it "should not start with any changes" do @status.change_count.should == 0 @status.changed.should == false @status.out_of_sync.should == false end it "should not treat failure, audit, or noop events as changed" do ['failure', 'audit', 'noop'].each do |s| @status << Puppet::Transaction::Event.new(:status => s) end @status.change_count.should == 0 @status.changed.should == false end it "should not treat audit events as out of sync" do @status << Puppet::Transaction::Event.new(:status => 'audit') @status.out_of_sync_count.should == 0 @status.out_of_sync.should == false end ['failure', 'noop', 'success'].each do |event_status| it "should treat #{event_status} events as out of sync" do 3.times do @status << Puppet::Transaction::Event.new(:status => event_status) end @status.out_of_sync_count.should == 3 @status.out_of_sync.should == true end end describe "When converting to YAML" do it "should include only documented attributes" do @status.file = "/foo.rb" @status.line = 27 @status.evaluation_time = 2.7 @status.tags = %w{one two} @status.to_yaml_properties.should =~ Puppet::Resource::Status::YAML_ATTRIBUTES end end end diff --git a/spec/unit/resource/type_collection_helper_spec.rb b/spec/unit/resource/type_collection_helper_spec.rb index ad8d75271..5fac3173f 100755 --- a/spec/unit/resource/type_collection_helper_spec.rb +++ b/spec/unit/resource/type_collection_helper_spec.rb @@ -1,24 +1,24 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/type_collection_helper' class RTCHelperTester include Puppet::Resource::TypeCollectionHelper end describe Puppet::Resource::TypeCollectionHelper do before do @helper = RTCHelperTester.new end it "should use its current environment to retrieve the known resource type collection" do env = stub 'environment' @helper.expects(:environment).returns env rtc = stub 'known_resource_types' env.expects(:known_resource_types).returns rtc @helper.known_resource_types.should equal(rtc) end end diff --git a/spec/unit/resource/type_collection_spec.rb b/spec/unit/resource/type_collection_spec.rb index c812fa789..7a7ad41f9 100755 --- a/spec/unit/resource/type_collection_spec.rb +++ b/spec/unit/resource/type_collection_spec.rb @@ -1,454 +1,454 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/type_collection' require 'puppet/resource/type' describe Puppet::Resource::TypeCollection do include PuppetSpec::Files before do @instance = Puppet::Resource::Type.new(:hostclass, "foo") @code = Puppet::Resource::TypeCollection.new("env") end it "should require an environment at initialization" do env = Puppet::Node::Environment.new("testing") Puppet::Resource::TypeCollection.new(env).environment.should equal(env) end it "should convert the environment into an environment instance if a string is provided" do env = Puppet::Node::Environment.new("testing") Puppet::Resource::TypeCollection.new("testing").environment.should equal(env) end it "should create a 'loader' at initialization" do Puppet::Resource::TypeCollection.new("testing").loader.should be_instance_of(Puppet::Parser::TypeLoader) end it "should be able to add a resource type" do Puppet::Resource::TypeCollection.new("env").should respond_to(:add) end it "should consider '<<' to be an alias to 'add' but should return self" do loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:add).with "foo" loader.expects(:add).with "bar" loader << "foo" << "bar" end it "should set itself as the code collection for added resource types" do loader = Puppet::Resource::TypeCollection.new("env") node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) node.resource_type_collection.should equal(@code) end it "should store node resource types as nodes" do node = Puppet::Resource::Type.new(:node, "foo") @code.add(node) @code.node("foo").should equal(node) end it "should store hostclasses as hostclasses" do klass = Puppet::Resource::Type.new(:hostclass, "foo") @code.add(klass) @code.hostclass("foo").should equal(klass) end it "should store definitions as definitions" do define = Puppet::Resource::Type.new(:definition, "foo") @code.add(define) @code.definition("foo").should equal(define) end it "should merge new classes with existing classes of the same name" do loader = Puppet::Resource::TypeCollection.new("env") first = Puppet::Resource::Type.new(:hostclass, "foo") second = Puppet::Resource::Type.new(:hostclass, "foo") loader.add first first.expects(:merge).with(second) loader.add(second) end it "should remove all nodes, classes, and definitions when cleared" do loader = Puppet::Resource::TypeCollection.new("env") loader.add Puppet::Resource::Type.new(:hostclass, "class") loader.add Puppet::Resource::Type.new(:definition, "define") loader.add Puppet::Resource::Type.new(:node, "node") watched_file = tmpfile('watched_file') loader.watch_file(watched_file) loader.clear loader.hostclass("class").should be_nil loader.definition("define").should be_nil loader.node("node").should be_nil loader.should_not be_watching_file(watched_file) end describe "when resolving namespaces" do [ ['', '::foo', ['foo']], ['a', '::foo', ['foo']], ['a::b', '::foo', ['foo']], [['a::b'], '::foo', ['foo']], [['a::b', 'c'], '::foo', ['foo']], [['A::B', 'C'], '::Foo', ['foo']], ['', '', ['']], ['a', '', ['']], ['a::b', '', ['']], [['a::b'], '', ['']], [['a::b', 'c'], '', ['']], [['A::B', 'C'], '', ['']], ['', 'foo', ['foo']], ['a', 'foo', ['a::foo', 'foo']], ['a::b', 'foo', ['a::b::foo', 'a::foo', 'foo']], ['A::B', 'Foo', ['a::b::foo', 'a::foo', 'foo']], [['a::b'], 'foo', ['a::b::foo', 'a::foo', 'foo']], [['a', 'b'], 'foo', ['a::foo', 'foo', 'b::foo']], [['a::b', 'c::d'], 'foo', ['a::b::foo', 'a::foo', 'foo', 'c::d::foo', 'c::foo']], [['a::b', 'a::c'], 'foo', ['a::b::foo', 'a::foo', 'foo', 'a::c::foo']], ].each do |namespaces, name, expected_result| it "should resolve #{name.inspect} in namespaces #{namespaces.inspect} correctly" do @code.instance_eval { resolve_namespaces(namespaces, name) }.should == expected_result end end end describe "when looking up names" do before do @type = Puppet::Resource::Type.new(:hostclass, "ns::klass") end it "should support looking up with multiple namespaces" do @code.add @type @code.find_hostclass(%w{boo baz ns}, "klass").should equal(@type) end it "should not attempt to import anything when the type is already defined" do @code.add @type @code.loader.expects(:import).never @code.find_hostclass(%w{ns}, "klass").should equal(@type) end describe "that need to be loaded" do it "should use the loader to load the files" do @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass") @code.loader.expects(:try_load_fqname).with(:hostclass, "klass") @code.find_hostclass(["ns"], "klass") end it "should downcase the name and downcase and array-fy the namespaces before passing to the loader" do @code.loader.expects(:try_load_fqname).with(:hostclass, "ns::klass") @code.loader.expects(:try_load_fqname).with(:hostclass, "klass") @code.find_hostclass("Ns", "Klass") end it "should use the class returned by the loader" do @code.loader.expects(:try_load_fqname).returns(:klass) @code.expects(:hostclass).with("ns::klass").returns(false) @code.find_hostclass("ns", "klass").should == :klass end it "should return nil if the name isn't found" do @code.stubs(:try_load_fqname).returns(nil) @code.find_hostclass("Ns", "Klass").should be_nil end it "already-loaded names at broader scopes should not shadow autoloaded names" do @code.add Puppet::Resource::Type.new(:hostclass, "bar") @code.loader.expects(:try_load_fqname).with(:hostclass, "foo::bar").returns(:foobar) @code.find_hostclass("foo", "bar").should == :foobar end end end %w{hostclass node definition}.each do |data| before do @instance = Puppet::Resource::Type.new(data, "foo") end it "should have a method for adding a #{data}" do Puppet::Resource::TypeCollection.new("env").should respond_to("add_#{data}") end it "should use the name of the instance to add it" do loader = Puppet::Resource::TypeCollection.new("env") loader.send("add_#{data}", @instance) loader.send(data, @instance.name).should equal(@instance) end unless data == "hostclass" it "should fail to add a #{data} when one already exists" do loader = Puppet::Resource::TypeCollection.new("env") loader.add @instance lambda { loader.add(@instance) }.should raise_error(Puppet::ParseError) end end it "should return the added #{data}" do loader = Puppet::Resource::TypeCollection.new("env") loader.add(@instance).should equal(@instance) end it "should be able to retrieve #{data} by name" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(data, "bar") loader.add instance loader.send(data, "bar").should equal(instance) end it "should retrieve #{data} insensitive to case" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(data, "Bar") loader.add instance loader.send(data, "bAr").should equal(instance) end it "should return nil when asked for a #{data} that has not been added" do Puppet::Resource::TypeCollection.new("env").send(data, "foo").should be_nil end it "should be able to retrieve all #{data}s" do plurals = { "hostclass" => "hostclasses", "node" => "nodes", "definition" => "definitions" } loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(data, "foo") loader.add instance loader.send(plurals[data]).should == { "foo" => instance } end end describe "when finding a qualified instance" do it "should return any found instance if the instance name is fully qualified" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("namespace", "::foo::bar").should equal(instance) end it "should return nil if the instance name is fully qualified and no such instance exists" do loader = Puppet::Resource::TypeCollection.new("env") loader.find_hostclass("namespace", "::foo::bar").should be_nil end it "should be able to find classes in the base namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo") loader.add instance loader.find_hostclass("", "foo").should equal(instance) end it "should return the partially qualified object if it exists in a provided namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo", "bar::baz").should equal(instance) end it "should be able to find partially qualified objects in any of the provided namespaces" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass(["nons", "foo", "otherns"], "bar::baz").should equal(instance) end it "should return the unqualified object if it exists in a provided namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo", "bar").should equal(instance) end it "should return the unqualified object if it exists in the parent namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar") loader.add instance loader.find_hostclass("foo::bar::baz", "bar").should equal(instance) end it "should should return the partially qualified object if it exists in the parent namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "bar::baz").should equal(instance) end it "should return the qualified object if it exists in the root namespace" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "foo::bar::baz").should equal(instance) end it "should return nil if the object cannot be found" do loader = Puppet::Resource::TypeCollection.new("env") instance = Puppet::Resource::Type.new(:hostclass, "foo::bar::baz") loader.add instance loader.find_hostclass("foo::bar", "eh").should be_nil end describe "when topscope has a class that has the same name as a local class" do before do @loader = Puppet::Resource::TypeCollection.new("env") [ "foo::bar", "bar" ].each do |name| @loader.add Puppet::Resource::Type.new(:hostclass, name) end end it "should favor the local class, if the name is unqualified" do @loader.find_hostclass("foo", "bar").name.should == 'foo::bar' end it "should only look in the topclass, if the name is qualified" do @loader.find_hostclass("foo", "::bar").name.should == 'bar' end it "should only look in the topclass, if we assume the name is fully qualified" do @loader.find_hostclass("foo", "bar", :assume_fqname => true).name.should == 'bar' end end it "should not look in the local scope for classes when the name is qualified" do @loader = Puppet::Resource::TypeCollection.new("env") @loader.add Puppet::Resource::Type.new(:hostclass, "foo::bar") @loader.find_hostclass("foo", "::bar").should == nil end end it "should be able to find nodes" do node = Puppet::Resource::Type.new(:node, "bar") loader = Puppet::Resource::TypeCollection.new("env") loader.add(node) loader.find_node(stub("ignored"), "bar").should == node end it "should use the 'find_or_load' method to find hostclasses" do loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find_or_load).with("foo", "bar", :hostclass, {}) loader.find_hostclass("foo", "bar") end it "should use the 'find_or_load' method to find definitions" do loader = Puppet::Resource::TypeCollection.new("env") loader.expects(:find_or_load).with("foo", "bar", :definition) loader.find_definition("foo", "bar") end it "should indicate whether any nodes are defined" do loader = Puppet::Resource::TypeCollection.new("env") loader.add_node(Puppet::Resource::Type.new(:node, "foo")) loader.should be_nodes end it "should indicate whether no nodes are defined" do Puppet::Resource::TypeCollection.new("env").should_not be_nodes end describe "when finding nodes" do before :each do @loader = Puppet::Resource::TypeCollection.new("env") end it "should return any node whose name exactly matches the provided node name" do node = Puppet::Resource::Type.new(:node, "foo") @loader << node @loader.node("foo").should equal(node) end it "should return the first regex node whose regex matches the provided node name" do node1 = Puppet::Resource::Type.new(:node, /\w/) node2 = Puppet::Resource::Type.new(:node, /\d/) @loader << node1 << node2 @loader.node("foo10").should equal(node1) end it "should preferentially return a node whose name is string-equal over returning a node whose regex matches a provided name" do node1 = Puppet::Resource::Type.new(:node, /\w/) node2 = Puppet::Resource::Type.new(:node, "foo") @loader << node1 << node2 @loader.node("foo").should equal(node2) end end describe "when managing files" do before do @loader = Puppet::Resource::TypeCollection.new("env") Puppet::Util::LoadedFile.stubs(:new).returns stub("watched_file") end it "should have a method for specifying a file should be watched" do @loader.should respond_to(:watch_file) end it "should have a method for determining if a file is being watched" do @loader.watch_file("/foo/bar") @loader.should be_watching_file("/foo/bar") end it "should use LoadedFile to watch files" do Puppet::Util::LoadedFile.expects(:new).with("/foo/bar").returns stub("watched_file") @loader.watch_file("/foo/bar") end it "should be considered stale if any files have changed" do file1 = stub 'file1', :changed? => false file2 = stub 'file2', :changed? => true Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) @loader.watch_file("/foo/bar") @loader.watch_file("/other/bar") @loader.should be_stale end it "should not be considered stable if no files have changed" do file1 = stub 'file1', :changed? => false file2 = stub 'file2', :changed? => false Puppet::Util::LoadedFile.expects(:new).times(2).returns(file1).then.returns(file2) @loader.watch_file("/foo/bar") @loader.watch_file("/other/bar") @loader.should_not be_stale end end describe "when determining the configuration version" do before do @code = Puppet::Resource::TypeCollection.new("env") end it "should default to the current time" do time = Time.now Time.stubs(:now).returns time @code.version.should == time.to_i end it "should use the output of the environment's config_version setting if one is provided" do @code.environment.stubs(:[]).with(:config_version).returns("/my/foo") Puppet::Util::Execution.expects(:execute).with(["/my/foo"]).returns "output\n" @code.version.should == "output" end it "should raise a puppet parser error if executing config_version fails" do @code.environment.stubs(:[]).with(:config_version).returns("test") Puppet::Util::Execution.expects(:execute).raises(Puppet::ExecutionFailure.new("msg")) lambda { @code.version }.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb index d1987af61..6887c1d24 100755 --- a/spec/unit/resource/type_spec.rb +++ b/spec/unit/resource/type_spec.rb @@ -1,766 +1,766 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource/type' describe Puppet::Resource::Type do it "should have a 'name' attribute" do Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo" end [:code, :doc, :line, :file, :resource_type_collection, :ruby_code].each do |attr| it "should have a '#{attr}' attribute" do type = Puppet::Resource::Type.new(:hostclass, "foo") type.send(attr.to_s + "=", "yay") type.send(attr).should == "yay" end end [:hostclass, :node, :definition].each do |type| it "should know when it is a #{type}" do Puppet::Resource::Type.new(type, "foo").send("#{type}?").should be_true end end it "should indirect 'resource_type'" do Puppet::Resource::Type.indirection.name.should == :resource_type end it "should default to 'parser' for its terminus class" do Puppet::Resource::Type.indirection.terminus_class.should == :parser end describe "when converting to json" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo") end def from_json(json) Puppet::Resource::Type.from_pson(json) end def double_convert Puppet::Resource::Type.from_pson(PSON.parse(@type.to_pson)) end it "should include the name and type" do double_convert.name.should == @type.name double_convert.type.should == @type.type end it "should include any arguments" do @type.set_arguments("one" => nil, "two" => "foo") double_convert.arguments.should == {"one" => nil, "two" => "foo"} end it "should not include arguments if none are present" do @type.to_pson["arguments"].should be_nil end [:line, :doc, :file, :parent].each do |attr| it "should include #{attr} when set" do @type.send(attr.to_s + "=", "value") double_convert.send(attr).should == "value" end it "should not include #{attr} when not set" do @type.to_pson[attr.to_s].should be_nil end end it "should not include docs if they are empty" do @type.doc = "" @type.to_pson["doc"].should be_nil end end describe "when a node" do it "should allow a regex as its name" do lambda { Puppet::Resource::Type.new(:node, /foo/) }.should_not raise_error end it "should allow a AST::HostName instance as its name" do regex = Puppet::Parser::AST::Regex.new(:value => /foo/) name = Puppet::Parser::AST::HostName.new(:value => regex) lambda { Puppet::Resource::Type.new(:node, name) }.should_not raise_error end it "should match against the regexp in the AST::HostName when a HostName instance is provided" do regex = Puppet::Parser::AST::Regex.new(:value => /\w/) name = Puppet::Parser::AST::HostName.new(:value => regex) node = Puppet::Resource::Type.new(:node, name) node.match("foo").should be_true end it "should return the value of the hostname if provided a string-form AST::HostName instance as the name" do name = Puppet::Parser::AST::HostName.new(:value => "foo") node = Puppet::Resource::Type.new(:node, name) node.name.should == "foo" end describe "and the name is a regex" do it "should have a method that indicates that this is the case" do Puppet::Resource::Type.new(:node, /w/).should be_name_is_regex end it "should set its namespace to ''" do Puppet::Resource::Type.new(:node, /w/).namespace.should == "" end it "should return the regex converted to a string when asked for its name" do Puppet::Resource::Type.new(:node, /ww/).name.should == "ww" end it "should downcase the regex when returning the name as a string" do Puppet::Resource::Type.new(:node, /W/).name.should == "w" end it "should remove non-alpha characters when returning the name as a string" do Puppet::Resource::Type.new(:node, /w*w/).name.should_not include("*") end it "should remove leading dots when returning the name as a string" do Puppet::Resource::Type.new(:node, /.ww/).name.should_not =~ /^\./ end it "should have a method for matching its regex name against a provided name" do Puppet::Resource::Type.new(:node, /.ww/).should respond_to(:match) end it "should return true when its regex matches the provided name" do Puppet::Resource::Type.new(:node, /\w/).match("foo").should be_true end it "should return false when its regex does not match the provided name" do (!!Puppet::Resource::Type.new(:node, /\d/).match("foo")).should be_false end it "should return true when its name, as a string, is matched against an equal string" do Puppet::Resource::Type.new(:node, "foo").match("foo").should be_true end it "should return false when its name is matched against an unequal string" do Puppet::Resource::Type.new(:node, "foo").match("bar").should be_false end it "should match names insensitive to case" do Puppet::Resource::Type.new(:node, "fOo").match("foO").should be_true end end it "should return the name converted to a string when the name is not a regex" do pending "Need to define LoadedCode behaviour first" name = Puppet::Parser::AST::HostName.new(:value => "foo") Puppet::Resource::Type.new(:node, name).name.should == "foo" end it "should return the name converted to a string when the name is a regex" do pending "Need to define LoadedCode behaviour first" name = Puppet::Parser::AST::HostName.new(:value => /regex/) Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s end it "should mark any created scopes as a node scope" do pending "Need to define LoadedCode behaviour first" name = Puppet::Parser::AST::HostName.new(:value => /regex/) Puppet::Resource::Type.new(:node, name).name.should == /regex/.to_s end end describe "when initializing" do it "should require a resource super type" do Puppet::Resource::Type.new(:hostclass, "foo").type.should == :hostclass end it "should fail if provided an invalid resource super type" do lambda { Puppet::Resource::Type.new(:nope, "foo") }.should raise_error(ArgumentError) end it "should set its name to the downcased, stringified provided name" do Puppet::Resource::Type.new(:hostclass, "Foo::Bar".intern).name.should == "foo::bar" end it "should set its namespace to the downcased, stringified qualified name for classes" do Puppet::Resource::Type.new(:hostclass, "Foo::Bar::Baz".intern).namespace.should == "foo::bar::baz" end [:definition, :node].each do |type| it "should set its namespace to the downcased, stringified qualified portion of the name for #{type}s" do Puppet::Resource::Type.new(type, "Foo::Bar::Baz".intern).namespace.should == "foo::bar" end end %w{code line file doc}.each do |arg| it "should set #{arg} if provided" do type = Puppet::Resource::Type.new(:hostclass, "foo", arg.to_sym => "something") type.send(arg).should == "something" end end it "should set any provided arguments with the keys as symbols" do type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {:foo => "bar", :baz => "biz"}) type.should be_valid_parameter("foo") type.should be_valid_parameter("baz") end it "should set any provided arguments with they keys as strings" do type = Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar", "baz" => "biz"}) type.should be_valid_parameter(:foo) type.should be_valid_parameter(:baz) end it "should function if provided no arguments" do type = Puppet::Resource::Type.new(:hostclass, "foo") type.should_not be_valid_parameter(:foo) end end describe "when testing the validity of an attribute" do it "should return true if the parameter was typed at initialization" do Puppet::Resource::Type.new(:hostclass, "foo", :arguments => {"foo" => "bar"}).should be_valid_parameter("foo") end it "should return true if it is a metaparam" do Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("require") end it "should return true if the parameter is named 'name'" do Puppet::Resource::Type.new(:hostclass, "foo").should be_valid_parameter("name") end it "should return false if it is not a metaparam and was not provided at initialization" do Puppet::Resource::Type.new(:hostclass, "foo").should_not be_valid_parameter("yayness") end end describe "when setting its parameters in the scope" do before do @scope = Puppet::Parser::Scope.new(:compiler => Puppet::Parser::Compiler.new(Puppet::Node.new("foo")), :source => stub("source")) @resource = Puppet::Parser::Resource.new(:foo, "bar", :scope => @scope) @type = Puppet::Resource::Type.new(:definition, "foo") @resource.environment.known_resource_types.add @type end ['module_name', 'name', 'title'].each do |variable| it "should allow #{variable} to be evaluated as param default" do @type.instance_eval { @module_name = "bar" } var = Puppet::Parser::AST::Variable.new({'value' => variable}) @type.set_arguments :foo => var @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == 'bar' end end # this test is to clarify a crazy edge case # if you specify these special names as params, the resource # will override the special variables it "should allow the resource to override defaults" do @type.set_arguments :name => nil @resource[:name] = 'foobar' var = Puppet::Parser::AST::Variable.new({'value' => 'name'}) @type.set_arguments :foo => var @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == 'foobar' end it "should set each of the resource's parameters as variables in the scope" do @type.set_arguments :foo => nil, :boo => nil @resource[:foo] = "bar" @resource[:boo] = "baz" @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == "bar" @scope['boo'].should == "baz" end it "should set the variables as strings" do @type.set_arguments :foo => nil @resource[:foo] = "bar" @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == "bar" end it "should fail if any of the resource's parameters are not valid attributes" do @type.set_arguments :foo => nil @resource[:boo] = "baz" lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) end it "should evaluate and set its default values as variables for parameters not provided by the resource" do @type.set_arguments :foo => Puppet::Parser::AST::String.new(:value => "something") @type.set_resource_parameters(@resource, @scope) @scope['foo'].should == "something" end it "should set all default values as parameters in the resource" do @type.set_arguments :foo => Puppet::Parser::AST::String.new(:value => "something") @type.set_resource_parameters(@resource, @scope) @resource[:foo].should == "something" end it "should fail if the resource does not provide a value for a required argument" do @type.set_arguments :foo => nil lambda { @type.set_resource_parameters(@resource, @scope) }.should raise_error(Puppet::ParseError) end it "should set the resource's title as a variable if not otherwise provided" do @type.set_resource_parameters(@resource, @scope) @scope['title'].should == "bar" end it "should set the resource's name as a variable if not otherwise provided" do @type.set_resource_parameters(@resource, @scope) @scope['name'].should == "bar" end it "should set its module name in the scope if available" do @type.instance_eval { @module_name = "mymod" } @type.set_resource_parameters(@resource, @scope) @scope["module_name"].should == "mymod" end it "should set its caller module name in the scope if available" do @scope.expects(:parent_module_name).returns "mycaller" @type.set_resource_parameters(@resource, @scope) @scope["caller_module_name"].should == "mycaller" end end describe "when describing and managing parent classes" do before do @krt = Puppet::Node::Environment.new.known_resource_types @parent = Puppet::Resource::Type.new(:hostclass, "bar") @krt.add @parent @child = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") @krt.add @child @scope = Puppet::Parser::Scope.new end it "should be able to define a parent" do Puppet::Resource::Type.new(:hostclass, "foo", :parent => "bar") end it "should use the code collection to find the parent resource type" do @child.parent_type(@scope).should equal(@parent) end it "should be able to find parent nodes" do parent = Puppet::Resource::Type.new(:node, "bar") @krt.add parent child = Puppet::Resource::Type.new(:node, "foo", :parent => "bar") @krt.add child child.parent_type(@scope).should equal(parent) end it "should cache a reference to the parent type" do @krt.stubs(:hostclass).with("foo::bar").returns nil @krt.expects(:hostclass).with("bar").once.returns @parent @child.parent_type(@scope) @child.parent_type end it "should correctly state when it is another type's child" do @child.parent_type(@scope) @child.should be_child_of(@parent) end it "should be considered the child of a parent's parent" do @grandchild = Puppet::Resource::Type.new(:hostclass, "baz", :parent => "foo") @krt.add @grandchild @child.parent_type(@scope) @grandchild.parent_type(@scope) @grandchild.should be_child_of(@parent) end it "should correctly state when it is not another type's child" do @notchild = Puppet::Resource::Type.new(:hostclass, "baz") @krt.add @notchild @notchild.should_not be_child_of(@parent) end end describe "when evaluating its code" do before do @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode")) @scope = Puppet::Parser::Scope.new :compiler => @compiler @resource = Puppet::Parser::Resource.new(:class, "foo", :scope => @scope) # This is so the internal resource lookup works, yo. @compiler.catalog.add_resource @resource @type = Puppet::Resource::Type.new(:hostclass, "foo") @resource.environment.known_resource_types.add @type end it "should add hostclass names to the classes list" do @type.evaluate_code(@resource) @compiler.catalog.classes.should be_include("foo") end it "should add node names to the classes list" do @type = Puppet::Resource::Type.new(:node, "foo") @type.evaluate_code(@resource) @compiler.catalog.classes.should be_include("foo") end it "should not add defined resource names to the classes list" do @type = Puppet::Resource::Type.new(:definition, "foo") @type.evaluate_code(@resource) @compiler.catalog.classes.should_not be_include("foo") end it "should set all of its parameters in a subscope" do subscope = stub 'subscope', :compiler => @compiler @scope.expects(:newscope).with(:source => @type, :namespace => 'foo', :resource => @resource).returns subscope @type.expects(:set_resource_parameters).with(@resource, subscope) @type.evaluate_code(@resource) end it "should not create a subscope for the :main class" do @resource.stubs(:title).returns(:main) @type.expects(:subscope).never @type.expects(:set_resource_parameters).with(@resource, @scope) @type.evaluate_code(@resource) end it "should store the class scope" do @type.evaluate_code(@resource) @scope.class_scope(@type).should be_instance_of(@scope.class) end it "should still create a scope but not store it if the type is a definition" do @type = Puppet::Resource::Type.new(:definition, "foo") @type.evaluate_code(@resource) @scope.class_scope(@type).should be_nil end it "should evaluate the AST code if any is provided" do code = stub 'code' @type.stubs(:code).returns code subscope = stub_everything("subscope", :compiler => @compiler) @scope.stubs(:newscope).returns subscope code.expects(:safeevaluate).with subscope @type.evaluate_code(@resource) end describe "and ruby code is provided" do it "should create a DSL Resource API and evaluate it" do @type.stubs(:ruby_code).returns(proc { "foo" }) @api = stub 'api' Puppet::DSL::ResourceAPI.expects(:new).with { |res, scope, code| code == @type.ruby_code }.returns @api @api.expects(:evaluate) @type.evaluate_code(@resource) end end it "should noop if there is no code" do @type.expects(:code).returns nil @type.evaluate_code(@resource) end describe "and it has a parent class" do before do @parent_type = Puppet::Resource::Type.new(:hostclass, "parent") @type.parent = "parent" @parent_resource = Puppet::Parser::Resource.new(:class, "parent", :scope => @scope) @compiler.add_resource @scope, @parent_resource @type.resource_type_collection = @scope.known_resource_types @type.resource_type_collection.add @parent_type end it "should evaluate the parent's resource" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil end it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @type.evaluate_code(@resource) end it "should use the parent's scope as its base scope" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id end end describe "and it has a parent node" do before do @type = Puppet::Resource::Type.new(:node, "foo") @parent_type = Puppet::Resource::Type.new(:node, "parent") @type.parent = "parent" @parent_resource = Puppet::Parser::Resource.new(:node, "parent", :scope => @scope) @compiler.add_resource @scope, @parent_resource @type.resource_type_collection = @scope.known_resource_types @type.resource_type_collection.add(@parent_type) end it "should evaluate the parent's resource" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@parent_type).should_not be_nil end it "should not evaluate the parent's resource if it has already been evaluated" do @parent_resource.evaluate @type.parent_type(@scope) @parent_resource.expects(:evaluate).never @type.evaluate_code(@resource) end it "should use the parent's scope as its base scope" do @type.parent_type(@scope) @type.evaluate_code(@resource) @scope.class_scope(@type).parent.object_id.should == @scope.class_scope(@parent_type).object_id end end end describe "when creating a resource" do before do @node = Puppet::Node.new("foo", :environment => 'env') @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(:compiler => @compiler) @top = Puppet::Resource::Type.new :hostclass, "top" @middle = Puppet::Resource::Type.new :hostclass, "middle", :parent => "top" @code = Puppet::Resource::TypeCollection.new("env") @code.add @top @code.add @middle @node.environment.stubs(:known_resource_types).returns(@code) end it "should create a resource instance" do @top.ensure_in_catalog(@scope).should be_instance_of(Puppet::Parser::Resource) end it "should set its resource type to 'class' when it is a hostclass" do Puppet::Resource::Type.new(:hostclass, "top").ensure_in_catalog(@scope).type.should == "Class" end it "should set its resource type to 'node' when it is a node" do Puppet::Resource::Type.new(:node, "top").ensure_in_catalog(@scope).type.should == "Node" end it "should fail when it is a definition" do lambda { Puppet::Resource::Type.new(:definition, "top").ensure_in_catalog(@scope) }.should raise_error(ArgumentError) end it "should add the created resource to the scope's catalog" do @top.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should add specified parameters to the resource" do @top.ensure_in_catalog(@scope, {'one'=>'1', 'two'=>'2'}) @compiler.catalog.resource(:class, "top")['one'].should == '1' @compiler.catalog.resource(:class, "top")['two'].should == '2' end it "should not require params for a param class" do @top.ensure_in_catalog(@scope, {}) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should evaluate the parent class if one exists" do @middle.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should evaluate the parent class if one exists" do @middle.ensure_in_catalog(@scope, {}) @compiler.catalog.resource(:class, "top").should be_instance_of(Puppet::Parser::Resource) end it "should fail if you try to create duplicate class resources" do othertop = Puppet::Parser::Resource.new(:class, 'top',:source => @source, :scope => @scope ) # add the same class resource to the catalog @compiler.catalog.add_resource(othertop) lambda { @top.ensure_in_catalog(@scope, {}) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should fail to evaluate if a parent class is defined but cannot be found" do othertop = Puppet::Resource::Type.new :hostclass, "something", :parent => "yay" @code.add othertop lambda { othertop.ensure_in_catalog(@scope) }.should raise_error(Puppet::ParseError) end it "should not create a new resource if one already exists" do @compiler.catalog.expects(:resource).with(:class, "top").returns("something") @compiler.catalog.expects(:add_resource).never @top.ensure_in_catalog(@scope) end it "should return the existing resource when not creating a new one" do @compiler.catalog.expects(:resource).with(:class, "top").returns("something") @compiler.catalog.expects(:add_resource).never @top.ensure_in_catalog(@scope).should == "something" end it "should not create a new parent resource if one already exists and it has a parent class" do @top.ensure_in_catalog(@scope) top_resource = @compiler.catalog.resource(:class, "top") @middle.ensure_in_catalog(@scope) @compiler.catalog.resource(:class, "top").should equal(top_resource) end # #795 - tag before evaluation. it "should tag the catalog with the resource tags when it is evaluated" do @middle.ensure_in_catalog(@scope) @compiler.catalog.should be_tagged("middle") end it "should tag the catalog with the parent class tags when it is evaluated" do @middle.ensure_in_catalog(@scope) @compiler.catalog.should be_tagged("top") end end describe "when merging code from another instance" do def code(str) Puppet::Parser::AST::Leaf.new :value => str end it "should fail unless it is a class" do lambda { Puppet::Resource::Type.new(:node, "bar").merge("foo") }.should raise_error(Puppet::Error) end it "should fail unless the source instance is a class" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:node, "foo") lambda { dest.merge(source) }.should raise_error(Puppet::Error) end it "should fail if both classes have different parent classes" do code = Puppet::Resource::TypeCollection.new("env") {"a" => "b", "c" => "d"}.each do |parent, child| code.add Puppet::Resource::Type.new(:hostclass, parent) code.add Puppet::Resource::Type.new(:hostclass, child, :parent => parent) end lambda { code.hostclass("b").merge(code.hostclass("d")) }.should raise_error(Puppet::Error) end it "should fail if it's named 'main' and 'freeze_main' is enabled" do Puppet.settings[:freeze_main] = true code = Puppet::Resource::TypeCollection.new("env") code.add Puppet::Resource::Type.new(:hostclass, "") other = Puppet::Resource::Type.new(:hostclass, "") lambda { code.hostclass("").merge(other) }.should raise_error(Puppet::Error) end it "should copy the other class's parent if it has not parent" do dest = Puppet::Resource::Type.new(:hostclass, "bar") parent = Puppet::Resource::Type.new(:hostclass, "parent") source = Puppet::Resource::Type.new(:hostclass, "foo", :parent => "parent") dest.merge(source) dest.parent.should == "parent" end it "should copy the other class's documentation as its docs if it has no docs" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") dest.merge(source) dest.doc.should == "yayness" end it "should append the other class's docs to its docs if it has any" do dest = Puppet::Resource::Type.new(:hostclass, "bar", :doc => "fooness") source = Puppet::Resource::Type.new(:hostclass, "foo", :doc => "yayness") dest.merge(source) dest.doc.should == "foonessyayness" end it "should turn its code into an ASTArray if necessary" do dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => code("foo")) source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) dest.merge(source) dest.code.should be_instance_of(Puppet::Parser::AST::ASTArray) end it "should set the other class's code as its code if it has none" do dest = Puppet::Resource::Type.new(:hostclass, "bar") source = Puppet::Resource::Type.new(:hostclass, "foo", :code => code("bar")) dest.merge(source) dest.code.value.should == "bar" end it "should append the other class's code to its code if it has any" do dcode = Puppet::Parser::AST::ASTArray.new :children => [code("dest")] dest = Puppet::Resource::Type.new(:hostclass, "bar", :code => dcode) scode = Puppet::Parser::AST::ASTArray.new :children => [code("source")] source = Puppet::Resource::Type.new(:hostclass, "foo", :code => scode) dest.merge(source) dest.code.children.collect { |l| l.value }.should == %w{dest source} end end end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index f3e68996a..1aaeeaf03 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -1,891 +1,891 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/resource' describe Puppet::Resource do include PuppetSpec::Files let :basepath do make_absolute("/somepath") end [:catalog, :file, :line].each do |attr| it "should have an #{attr} attribute" do resource = Puppet::Resource.new("file", "/my/file") resource.should respond_to(attr) resource.should respond_to(attr.to_s + "=") end end it "should have a :title attribute" do Puppet::Resource.new(:user, "foo").title.should == "foo" end it "should require the type and title" do lambda { Puppet::Resource.new }.should raise_error(ArgumentError) end it "should canonize types to capitalized strings" do Puppet::Resource.new(:user, "foo").type.should == "User" end it "should canonize qualified types so all strings are capitalized" do Puppet::Resource.new("foo::bar", "foo").type.should == "Foo::Bar" end it "should tag itself with its type" do Puppet::Resource.new("file", "/f").should be_tagged("file") end it "should tag itself with its title if the title is a valid tag" do Puppet::Resource.new("user", "bar").should be_tagged("bar") end it "should not tag itself with its title if the title is a not valid tag" do Puppet::Resource.new("file", "/bar").should_not be_tagged("/bar") end it "should allow setting of attributes" do Puppet::Resource.new("file", "/bar", :file => "/foo").file.should == "/foo" Puppet::Resource.new("file", "/bar", :exported => true).should be_exported end it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do ref = Puppet::Resource.new(:component, "foo") ref.type.should == "Class" ref.title.should == "Foo" end it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do ref = Puppet::Resource.new(:component, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should set the type to 'Class' if it is nil and the title contains no square brackets" do ref = Puppet::Resource.new(nil, "yay") ref.type.should == "Class" ref.title.should == "Yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[baz[yay]]") ref.type.should == "Foo::Bar" ref.title.should =="baz[yay]" end it "should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets" do ref = Puppet::Resource.new("foo::bar[baz]") ref.type.should == "Foo::Bar" ref.title.should =="baz" end it "should be able to extract its information from a Puppet::Type instance" do ral = Puppet::Type.type(:file).new :path => basepath+"/foo" ref = Puppet::Resource.new(ral) ref.type.should == "File" ref.title.should == basepath+"/foo" end it "should fail if the title is nil and the type is not a valid resource reference string" do expect { Puppet::Resource.new("resource-spec-foo") }.should raise_error(ArgumentError) end it 'should fail if strict is set and type does not exist' do expect { Puppet::Resource.new('resource-spec-foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type resource-spec-foo') end it 'should fail if strict is set and class does not exist' do expect { Puppet::Resource.new('Class', 'resource-spec-foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class resource-spec-foo') end it "should fail if the title is a hash and the type is not a valid resource reference string" do expect { Puppet::Resource.new({:type => "resource-spec-foo", :title => "bar"}) }. to raise_error ArgumentError, /Puppet::Resource.new does not take a hash/ end it "should be taggable" do Puppet::Resource.ancestors.should be_include(Puppet::Util::Tagging) end it "should have an 'exported' attribute" do resource = Puppet::Resource.new("file", "/f") resource.exported = true resource.exported.should == true resource.should be_exported end it "should support an environment attribute" do Puppet::Resource.new("file", "/my/file", :environment => :foo).environment.name.should == :foo end describe "and munging its type and title" do describe "when modeling a builtin resource" do it "should be able to find the resource type" do Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) end it "should set its type to the capitalized type name" do Puppet::Resource.new("file", "/my/file").type.should == "File" end end describe "when modeling a defined resource" do describe "that exists" do before do @type = Puppet::Resource::Type.new(:definition, "foo::bar") Puppet::Node::Environment.new.known_resource_types.add @type end it "should set its type to the capitalized type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) end it "should set its title to the provided title" do Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" end end describe "that does not exist" do it "should set its resource type to the capitalized resource type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end end end describe "when modeling a node" do # Life's easier with nodes, because they can't be qualified. it "should set its type to 'Node' and its title to the provided title" do node = Puppet::Resource.new("node", "foo") node.type.should == "Node" node.title.should == "foo" end end describe "when modeling a class" do it "should set its type to 'Class'" do Puppet::Resource.new("class", "foo").type.should == "Class" end describe "that exists" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") Puppet::Node::Environment.new.known_resource_types.add @type end it "should set its title to the capitalized, fully qualified resource type" do Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) end end describe "that does not exist" do it "should set its type to 'Class' and its title to the capitalized provided name" do klass = Puppet::Resource.new("class", "foo::bar") klass.type.should == "Class" klass.title.should == "Foo::Bar" end end describe "and its name is set to the empty string" do it "should set its title to :main" do Puppet::Resource.new("class", "").title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") Puppet::Node::Environment.new.known_resource_types.add @type Puppet::Resource.new("class", "").title.should == :main end end end describe "and its name is set to :main" do it "should set its title to :main" do Puppet::Resource.new("class", :main).title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") Puppet::Node::Environment.new.known_resource_types.add @type Puppet::Resource.new("class", :main).title.should == :main end end end end end it "should return nil when looking up resource types that don't exist" do Puppet::Resource.new("foobar", "bar").resource_type.should be_nil end it "should not fail when an invalid parameter is used and strict mode is disabled" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type resource = Puppet::Resource.new("foobar", "/my/file") resource[:yay] = true end it "should be considered equivalent to another resource if their type and title match and no parameters are set" do Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") end it "should be considered equivalent to another resource if their type, title, and parameters are equal" do Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}).should == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to another resource if their type and title match but parameters are different" do Puppet::Resource.new("file", "/f", :parameters => {:fee => "baz"}).should_not == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to a non-resource" do Puppet::Resource.new("file", "/f").should_not == "foo" end it "should not be considered equivalent to another resource if their types do not match" do Puppet::Resource.new("file", "/f").should_not == Puppet::Resource.new("exec", "/f") end it "should not be considered equivalent to another resource if their titles do not match" do Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") end describe "when setting default parameters" do before do @scope = mock "Scope" @scope.stubs(:source).returns(nil) end it "should fail when asked to set default values and it is not a parser resource" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "default")}) ) resource = Puppet::Resource.new("default_param", "name") lambda { resource.set_default_parameters(@scope) }.should raise_error(Puppet::DevError) end it "should evaluate and set any default values when no value is provided" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => Puppet::Parser::Scope.new) resource.set_default_parameters(@scope) resource["a"].should == "a_default_value" end it "should skip attributes with no default value" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "a_default_value")}) ) resource = Puppet::Parser::Resource.new("no_default_param", "name", :scope => Puppet::Parser::Scope.new) lambda { resource.set_default_parameters(@scope) }.should_not raise_error end it "should return the list of default parameters set" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => Puppet::Parser::Scope.new) resource.set_default_parameters(@scope).should == [:a] end describe "when the resource type is :hostclass" do let(:environmnet_name) { "testing env" } let(:fact_values) { { :a => 1 } } let(:port) { Puppet::Parser::AST::String.new(:value => '80') } let(:apache) { Puppet::Resource::Type.new(:hostclass, 'apache', :arguments => { 'port' => port }) } before do environment = Puppet::Node::Environment.new(environmnet_name) environment.known_resource_types.add(apache) @scope.stubs(:host).returns('host') @scope.stubs(:environment).returns(Puppet::Node::Environment.new(environmnet_name)) @scope.stubs(:facts).returns(Puppet::Node::Facts.new("facts", fact_values)) end context "when no value is provided" do let(:resource) do Puppet::Parser::Resource.new("class", "apache", :scope => @scope) end it "should query the data_binding terminus using a namespaced key" do Puppet::DataBinding.indirection.expects(:find).with( 'apache::port', :host => 'host', :environment => environmnet_name, :facts => fact_values) resource.set_default_parameters(@scope) end it "should use the value from the data_binding terminus" do Puppet::DataBinding.indirection.expects(:find).returns('443') resource.set_default_parameters(@scope).should == [:port] resource[:port].should == '443' end it "should use the default value if the data_binding terminus returns nil" do Puppet::DataBinding.indirection.expects(:find).returns(nil) resource.set_default_parameters(@scope).should == [:port] resource[:port].should == '80' end end context "when a value is provided" do let(:port_parameter) do Puppet::Parser::Resource::Param.new( { :name => 'port', :value => '8080' } ) end let(:resource) do Puppet::Parser::Resource.new("class", "apache", :scope => @scope, :parameters => [port_parameter]) end it "should not query the data_binding terminus" do Puppet::DataBinding.indirection.expects(:find).never resource.set_default_parameters(@scope) end it "should use the value provided" do Puppet::DataBinding.indirection.expects(:find).never resource.set_default_parameters(@scope).should == [] resource[:port].should == '8080' end end end end describe "when validating all required parameters are present" do it "should be able to validate that all required parameters are present" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "required_param", :arguments => {"a" => nil}) ) lambda { Puppet::Resource.new("required_param", "name").validate_complete }.should raise_error(Puppet::ParseError) end it "should not fail when all required parameters are present" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_required_param") ) resource = Puppet::Resource.new("no_required_param", "name") resource["a"] = "meh" lambda { resource.validate_complete }.should_not raise_error end it "should not validate against builtin types" do lambda { Puppet::Resource.new("file", "/bar").validate_complete }.should_not raise_error end end describe "when referring to a resource with name canonicalization" do it "should canonicalize its own name" do res = Puppet::Resource.new("file", "/path/") res.uniqueness_key.should == ["/path"] res.ref.should == "File[/path/]" end end describe "when running in strict mode" do it "should be strict" do Puppet::Resource.new("file", "/path", :strict => true).should be_strict end it "should fail if invalid parameters are used" do expect { Puppet::Resource.new("file", "/path", :strict => true, :parameters => {:nosuchparam => "bar"}) }.should raise_error end it "should fail if the resource type cannot be resolved" do expect { Puppet::Resource.new("nosuchtype", "/path", :strict => true) }.should raise_error end end describe "when managing parameters" do before do @resource = Puppet::Resource.new("file", "/my/file") end it "should correctly detect when provided parameters are not valid for builtin types" do Puppet::Resource.new("file", "/my/file").should_not be_valid_parameter("foobar") end it "should correctly detect when provided parameters are valid for builtin types" do Puppet::Resource.new("file", "/my/file").should be_valid_parameter("mode") end it "should correctly detect when provided parameters are not valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file").should_not be_valid_parameter("myparam") end it "should correctly detect when provided parameters are valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) Puppet::Node::Environment.new.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file").should be_valid_parameter("myparam") end it "should allow setting and retrieving of parameters" do @resource[:foo] = "bar" @resource[:foo].should == "bar" end it "should allow setting of parameters at initialization" do Puppet::Resource.new("file", "/my/file", :parameters => {:foo => "bar"})[:foo].should == "bar" end it "should canonicalize retrieved parameter names to treat symbols and strings equivalently" do @resource[:foo] = "bar" @resource["foo"].should == "bar" end it "should canonicalize set parameter names to treat symbols and strings equivalently" do @resource["foo"] = "bar" @resource[:foo].should == "bar" end it "should set the namevar when asked to set the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:name] = "bob" resource[:myvar].should == "bob" end it "should return the namevar when asked to return the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:myvar] = "test" resource[:name].should == "test" end it "should be able to set the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" expect { resource[:name] = "eh" }.should_not raise_error end it "should be able to return the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" resource[:name].should == "eh" end it "should be able to iterate over parameters" do @resource[:foo] = "bar" @resource[:fee] = "bare" params = {} @resource.each do |key, value| params[key] = value end params.should == {:foo => "bar", :fee => "bare"} end it "should include Enumerable" do @resource.class.ancestors.should be_include(Enumerable) end it "should have a method for testing whether a parameter is included" do @resource[:foo] = "bar" @resource.should be_has_key(:foo) @resource.should_not be_has_key(:eh) end it "should have a method for providing the list of parameters" do @resource[:foo] = "bar" @resource[:bar] = "foo" keys = @resource.keys keys.should be_include(:foo) keys.should be_include(:bar) end it "should have a method for providing the number of parameters" do @resource[:foo] = "bar" @resource.length.should == 1 end it "should have a method for deleting parameters" do @resource[:foo] = "bar" @resource.delete(:foo) @resource[:foo].should be_nil end it "should have a method for testing whether the parameter list is empty" do @resource.should be_empty @resource[:foo] = "bar" @resource.should_not be_empty end it "should be able to produce a hash of all existing parameters" do @resource[:foo] = "bar" @resource[:fee] = "yay" hash = @resource.to_hash hash[:foo].should == "bar" hash[:fee].should == "yay" end it "should not provide direct access to the internal parameters hash when producing a hash" do hash = @resource.to_hash hash[:foo] = "bar" @resource[:foo].should be_nil end it "should use the title as the namevar to the hash if no namevar is present" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource.to_hash[:myvar].should == "bob" end it "should set :name to the title if :name is not present for non-builtin types" do krt = Puppet::Resource::TypeCollection.new("myenv") krt.add Puppet::Resource::Type.new(:definition, :foo) resource = Puppet::Resource.new :foo, "bar" resource.stubs(:known_resource_types).returns krt resource.to_hash[:name].should == "bar" end end describe "when serializing" do before do @resource = Puppet::Resource.new("file", "/my/file") @resource["one"] = "test" @resource["two"] = "other" end it "should be able to be dumped to yaml" do proc { YAML.dump(@resource) }.should_not raise_error end it "should produce an equivalent yaml object" do text = YAML.dump(@resource) newresource = YAML.load(text) newresource.title.should == @resource.title newresource.type.should == @resource.type %w{one two}.each do |param| newresource[param].should == @resource[param] end end end describe "when loading 0.25.x storedconfigs YAML" do before :each do @old_storedconfig_yaml = %q{--- !ruby/object:Puppet::Resource::Reference builtin_type: title: /tmp/bar type: File } end it "should deserialize a Puppet::Resource::Reference without exceptions" do expect { YAML.load(@old_storedconfig_yaml) }.should_not raise_error end it "should deserialize as a Puppet::Resource::Reference as a Puppet::Resource" do YAML.load(@old_storedconfig_yaml).class.should == Puppet::Resource end it "should to_hash properly" do YAML.load(@old_storedconfig_yaml).to_hash.should == { :path => "/tmp/bar" } end end describe "when converting to a RAL resource" do it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do resource = Puppet::Resource.new("file", basepath+"/my/file") result = resource.to_ral result.should be_instance_of(Puppet::Type.type(:file)) result[:path].should == basepath+"/my/file" end it "should convert to a component instance if the resource type is not of a builtin type" do resource = Puppet::Resource.new("foobar", "somename") result = resource.to_ral result.should be_instance_of(Puppet::Type.type(:component)) result.title.should == "Foobar[somename]" end end describe "when converting to puppet code" do before do @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => { :noop => true, :foo => %w{one two}, :ensure => 'present', } ) end it "should align, sort and add trailing commas to attributes with ensure first" do @resource.to_manifest.should == <<-HEREDOC.gsub(/^\s{8}/, '').gsub(/\n$/, '') one::two { '/my/file': ensure => 'present', foo => ['one', 'two'], noop => 'true', } HEREDOC end end describe "when converting to pson", :if => Puppet.features.pson? do def pson_output_should @resource.class.expects(:pson_create).with { |hash| yield hash } end it "should include the pson util module" do Puppet::Resource.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its type to the provided type" do Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" end it "should include all tags from the resource" do resource = Puppet::Resource.new("File", "/foo") resource.tag("yay") Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).tags.should == resource.tags end it "should include the file if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.file = "/my/file" Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).file.should == "/my/file" end it "should include the line if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.line = 50 Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).line.should == 50 end it "should include the 'exported' value if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.exported = true Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_true end it "should set 'exported' to false if no value is set" do resource = Puppet::Resource.new("File", "/foo") Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_false end it "should set all of its parameters as the 'parameters' entry" do resource = Puppet::Resource.new("File", "/foo") resource[:foo] = %w{bar eh} resource[:fee] = %w{baz} result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result["foo"].should == %w{bar eh} result["fee"].should == %w{baz} end it "should serialize relationships as reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = Puppet::Resource.new("File", "/bar") result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result[:requires].should == "File[/bar]" end it "should serialize multiple relationships as arrays of reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = [Puppet::Resource.new("File", "/bar"), Puppet::Resource.new("File", "/baz")] result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result[:requires].should == [ "File[/bar]", "File[/baz]" ] end end describe "when converting from pson", :if => Puppet.features.pson? do def pson_result_should Puppet::Resource.expects(:new).with { |hash| yield hash } end before do @data = { 'type' => "file", 'title' => basepath+"/yay", } end it "should set its type to the provided type" do Puppet::Resource.from_pson(@data).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_pson(@data).title.should == basepath+"/yay" end it "should tag the resource with any provided tags" do @data['tags'] = %w{foo bar} resource = Puppet::Resource.from_pson(@data) resource.tags.should be_include("foo") resource.tags.should be_include("bar") end it "should set its file to the provided file" do @data['file'] = "/foo/bar" Puppet::Resource.from_pson(@data).file.should == "/foo/bar" end it "should set its line to the provided line" do @data['line'] = 50 Puppet::Resource.from_pson(@data).line.should == 50 end it "should 'exported' to true if set in the pson data" do @data['exported'] = true Puppet::Resource.from_pson(@data).exported.should be_true end it "should 'exported' to false if not set in the pson data" do Puppet::Resource.from_pson(@data).exported.should be_false end it "should fail if no title is provided" do @data.delete('title') expect { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) end it "should fail if no type is provided" do @data.delete('type') expect { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) end it "should set each of the provided parameters" do @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} resource = Puppet::Resource.from_pson(@data) resource['foo'].should == %w{one two} resource['fee'].should == %w{three four} end it "should convert single-value array parameters to normal values" do @data['parameters'] = {'foo' => %w{one}} resource = Puppet::Resource.from_pson(@data) resource['foo'].should == %w{one} end end describe "it should implement to_resource" do resource = Puppet::Resource.new("file", "/my/file") resource.to_resource.should == resource end describe "because it is an indirector model" do it "should include Puppet::Indirector" do Puppet::Resource.should be_is_a(Puppet::Indirector) end it "should have a default terminus" do Puppet::Resource.indirection.terminus_class.should be end it "should have a name" do Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" end end describe "when resolving resources with a catalog" do it "should resolve all resources using the catalog" do catalog = mock 'catalog' resource = Puppet::Resource.new("foo::bar", "yay") resource.catalog = catalog catalog.expects(:resource).with("Foo::Bar[yay]").returns(:myresource) resource.resolve.should == :myresource end end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:myvar, :owner, :path] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) res = Puppet::Resource.new("file", "/my/file", :parameters => {:owner => 'root', :content => 'hello'}) res.uniqueness_key.should == [ nil, 'root', '/my/file'] end end describe "#prune_parameters" do before do Puppet.newtype('blond') do newproperty(:ensure) newproperty(:height) newproperty(:weight) newproperty(:sign) newproperty(:friends) newparam(:admits_to_dying_hair) newparam(:admits_to_age) newparam(:name) end end it "should strip all parameters and strip properties that are nil, empty or absent except for ensure" do resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :ensure => 'absent', :height => '', :weight => 'absent', :friends => [], :admits_to_age => true, :admits_to_dying_hair => false }) pruned_resource = resource.prune_parameters pruned_resource.should == Puppet::Resource.new("blond", "Bambi", :parameters => {:ensure => 'absent'}) end it "should leave parameters alone if in parameters_to_include" do resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :admits_to_age => true, :admits_to_dying_hair => false }) pruned_resource = resource.prune_parameters(:parameters_to_include => [:admits_to_dying_hair]) pruned_resource.should == Puppet::Resource.new("blond", "Bambi", :parameters => {:admits_to_dying_hair => false}) end it "should leave properties if not nil, absent or empty" do resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :ensure => 'silly', :height => '7 ft 5 in', :friends => ['Oprah'], }) pruned_resource = resource.prune_parameters pruned_resource.should == resource = Puppet::Resource.new("blond", "Bambi", :parameters => { :ensure => 'silly', :height => '7 ft 5 in', :friends => ['Oprah'], }) end end end diff --git a/spec/unit/run_spec.rb b/spec/unit/run_spec.rb index ea6ec74b1..b4480fccf 100755 --- a/spec/unit/run_spec.rb +++ b/spec/unit/run_spec.rb @@ -1,136 +1,136 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/agent' require 'puppet/run' describe Puppet::Run do before do @runner = Puppet::Run.new end it "should indirect :run" do Puppet::Run.indirection.name.should == :run end it "should use a configurer agent as its agent" do agent = mock 'agent' Puppet::Agent.expects(:new).with(Puppet::Configurer).returns agent @runner.agent.should equal(agent) end it "should accept options at initialization" do lambda { Puppet::Run.new :background => true }.should_not raise_error end it "should default to running in the foreground" do Puppet::Run.new.should_not be_background end it "should default to its options being an empty hash" do Puppet::Run.new.options.should == {} end it "should accept :tags for the agent" do Puppet::Run.new(:tags => "foo").options[:tags].should == "foo" end it "should accept :ignoreschedules for the agent" do Puppet::Run.new(:ignoreschedules => true).options[:ignoreschedules].should be_true end it "should accept an option to configure it to run in the background" do Puppet::Run.new(:background => true).should be_background end it "should retain the background option" do Puppet::Run.new(:background => true).options[:background].should be_nil end it "should not accept arbitrary options" do lambda { Puppet::Run.new(:foo => true) }.should raise_error(ArgumentError) end describe "when asked to run" do before do @agent = stub 'agent', :run => nil, :running? => false @runner.stubs(:agent).returns @agent end it "should run its agent" do agent = stub 'agent2', :running? => false @runner.stubs(:agent).returns agent agent.expects(:run) @runner.run end it "should pass any of its options on to the agent" do @runner.stubs(:options).returns(:foo => :bar) @agent.expects(:run).with(:foo => :bar) @runner.run end it "should log its run using the provided options" do @runner.expects(:log_run) @runner.run end it "should set its status to 'already_running' if the agent is already running" do @agent.expects(:running?).returns true @runner.run @runner.status.should == "running" end it "should set its status to 'success' if the agent is run" do @agent.expects(:running?).returns false @runner.run @runner.status.should == "success" end it "should run the agent in a thread if asked to run it in the background" do Thread.expects(:new) @runner.expects(:background?).returns true @agent.expects(:run).never # because our thread didn't yield @runner.run end it "should run the agent directly if asked to run it in the foreground" do Thread.expects(:new).never @runner.expects(:background?).returns false @agent.expects(:run) @runner.run end end describe ".from_pson" do it "should accept a hash of options, and pass them with symbolified keys to new" do options = { "tags" => "whatever", "background" => true, } Puppet::Run.expects(:new).with( { :tags => "whatever", :background => true, }) Puppet::Run.from_pson(options) end end end diff --git a/spec/unit/settings/directory_setting_spec.rb b/spec/unit/settings/directory_setting_spec.rb index ee9138743..9b4223bd1 100755 --- a/spec/unit/settings/directory_setting_spec.rb +++ b/spec/unit/settings/directory_setting_spec.rb @@ -1,32 +1,32 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/settings' require 'puppet/settings/directory_setting' describe Puppet::Settings::DirectorySetting do DirectorySetting = Puppet::Settings::DirectorySetting include PuppetSpec::Files before do @basepath = make_absolute("/somepath") end describe "when being converted to a resource" do before do @settings = mock 'settings' @dir = Puppet::Settings::DirectorySetting.new( :settings => @settings, :desc => "eh", :name => :mydir, :section => "mysect") @settings.stubs(:value).with(:mydir).returns @basepath end it "should return :directory as its type" do @dir.type.should == :directory end end end diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb index e04ccd6e2..e765cbd6b 100755 --- a/spec/unit/settings/file_setting_spec.rb +++ b/spec/unit/settings/file_setting_spec.rb @@ -1,290 +1,290 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/settings' require 'puppet/settings/file_setting' describe Puppet::Settings::FileSetting do FileSetting = Puppet::Settings::FileSetting include PuppetSpec::Files before do @basepath = make_absolute("/somepath") end describe "when determining whether the service user should be used" do before do @settings = mock 'settings' @settings.stubs(:[]).with(:mkusers).returns false @settings.stubs(:service_user_available?).returns true end it "should be true if the service user is available" do @settings.expects(:service_user_available?).returns true setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end it "should be true if 'mkusers' is set" do @settings.expects(:[]).with(:mkusers).returns true setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end it "should be false if the service user is not available and 'mkusers' is unset" do setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end end describe "when setting the owner" do it "should allow the file to be owned by root" do root_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") } root_owner.should_not raise_error end it "should allow the file to be owned by the service user" do service_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "service", :desc => "a setting") } service_owner.should_not raise_error end it "should allow the ownership of the file to be unspecified" do no_owner = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } no_owner.should_not raise_error end it "should not allow other owners" do invalid_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "invalid", :desc => "a setting") } invalid_owner.should raise_error(FileSetting::SettingError) end end describe "when reading the owner" do it "should be root when the setting specifies root" do setting = FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") setting.owner.should == "root" end it "should be the owner of the service when the setting specifies service and the service user should be used" do settings = mock("settings") settings.stubs(:[]).returns "the_service" setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") setting.expects(:use_service_user?).returns true setting.owner.should == "the_service" end it "should be the root when the setting specifies service and the service user should not be used" do settings = mock("settings") settings.stubs(:[]).returns "the_service" setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") setting.expects(:use_service_user?).returns false setting.owner.should == "root" end it "should be nil when the owner is unspecified" do FileSetting.new(:settings => mock("settings"), :desc => "a setting").owner.should be_nil end end describe "when setting the group" do it "should allow the group to be service" do service_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "service", :desc => "a setting") } service_group.should_not raise_error end it "should allow the group to be unspecified" do no_group = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } no_group.should_not raise_error end it "should not allow invalid groups" do invalid_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "invalid", :desc => "a setting") } invalid_group.should raise_error(FileSetting::SettingError) end end describe "when reading the group" do it "should be service when the setting specifies service" do setting = FileSetting.new(:settings => mock("settings", :[] => "the_service"), :group => "service", :desc => "a setting") setting.group.should == "the_service" end it "should be nil when the group is unspecified" do FileSetting.new(:settings => mock("settings"), :desc => "a setting").group.should be_nil end end it "should be able to be converted into a resource" do FileSetting.new(:settings => mock("settings"), :desc => "eh").should respond_to(:to_resource) end describe "when being converted to a resource" do before do @settings = mock 'settings' @file = Puppet::Settings::FileSetting.new(:settings => @settings, :desc => "eh", :name => :myfile, :section => "mysect") @file.stubs(:create_files?).returns true @settings.stubs(:value).with(:myfile).returns @basepath end it "should return :file as its type" do @file.type.should == :file end it "should skip non-existent files if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file File.expects(:exist?).with(@basepath).returns false @file.to_resource.should be_nil end it "should manage existent files even if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file File.expects(:exist?).with(@basepath).returns true @file.to_resource.should be_instance_of(Puppet::Resource) end describe "on POSIX systems", :if => Puppet.features.posix? do it "should skip files in /dev" do @settings.stubs(:value).with(:myfile).returns "/dev/file" @file.to_resource.should be_nil end end it "should skip files whose paths are not strings" do @settings.stubs(:value).with(:myfile).returns :foo @file.to_resource.should be_nil end it "should return a file resource with the path set appropriately" do resource = @file.to_resource resource.type.should == "File" resource.title.should == @basepath end it "should fully qualified returned files if necessary (#795)" do @settings.stubs(:value).with(:myfile).returns "myfile" path = File.join(Dir.getwd, "myfile") # Dir.getwd can return windows paths with backslashes, so we normalize them using expand_path path = File.expand_path(path) if Puppet.features.microsoft_windows? @file.to_resource.title.should == path end it "should set the mode on the file if a mode is provided as an octal number" do @file.mode = 0755 @file.to_resource[:mode].should == '755' end it "should set the mode on the file if a mode is provided as a string" do @file.mode = '0755' @file.to_resource[:mode].should == '755' end it "should not set the mode on a the file if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false @file.stubs(:mode).returns(0755) @file.to_resource[:mode].should == nil end it "should set the owner if running as root and the owner is provided" do Puppet.features.expects(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == "foo" end it "should not set the owner if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false Puppet.features.stubs(:root?).returns true @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == nil end it "should set the group if running as root and the group is provided" do Puppet.features.expects(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should == "foo" end it "should not set the group if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false Puppet.features.stubs(:root?).returns true @file.stubs(:group).returns "foo" @file.to_resource[:group].should == nil end it "should not set owner if not running as root" do Puppet.features.expects(:root?).returns false Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should be_nil end it "should not set group if not running as root" do Puppet.features.expects(:root?).returns false Puppet.features.stubs(:microsoft_windows?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should be_nil end describe "on Microsoft Windows systems" do before :each do Puppet.features.stubs(:microsoft_windows?).returns true end it "should not set owner" do @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should be_nil end it "should not set group" do @file.stubs(:group).returns "foo" @file.to_resource[:group].should be_nil end end it "should set :ensure to the file type" do @file.expects(:type).returns :directory @file.to_resource[:ensure].should == :directory end it "should set the loglevel to :debug" do @file.to_resource[:loglevel].should == :debug end it "should set the backup to false" do @file.to_resource[:backup].should be_false end it "should tag the resource with the settings section" do @file.expects(:section).returns "mysect" @file.to_resource.should be_tagged("mysect") end it "should tag the resource with the setting name" do @file.to_resource.should be_tagged("myfile") end it "should tag the resource with 'settings'" do @file.to_resource.should be_tagged("settings") end it "should set links to 'follow'" do @file.to_resource[:links].should == :follow end end end diff --git a/spec/unit/settings/path_setting_spec.rb b/spec/unit/settings/path_setting_spec.rb index a5cd945a9..780760a77 100755 --- a/spec/unit/settings/path_setting_spec.rb +++ b/spec/unit/settings/path_setting_spec.rb @@ -1,30 +1,30 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Settings::PathSetting do subject { described_class.new(:settings => mock('settings'), :desc => "test") } context "#munge" do it "should expand all path elements" do munged = subject.munge("hello#{File::PATH_SEPARATOR}good/morning#{File::PATH_SEPARATOR}goodbye") munged.split(File::PATH_SEPARATOR).each do |p| Puppet::Util.should be_absolute_path(p) end end it "should leave nil as nil" do subject.munge(nil).should be_nil end context "on Windows", :if => Puppet.features.microsoft_windows? do it "should convert \\ to /" do subject.munge('C:\test\directory').should == 'C:/test/directory' end it "should work with UNC paths" do subject.munge('//server/some/path').should == '//server/some/path' subject.munge('\\\\server\some\path').should == '//server/some/path' end end end end diff --git a/spec/unit/settings/string_setting_spec.rb b/spec/unit/settings/string_setting_spec.rb index 88e382a36..18eb1427d 100644 --- a/spec/unit/settings/string_setting_spec.rb +++ b/spec/unit/settings/string_setting_spec.rb @@ -1,75 +1,75 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/settings' require 'puppet/settings/string_setting' describe Puppet::Settings::StringSetting do StringSetting = Puppet::Settings::StringSetting before(:each) do @test_setting_name = :test_setting @test_setting_default = "my_crazy_default/$var" @application_setting = "application/$var" @application_defaults = { } Puppet::Settings::REQUIRED_APP_SETTINGS.each do |key| @application_defaults[key] = "foo" end @application_defaults[:run_mode] = :user @settings = Puppet::Settings.new @application_defaults.each { |k,v| @settings.define_settings :main, k => {:default=>"", :desc => "blah"} } @settings.define_settings :main, :var => { :default => "interpolate!", :type => :string, :desc => "my var desc" }, @test_setting_name => { :default => @test_setting_default, :type => :string, :desc => "my test desc" } @test_setting = @settings.setting(@test_setting_name) end describe "#default" do describe "with no arguments" do it "should return the setting default" do @test_setting.default.should == @test_setting_default end it "should be uninterpolated" do @test_setting.default.should_not =~ /interpolate/ end end describe "checking application defaults first" do describe "if application defaults set" do before(:each) do @settings.initialize_app_defaults @application_defaults.merge @test_setting_name => @application_setting end it "should return the application-set default" do @test_setting.default(true).should == @application_setting end it "should be uninterpolated" do @test_setting.default(true).should_not =~ /interpolate/ end end describe "if application defaults not set" do it "should return the regular default" do @test_setting.default(true).should == @test_setting_default end it "should be uninterpolated" do @test_setting.default(true).should_not =~ /interpolate/ end end end end describe "#value" do it "should be interpolated" do @test_setting.value.should =~ /interpolate/ end end end diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb index a46100ab9..8dfeec69e 100755 --- a/spec/unit/settings_spec.rb +++ b/spec/unit/settings_spec.rb @@ -1,1532 +1,1532 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'ostruct' require 'puppet/settings/errors' describe Puppet::Settings do include PuppetSpec::Files MAIN_CONFIG_FILE_DEFAULT_LOCATION = File.join(Puppet::Settings.default_global_config_dir, "puppet.conf") USER_CONFIG_FILE_DEFAULT_LOCATION = File.join(Puppet::Settings.default_user_config_dir, "puppet.conf") describe "when dealing with user default directories" do context "user config dir" do it "should expand the value to an absolute path" do Pathname.new(Puppet::Settings.default_user_config_dir).absolute?.should be_true end end context "user var dir" do it "should expand the value to an absolute path" do Pathname.new(Puppet::Settings.default_user_var_dir).absolute?.should be_true end end end describe "when specifying defaults" do before do @settings = Puppet::Settings.new end it "should start with no defined parameters" do @settings.params.length.should == 0 end it "should not allow specification of default values associated with a section as an array" do expect { @settings.define_settings(:section, :myvalue => ["defaultval", "my description"]) }.to raise_error end it "should not allow duplicate parameter specifications" do @settings.define_settings(:section, :myvalue => { :default => "a", :desc => "b" }) lambda { @settings.define_settings(:section, :myvalue => { :default => "c", :desc => "d" }) }.should raise_error(ArgumentError) end it "should allow specification of default values associated with a section as a hash" do @settings.define_settings(:section, :myvalue => {:default => "defaultval", :desc => "my description"}) end it "should consider defined parameters to be valid" do @settings.define_settings(:section, :myvalue => { :default => "defaultval", :desc => "my description" }) @settings.valid?(:myvalue).should be_true end it "should require a description when defaults are specified with a hash" do lambda { @settings.define_settings(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError) end it "should support specifying owner, group, and mode when specifying files" do @settings.define_settings(:section, :myvalue => {:type => :file, :default => "/some/file", :owner => "service", :mode => "boo", :group => "service", :desc => "whatever"}) end it "should support specifying a short name" do @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) end it "should support specifying the setting type" do @settings.define_settings(:section, :myvalue => {:default => "/w", :desc => "b", :type => :string}) @settings.setting(:myvalue).should be_instance_of(Puppet::Settings::StringSetting) end it "should fail if an invalid setting type is specified" do lambda { @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :type => :foo}) }.should raise_error(ArgumentError) end it "should fail when short names conflict" do @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) lambda { @settings.define_settings(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError) end end describe "when initializing application defaults do" do before do @settings = Puppet::Settings.new end it "should fail if someone attempts to initialize app defaults more than once" do @settings.expects(:app_defaults_initialized?).returns(true) expect { @settings.initialize_app_defaults({}) }.to raise_error(Puppet::DevError) end it "should fail if the app defaults hash is missing any required values" do expect { @settings.initialize_app_defaults({}) }.to raise_error(Puppet::Settings::SettingsError) end # ultimately I'd like to stop treating "run_mode" as a normal setting, because it has so many special # case behaviors / uses. However, until that time... we need to make sure that our private run_mode= # setter method gets properly called during app initialization. it "should call the hacky run mode setter method until we do a better job of separating run_mode" do app_defaults = {} Puppet::Settings::REQUIRED_APP_SETTINGS.each do |key| app_defaults[key] = "foo" end @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS) @settings.expects(:run_mode=).with("foo") @settings.initialize_app_defaults(app_defaults) end end describe "#call_hooks_deferred_to_application_initialization" do let (:good_default) { "yay" } let (:bad_default) { "$doesntexist" } before(:each) do @settings = Puppet::Settings.new end describe "when ignoring dependency interpolation errors" do let(:options) { {:ignore_interpolation_dependency_errors => true} } describe "if interpolation error" do it "should not raise an error" do hook_values = [] @settings.define_settings(:section, :badhook => {:default => bad_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }}) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to_not raise_error end end describe "if no interpolation error" do it "should not raise an error" do hook_values = [] @settings.define_settings(:section, :goodhook => {:default => good_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v }}) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to_not raise_error end end end describe "when not ignoring dependency interpolation errors" do [ {}, {:ignore_interpolation_dependency_errors => false}].each do |options| describe "if interpolation error" do it "should raise an error" do hook_values = [] @settings.define_settings( :section, :badhook => { :default => bad_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v } } ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to raise_error Puppet::Settings::InterpolationError end it "should contain the setting name in error message" do hook_values = [] @settings.define_settings( :section, :badhook => { :default => bad_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v } } ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to raise_error Puppet::Settings::InterpolationError, /badhook/ end end describe "if no interpolation error" do it "should not raise an error" do hook_values = [] @settings.define_settings( :section, :goodhook => { :default => good_default, :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| hook_values << v } } ) expect do @settings.send(:call_hooks_deferred_to_application_initialization, options) end.to_not raise_error end end end end end describe "when setting values" do before do @settings = Puppet::Settings.new @settings.define_settings :main, :myval => { :default => "val", :desc => "desc" } @settings.define_settings :main, :bool => { :type => :boolean, :default => true, :desc => "desc" } end it "should provide a method for setting values from other objects" do @settings[:myval] = "something else" @settings[:myval].should == "something else" end it "should support a getopt-specific mechanism for setting values" do @settings.handlearg("--myval", "newval") @settings[:myval].should == "newval" end it "should support a getopt-specific mechanism for turning booleans off" do @settings[:bool] = true @settings.handlearg("--no-bool", "") @settings[:bool].should == false end it "should support a getopt-specific mechanism for turning booleans on" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with no argument to be a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be a boolean, if the setting itself is a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do @settings[:myval] = "bob" @settings.handlearg("--myval", "") @settings[:myval].should == "" end it "should consider a cli setting with a boolean as an argument to be a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "true") @settings[:bool].should == true end it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do # Turn it off first @settings[:myval] = "bob" @settings.handlearg("--no-myval", "") @settings[:myval].should == "" end it "should flag string settings from the CLI" do @settings.handlearg("--myval", "12") @settings.set_by_cli?(:myval).should be_true end it "should flag bool settings from the CLI" do @settings[:bool] = false @settings.handlearg("--bool") @settings.set_by_cli?(:bool).should be_true end it "should not flag settings memory as from CLI" do @settings[:myval] = "12" @settings.set_by_cli?(:myval).should be_false end describe "setbycli" do it "should generate a deprecation warning" do Puppet.expects(:deprecation_warning) @settings.setting(:myval).setbycli = true end it "should set the value" do @settings[:myval] = "blah" @settings.setting(:myval).setbycli = true @settings.set_by_cli?(:myval).should be_true end it "should raise error if trying to unset value" do @settings.handlearg("--myval", "blah") expect do @settings.setting(:myval).setbycli = nil end.to raise_error ArgumentError, /unset/ end end it "should clear the cache when setting getopt-specific values" do @settings.define_settings :mysection, :one => { :default => "whah", :desc => "yay" }, :two => { :default => "$one yay", :desc => "bah" } @settings[:two].should == "whah yay" @settings.handlearg("--one", "else") @settings[:two].should == "else yay" end it "should not clear other values when setting getopt-specific values" do @settings[:myval] = "yay" @settings.handlearg("--no-bool", "") @settings[:myval].should == "yay" end it "should clear the list of used sections" do @settings.expects(:clearused) @settings[:myval] = "yay" end describe "call_hook" do Puppet::Settings::StringSetting.available_call_hook_values.each do |val| describe "when :#{val}" do describe "and definition invalid" do it "should raise error if no hook defined" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val}) end.to raise_error ArgumentError, /no :hook/ end it "should include the setting name in the error message" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val}) end.to raise_error ArgumentError, /for :hooker/ end end describe "and definition valid" do before(:each) do hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val, :hook => lambda { |v| hook_values << v }}) end it "should call the hook when value written" do @settings.setting(:hooker).expects(:handle).with("something").once @settings[:hooker] = "something" end end end end it "should have a default value of :on_write_only" do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_hook.should == :on_write_only end describe "when nil" do it "should generate a warning" do Puppet.expects(:warning) @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => nil, :hook => lambda { |v| hook_values << v }}) end it "should use default" do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => nil, :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_hook.should == :on_write_only end end describe "when invalid" do it "should raise an error" do expect do @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :foo, :hook => lambda { |v| hook_values << v }}) end.to raise_error ArgumentError, /invalid.*call_hook/i end end describe "when :on_define_and_write" do it "should call the hook at definition" do hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :on_define_and_write, :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_hook.should == :on_define_and_write hook_values.should == %w{yay} end end describe "when :on_initialize_and_write" do before(:each) do @hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :on_initialize_and_write, :hook => lambda { |v| @hook_values << v }}) end it "should not call the hook at definition" do @hook_values.should == [] @hook_values.should_not == %w{yay} end it "should call the hook at initialization" do app_defaults = {} Puppet::Settings::REQUIRED_APP_SETTINGS.each do |key| app_defaults[key] = "foo" end app_defaults[:run_mode] = :user @settings.define_settings(:main, PuppetSpec::Settings::TEST_APP_DEFAULT_DEFINITIONS) @settings.setting(:hooker).expects(:handle).with("yay").once @settings.initialize_app_defaults app_defaults end end end describe "call_on_define" do [true, false].each do |val| describe "to #{val}" do it "should generate a deprecation warning" do Puppet.expects(:deprecation_warning) values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_on_define => val, :hook => lambda { |v| values << v }}) end it "should should set call_hook" do values = [] name = "hooker_#{val}".to_sym @settings.define_settings(:section, name => {:default => "yay", :desc => "boo", :call_on_define => val, :hook => lambda { |v| values << v }}) @settings.setting(name).call_hook.should == :on_define_and_write if val @settings.setting(name).call_hook.should == :on_write_only unless val end end end end it "should call passed blocks when values are set" do values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings[:hooker] = "something" values.should == %w{something} end it "should call passed blocks when values are set via the command line" do values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings.handlearg("--hooker", "yay") values.should == %w{yay} end it "should provide an option to call passed blocks during definition" do values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => :on_define_and_write, :hook => lambda { |v| values << v }}) values.should == %w{yay} end it "should pass the fully interpolated value to the hook when called on definition" do values = [] @settings.define_settings(:section, :one => { :default => "test", :desc => "a" }) @settings.define_settings(:section, :hooker => {:default => "$one/yay", :desc => "boo", :call_hook => :on_define_and_write, :hook => lambda { |v| values << v }}) values.should == %w{test/yay} end it "should munge values using the setting-specific methods" do @settings[:bool] = "false" @settings[:bool].should == false end it "should prefer cli values to values set in Ruby code" do @settings.handlearg("--myval", "cliarg") @settings[:myval] = "memarg" @settings[:myval].should == "cliarg" end it "should clear the list of environments" do Puppet::Node::Environment.expects(:clear).at_least(1) @settings[:myval] = "memarg" end it "should raise an error if we try to set a setting that hasn't been defined'" do lambda{ @settings[:why_so_serious] = "foo" }.should raise_error(ArgumentError, /unknown configuration parameter/) end it "should raise an error if we try to set a setting that is read-only (which, really, all of our settings probably should be)" do @settings.define_settings(:section, :one => { :default => "test", :desc => "a" }) @settings.expects(:read_only_settings).returns([:one]) lambda{ @settings[:one] = "foo" }.should raise_error(ArgumentError, /read-only/) end end describe "when returning values" do before do @settings = Puppet::Settings.new @settings.define_settings :section, :config => { :type => :file, :default => "/my/file", :desc => "eh" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b"}, :three => { :default => "$one $two THREE", :desc => "c"}, :four => { :default => "$two $three FOUR", :desc => "d"} FileTest.stubs(:exist?).returns true end describe "call_on_define" do it "should generate a deprecation warning" do Puppet.expects(:deprecation_warning) @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_on_define end Puppet::Settings::StringSetting.available_call_hook_values.each do |val| it "should match value for call_hook => :#{val}" do hook_values = [] @settings.define_settings(:section, :hooker => {:default => "yay", :desc => "boo", :call_hook => val, :hook => lambda { |v| hook_values << v }}) @settings.setting(:hooker).call_on_define.should == @settings.setting(:hooker).call_hook_on_define? end end end it "should provide a mechanism for returning set values" do @settings[:one] = "other" @settings[:one].should == "other" end it "should interpolate default values for other parameters into returned parameter values" do @settings[:one].should == "ONE" @settings[:two].should == "ONE TWO" @settings[:three].should == "ONE ONE TWO THREE" end it "should interpolate default values that themselves need to be interpolated" do @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR" end it "should provide a method for returning uninterpolated values" do @settings[:two] = "$one tw0" @settings.uninterpolated_value(:two).should == "$one tw0" @settings.uninterpolated_value(:four).should == "$two $three FOUR" end it "should interpolate set values for other parameters into returned parameter values" do @settings[:one] = "on3" @settings[:two] = "$one tw0" @settings[:three] = "$one $two thr33" @settings[:four] = "$one $two $three f0ur" @settings[:one].should == "on3" @settings[:two].should == "on3 tw0" @settings[:three].should == "on3 on3 tw0 thr33" @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur" end it "should not cache interpolated values such that stale information is returned" do @settings[:two].should == "ONE TWO" @settings[:one] = "one" @settings[:two].should == "one TWO" end it "should not cache values such that information from one environment is returned for another environment" do text = "[env1]\none = oneval\n[env2]\none = twoval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:one, "env1").should == "oneval" @settings.value(:one, "env2").should == "twoval" end it "should have a run_mode that defaults to user" do @settings.run_mode.should == :user end describe "setbycli" do it "should generate a deprecation warning" do @settings.handlearg("--one", "blah") Puppet.expects(:deprecation_warning) @settings.setting(:one).setbycli end it "should be true" do @settings.handlearg("--one", "blah") @settings.setting(:one).setbycli.should be_true end end end describe "when choosing which value to return" do before do @settings = Puppet::Settings.new @settings.define_settings :section, :config => { :type => :file, :default => "/my/file", :desc => "a" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "TWO", :desc => "b" } FileTest.stubs(:exist?).returns true @settings.stubs(:run_mode).returns :mymode end it "should return default values if no values have been set" do @settings[:one].should == "ONE" end it "should return values set on the cli before values set in the configuration file" do text = "[main]\none = fileval\n" @settings.stubs(:read_file).returns(text) @settings.handlearg("--one", "clival") @settings.send(:parse_config_files) @settings[:one].should == "clival" end it "should return values set on the cli before values set in Ruby" do @settings[:one] = "rubyval" @settings.handlearg("--one", "clival") @settings[:one].should == "clival" end it "should return values set in the mode-specific section before values set in the main section" do text = "[main]\none = mainval\n[mymode]\none = modeval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "modeval" end it "should not return values outside of its search path" do text = "[other]\none = oval\n" file = "/some/file" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "ONE" end it "should return values in a specified environment" do text = "[env]\none = envval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:one, "env").should == "envval" end it 'should use the current environment for $environment' do @settings.define_settings :main, :myval => { :default => "$environment/foo", :desc => "mydocs" } @settings.value(:myval, "myenv").should == "myenv/foo" end it "should interpolate found values using the current environment" do text = "[main]\none = mainval\n[myname]\none = nameval\ntwo = $one/two\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:two, "myname").should == "nameval/two" end it "should return values in a specified environment before values in the main or name sections" do text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings.value(:one, "env").should == "envval" end end describe "when locating config files" do before do @settings = Puppet::Settings.new end describe "when root" do it "should look for #{MAIN_CONFIG_FILE_DEFAULT_LOCATION} if config settings haven't been overridden'" do Puppet.features.stubs(:root?).returns(true) FileTest.expects(:exist?).with(MAIN_CONFIG_FILE_DEFAULT_LOCATION).returns(false) FileTest.expects(:exist?).with(USER_CONFIG_FILE_DEFAULT_LOCATION).never @settings.send(:parse_config_files) end end describe "when not root" do it "should look for #{MAIN_CONFIG_FILE_DEFAULT_LOCATION} and #{USER_CONFIG_FILE_DEFAULT_LOCATION} if config settings haven't been overridden'" do Puppet.features.stubs(:root?).returns(false) seq = sequence "load config files" FileTest.expects(:exist?).with(MAIN_CONFIG_FILE_DEFAULT_LOCATION).returns(false).in_sequence(seq) FileTest.expects(:exist?).with(USER_CONFIG_FILE_DEFAULT_LOCATION).returns(false).in_sequence(seq) @settings.send(:parse_config_files) end end end describe "when parsing its configuration" do before do @settings = Puppet::Settings.new @settings.stubs(:service_user_available?).returns true @file = make_absolute("/some/file") @userconfig = make_absolute("/test/userconfigfile") @settings.define_settings :section, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :section, :config => { :type => :file, :default => @file, :desc => "eh" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b" }, :three => { :default => "$one $two THREE", :desc => "c" } @settings.stubs(:user_config_file).returns(@userconfig) FileTest.stubs(:exist?).with(@file).returns true FileTest.stubs(:exist?).with(@userconfig).returns false end it "should not ignore the report setting" do @settings.define_settings :section, :report => { :default => "false", :desc => "a" } # This is needed in order to make sure we pass on windows myfile = File.expand_path(@file) @settings[:config] = myfile text = <<-CONF [puppetd] report=true CONF FileTest.expects(:exist?).with(myfile).returns(true) @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:report].should be_true end it "should use its current ':config' value for the file to parse" do myfile = make_absolute("/my/file") # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it @settings[:config] = myfile FileTest.expects(:exist?).with(myfile).returns(true) File.expects(:read).with(myfile).returns "[main]" @settings.send(:parse_config_files) end it "should not try to parse non-existent files" do FileTest.expects(:exist?).with(@file).returns false File.expects(:read).with(@file).never @settings.send(:parse_config_files) end it "should return values set in the configuration file" do text = "[main] one = fileval " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "fileval" end #484 - this should probably be in the regression area it "should not throw an exception on unknown parameters" do text = "[main]\nnosuchparam = mval\n" @settings.expects(:read_file).returns(text) lambda { @settings.send(:parse_config_files) }.should_not raise_error end it "should convert booleans in the configuration file into Ruby booleans" do text = "[main] one = true two = false " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == true @settings[:two].should == false end it "should convert integers in the configuration file into Ruby Integers" do text = "[main] one = 65 " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == 65 end it "should support specifying all metadata (owner, group, mode) in the configuration file" do @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") text = "[main] myfile = #{otherfile} {owner = service, group = service, mode = 644} " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} end it "should support specifying a single piece of metadata (owner, group, or mode) in the configuration file" do @settings.define_settings :section, :myfile => { :type => :file, :default => make_absolute("/myfile"), :desc => "a" } otherfile = make_absolute("/other/file") text = "[main] myfile = #{otherfile} {owner = service} " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser"} end it "should call hooks associated with values set in the configuration file" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = setval " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["setval"] end it "should not call the same hook for values set multiple times in the configuration file" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[user] mysetting = setval [main] mysetting = other " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["setval"] end it "should pass the environment-specific value to the hook when one is available" do values = [] @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} @settings.define_settings :section, :environment => { :default => "yay", :desc => "a" } @settings.define_settings :section, :environments => { :default => "yay,foo", :desc => "a" } text = "[main] mysetting = setval [yay] mysetting = other " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["other"] end it "should pass the interpolated value to the hook when one is available" do values = [] @settings.define_settings :section, :base => {:default => "yay", :desc => "a", :hook => proc { |v| values << v }} @settings.define_settings :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = $base/setval " @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) values.should == ["yay/setval"] end it "should allow empty values" do @settings.define_settings :section, :myarg => { :default => "myfile", :desc => "a" } text = "[main] myarg = " @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:myarg].should == "" end describe "and when reading a non-positive filetimeout value from the config file" do before do @settings.define_settings :foo, :filetimeout => { :default => 5, :desc => "eh" } somefile = "/some/file" text = "[main] filetimeout = -1 " File.expects(:read).with(somefile).returns(text) File.expects(:expand_path).with(somefile).returns somefile @settings[:config] = somefile end end end describe "when there are multiple config files" do let(:main_config_text) { "[main]\none = main\ntwo = main2" } let(:user_config_text) { "[main]\none = user\n" } let(:seq) { sequence "config_file_sequence" } before do Puppet.features.stubs(:root?).returns(false) @settings = Puppet::Settings.new @settings.define_settings(:section, { :one => { :default => "ONE", :desc => "a" }, :two => { :default => "TWO", :desc => "b" }, }) FileTest.expects(:exist?).with(MAIN_CONFIG_FILE_DEFAULT_LOCATION).returns(true).in_sequence(seq) @settings.expects(:read_file).with(MAIN_CONFIG_FILE_DEFAULT_LOCATION).returns(main_config_text).in_sequence(seq) FileTest.expects(:exist?).with(USER_CONFIG_FILE_DEFAULT_LOCATION).returns(true).in_sequence(seq) @settings.expects(:read_file).with(USER_CONFIG_FILE_DEFAULT_LOCATION).returns(user_config_text).in_sequence(seq) end it "should return values from the config file in the user's home dir before values set in the main configuration file" do @settings.send(:parse_config_files) @settings[:one].should == "user" end it "should return values from the main config file if they aren't overridden in the config file in the user's home dir" do @settings.send(:parse_config_files) @settings[:two].should == "main2" end end describe "when reparsing its configuration" do before do @file = make_absolute("/test/file") @userconfig = make_absolute("/test/userconfigfile") @settings = Puppet::Settings.new @settings.define_settings :section, :config => { :type => :file, :default => @file, :desc => "a" }, :one => { :default => "ONE", :desc => "a" }, :two => { :default => "$one TWO", :desc => "b" }, :three => { :default => "$one $two THREE", :desc => "c" } FileTest.stubs(:exist?).with(@file).returns true FileTest.stubs(:exist?).with(@userconfig).returns false @settings.stubs(:user_config_file).returns(@userconfig) end it "should use a LoadedFile instance to determine if the file has changed" do file = mock 'file' Puppet::Util::LoadedFile.expects(:new).with(@file).returns file file.expects(:changed?) @settings.stubs(:parse) @settings.reparse_config_files end it "should not create the LoadedFile instance and should not parse if the file does not exist" do FileTest.expects(:exist?).with(@file).returns false Puppet::Util::LoadedFile.expects(:new).never @settings.expects(:parse_config_files).never @settings.reparse_config_files end it "should not reparse if the file has not changed" do file = mock 'file' Puppet::Util::LoadedFile.expects(:new).with(@file).returns file file.expects(:changed?).returns false @settings.expects(:parse_config_files).never @settings.reparse_config_files end it "should reparse if the file has changed" do file = stub 'file', :file => @file Puppet::Util::LoadedFile.expects(:new).with(@file).returns file file.expects(:changed?).returns true @settings.expects(:parse_config_files) @settings.reparse_config_files end it "should replace in-memory values with on-file values" do # Init the value text = "[main]\none = disk-init\n" file = mock 'file' file.stubs(:changed?).returns(true) file.stubs(:file).returns(@file) @settings[:one] = "init" @settings.files = [file] # Now replace the value text = "[main]\none = disk-replace\n" # This is kinda ridiculous - the reason it parses twice is that # it goes to parse again when we ask for the value, because the # mock always says it should get reparsed. @settings.stubs(:read_file).returns(text) @settings.reparse_config_files @settings[:one].should == "disk-replace" end it "should retain parameters set by cli when configuration files are reparsed" do @settings.handlearg("--one", "clival") text = "[main]\none = on-disk\n" @settings.stubs(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "clival" end it "should remove in-memory values that are no longer set in the file" do # Init the value text = "[main]\none = disk-init\n" @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "disk-init" # Now replace the value text = "[main]\ntwo = disk-replace\n" @settings.expects(:read_file).returns(text) @settings.send(:parse_config_files) # The originally-overridden value should be replaced with the default @settings[:one].should == "ONE" # and we should now have the new value in memory @settings[:two].should == "disk-replace" end it "should retain in-memory values if the file has a syntax error" do # Init the value text = "[main]\none = initial-value\n" @settings.expects(:read_file).with(@file).returns(text) @settings.send(:parse_config_files) @settings[:one].should == "initial-value" # Now replace the value with something bogus text = "[main]\nkenny = killed-by-what-follows\n1 is 2, blah blah florp\n" @settings.expects(:read_file).with(@file).returns(text) @settings.send(:parse_config_files) # The originally-overridden value should not be replaced with the default @settings[:one].should == "initial-value" # and we should not have the new value in memory @settings[:kenny].should be_nil end end it "should provide a method for creating a catalog of resources from its configuration" do Puppet::Settings.new.should respond_to(:to_catalog) end describe "when creating a catalog" do before do @settings = Puppet::Settings.new @settings.stubs(:service_user_available?).returns true @prefix = Puppet.features.posix? ? "" : "C:" end it "should add all file resources to the catalog if no sections have been specified" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a"}, :seconddir => { :type => :directory, :default => @prefix+"/seconddir", :desc => "a"} @settings.define_settings :other, :otherdir => { :type => :directory, :default => @prefix+"/otherdir", :desc => "a" } catalog = @settings.to_catalog [@prefix+"/maindir", @prefix+"/seconddir", @prefix+"/otherdir"].each do |path| catalog.resource(:file, path).should be_instance_of(Puppet::Resource) end end it "should add only files in the specified sections if section names are provided" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } @settings.define_settings :other, :otherdir => { :type => :directory, :default => @prefix+"/otherdir", :desc => "a" } catalog = @settings.to_catalog(:main) catalog.resource(:file, @prefix+"/otherdir").should be_nil catalog.resource(:file, @prefix+"/maindir").should be_instance_of(Puppet::Resource) end it "should not try to add the same file twice" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } @settings.define_settings :other, :otherdir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } lambda { @settings.to_catalog }.should_not raise_error end it "should ignore files whose :to_resource method returns nil" do @settings.define_settings :main, :maindir => { :type => :directory, :default => @prefix+"/maindir", :desc => "a" } @settings.setting(:maindir).expects(:to_resource).returns nil Puppet::Resource::Catalog.any_instance.expects(:add_resource).never @settings.to_catalog end describe "on Microsoft Windows" do before :each do Puppet.features.stubs(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns true @settings.define_settings :foo, :mkusers => { :type => :boolean, :default => true, :desc => "e" }, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :other, :otherdir => { :type => :directory, :default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} @catalog = @settings.to_catalog end it "it should not add users and groups to the catalog" do @catalog.resource(:user, "suser").should be_nil @catalog.resource(:group, "sgroup").should be_nil end end describe "when adding users and groups to the catalog" do before do Puppet.features.stubs(:root?).returns true Puppet.features.stubs(:microsoft_windows?).returns false @settings.define_settings :foo, :mkusers => { :type => :boolean, :default => true, :desc => "e" }, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :other, :otherdir => {:type => :directory, :default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} @catalog = @settings.to_catalog end it "should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root" do @catalog.resource(:user, "suser").should be_instance_of(Puppet::Resource) @catalog.resource(:group, "sgroup").should be_instance_of(Puppet::Resource) end it "should only add users and groups to the catalog from specified sections" do @settings.define_settings :yay, :yaydir => { :type => :directory, :default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} catalog = @settings.to_catalog(:other) catalog.resource(:user, "jane").should be_nil catalog.resource(:group, "billy").should be_nil end it "should not add users or groups to the catalog if :mkusers not running as root" do Puppet.features.stubs(:root?).returns false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is not a valid setting" do Puppet.features.stubs(:root?).returns true settings = Puppet::Settings.new settings.define_settings :other, :otherdir => {:type => :directory, :default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} catalog = settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is a valid setting but is disabled" do @settings[:mkusers] = false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not try to add users or groups to the catalog twice" do @settings.define_settings :yay, :yaydir => {:type => :directory, :default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} # This would fail if users/groups were added twice lambda { @settings.to_catalog }.should_not raise_error end it "should set :ensure to :present on each created user and group" do @catalog.resource(:user, "suser")[:ensure].should == :present @catalog.resource(:group, "sgroup")[:ensure].should == :present end it "should set each created user's :gid to the service group" do @settings.to_catalog.resource(:user, "suser")[:gid].should == "sgroup" end it "should not attempt to manage the root user" do Puppet.features.stubs(:root?).returns true @settings.define_settings :foo, :foodir => {:type => :directory, :default => "/foodir", :desc => "a", :owner => "root", :group => "service"} @settings.to_catalog.resource(:user, "root").should be_nil end end end it "should be able to be converted to a manifest" do Puppet::Settings.new.should respond_to(:to_manifest) end describe "when being converted to a manifest" do it "should produce a string with the code for each resource joined by two carriage returns" do @settings = Puppet::Settings.new @settings.define_settings :main, :maindir => { :type => :directory, :default => "/maindir", :desc => "a"}, :seconddir => { :type => :directory, :default => "/seconddir", :desc => "a"} main = stub 'main_resource', :ref => "File[/maindir]" main.expects(:to_manifest).returns "maindir" second = stub 'second_resource', :ref => "File[/seconddir]" second.expects(:to_manifest).returns "seconddir" @settings.setting(:maindir).expects(:to_resource).returns main @settings.setting(:seconddir).expects(:to_resource).returns second @settings.to_manifest.split("\n\n").sort.should == %w{maindir seconddir} end end describe "when using sections of the configuration to manage the local host" do before do @settings = Puppet::Settings.new @settings.stubs(:service_user_available?).returns true @settings.define_settings :main, :noop => { :default => false, :desc => "", :type => :boolean } @settings.define_settings :main, :maindir => { :type => :directory, :default => make_absolute("/maindir"), :desc => "a" }, :seconddir => { :type => :directory, :default => make_absolute("/seconddir"), :desc => "a"} @settings.define_settings :main, :user => { :default => "suser", :desc => "doc" }, :group => { :default => "sgroup", :desc => "doc" } @settings.define_settings :other, :otherdir => {:type => :directory, :default => make_absolute("/otherdir"), :desc => "a", :owner => "service", :group => "service", :mode => 0755} @settings.define_settings :third, :thirddir => { :type => :directory, :default => make_absolute("/thirddir"), :desc => "b"} @settings.define_settings :files, :myfile => {:type => :file, :default => make_absolute("/myfile"), :desc => "a", :mode => 0755} end it "should provide a method that writes files with the correct modes" do @settings.should respond_to(:write) end it "should provide a method that creates directories with the correct modes" do Puppet::Util::SUIDManager.expects(:asuser).with("suser", "sgroup").yields Dir.expects(:mkdir).with(make_absolute("/otherdir"), 0755) @settings.mkdir(:otherdir) end it "should create a catalog with the specified sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should canonicalize the sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use("main", "other") end it "should ignore sections that have already been used" do @settings.expects(:to_catalog).with(:main).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main) @settings.expects(:to_catalog).with(:other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should ignore tags and schedules when creating files and directories" it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do pending "Not converted from test/unit yet" end it "should convert the created catalog to a RAL catalog" do @catalog = Puppet::Resource::Catalog.new("foo") @settings.expects(:to_catalog).with(:main).returns @catalog @catalog.expects(:to_ral).returns @catalog @settings.use(:main) end it "should specify that it is not managing a host catalog" do catalog = Puppet::Resource::Catalog.new("foo") catalog.expects(:apply) @settings.expects(:to_catalog).returns catalog catalog.stubs(:to_ral).returns catalog catalog.expects(:host_config=).with false @settings.use(:main) end it "should support a method for re-using all currently used sections" do @settings.expects(:to_catalog).with(:main, :third).times(2).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :third) @settings.reuse end it "should fail with an appropriate message if any resources fail" do @catalog = Puppet::Resource::Catalog.new("foo") @catalog.stubs(:to_ral).returns @catalog @settings.expects(:to_catalog).returns @catalog @trans = mock("transaction") @catalog.expects(:apply).yields(@trans) @trans.expects(:any_failed?).returns(true) report = mock 'report' @trans.expects(:report).returns report log = mock 'log', :to_s => "My failure", :level => :err report.expects(:logs).returns [log] @settings.expects(:raise).with { |msg| msg.include?("My failure") } @settings.use(:whatever) end end describe "when dealing with printing configs" do before do @settings = Puppet::Settings.new #these are the magic default values @settings.stubs(:value).with(:configprint).returns("") @settings.stubs(:value).with(:genconfig).returns(false) @settings.stubs(:value).with(:genmanifest).returns(false) @settings.stubs(:value).with(:environment).returns(nil) end describe "when checking print_config?" do it "should return false when the :configprint, :genconfig and :genmanifest are not set" do @settings.print_configs?.should be_false end it "should return true when :configprint has a value" do @settings.stubs(:value).with(:configprint).returns("something") @settings.print_configs?.should be_true end it "should return true when :genconfig has a value" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.print_configs?.should be_true end it "should return true when :genmanifest has a value" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.print_configs?.should be_true end end describe "when printing configs" do describe "when :configprint has a value" do it "should call print_config_options" do @settings.stubs(:value).with(:configprint).returns("something") @settings.expects(:print_config_options) @settings.print_configs end it "should get the value of the option using the environment" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.expects(:value).with(:environment).returns("env") @settings.expects(:value).with("something", "env").returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs end it "should print the value of the option" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.expects(:puts).with("foo") @settings.print_configs end it "should print the value pairs if there are multiple options" do @settings.stubs(:value).with(:configprint).returns("bar,baz") @settings.stubs(:include?).with("bar").returns(true) @settings.stubs(:include?).with("baz").returns(true) @settings.stubs(:value).with("bar", nil).returns("foo") @settings.stubs(:value).with("baz", nil).returns("fud") @settings.expects(:puts).with("bar = foo") @settings.expects(:puts).with("baz = fud") @settings.print_configs end it "should print a whole bunch of stuff if :configprint = all" it "should return true after printing" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs.should be_true end it "should return false if a config param is not found" do @settings.stubs :puts @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(false) @settings.print_configs.should be_false end end describe "when genconfig is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.expects(:to_config) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.stubs(:to_config) @settings.print_configs.should be_true end end describe "when genmanifest is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.expects(:to_manifest) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.stubs(:to_manifest) @settings.print_configs.should be_true end end end end describe "when determining if the service user is available" do it "should return false if there is no user setting" do Puppet::Settings.new.should_not be_service_user_available end it "should return false if the user provider says the user is missing" do settings = Puppet::Settings.new settings.define_settings :main, :user => { :default => "foo", :desc => "doc" } user = mock 'user' user.expects(:exists?).returns false Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user settings.should_not be_service_user_available end it "should return true if the user provider says the user is present" do settings = Puppet::Settings.new settings.define_settings :main, :user => { :default => "foo", :desc => "doc" } user = mock 'user' user.expects(:exists?).returns true Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user settings.should be_service_user_available end it "should cache the result" end describe "#writesub" do it "should only pass valid arguments to File.open" do settings = Puppet::Settings.new settings.stubs(:get_config_file_default).with(:privatekeydir).returns(OpenStruct.new(:mode => "750")) File.expects(:open).with("/path/to/keydir", "w", 750).returns true settings.writesub(:privatekeydir, "/path/to/keydir") end end describe "when dealing with command-line options" do let(:settings) { Puppet::Settings.new } it "should get options from Puppet.settings.optparse_addargs" do settings.expects(:optparse_addargs).returns([]) settings.send(:parse_global_options, []) end it "should add options to OptionParser" do settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option", :NONE]]) settings.expects(:handlearg).with("--option", true) settings.send(:parse_global_options, ["--option"]) end it "should not die if it sees an unrecognized option, because the app/face may handle it later" do expect { settings.send(:parse_global_options, ["--topuppet", "value"]) } .to_not raise_error end it "should not pass an unrecognized option to handleargs" do settings.expects(:handlearg).with("--topuppet", "value").never expect { settings.send(:parse_global_options, ["--topuppet", "value"]) } .to_not raise_error end it "should pass valid puppet settings options to handlearg even if they appear after an unrecognized option" do settings.stubs(:optparse_addargs).returns( [["--option","-o", "Funny Option", :NONE]]) settings.expects(:handlearg).with("--option", true) settings.send(:parse_global_options, ["--invalidoption", "--option"]) end it "should transform boolean option to normal form" do Puppet::Settings.clean_opt("--[no-]option", true).should == ["--option", true] end it "should transform boolean option to no- form" do Puppet::Settings.clean_opt("--[no-]option", false).should == ["--no-option", false] end end describe "default_certname" do describe "using hostname and domainname" do before :each do Puppet::Settings.stubs(:hostname_fact).returns("testhostname") Puppet::Settings.stubs(:domain_fact).returns("domain.test.") end it "should use both to generate fqdn" do Puppet::Settings.default_certname.should =~ /testhostname\.domain\.test/ end it "should remove trailing dots from fqdn" do Puppet::Settings.default_certname.should == 'testhostname.domain.test' end end describe "using just hostname" do before :each do Puppet::Settings.stubs(:hostname_fact).returns("testhostname") Puppet::Settings.stubs(:domain_fact).returns("") end it "should use only hostname to generate fqdn" do Puppet::Settings.default_certname.should == "testhostname" end it "should removing trailing dots from fqdn" do Puppet::Settings.default_certname.should == "testhostname" end end end end diff --git a/spec/unit/simple_graph_spec.rb b/spec/unit/simple_graph_spec.rb index 28f21bddd..5cb776602 100755 --- a/spec/unit/simple_graph_spec.rb +++ b/spec/unit/simple_graph_spec.rb @@ -1,933 +1,933 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/simple_graph' describe Puppet::SimpleGraph do it "should return the number of its vertices as its length" do @graph = Puppet::SimpleGraph.new @graph.add_vertex("one") @graph.add_vertex("two") @graph.size.should == 2 end it "should consider itself a directed graph" do Puppet::SimpleGraph.new.directed?.should be_true end it "should provide a method for reversing the graph" do @graph = Puppet::SimpleGraph.new @graph.add_edge(:one, :two) @graph.reversal.edge?(:two, :one).should be_true end it "should be able to produce a dot graph" do @graph = Puppet::SimpleGraph.new @graph.add_edge(:one, :two) proc { @graph.to_dot_graph }.should_not raise_error end describe "when managing vertices" do before do @graph = Puppet::SimpleGraph.new end it "should provide a method to add a vertex" do @graph.add_vertex(:test) @graph.vertex?(:test).should be_true end it "should reset its reversed graph when vertices are added" do rev = @graph.reversal @graph.add_vertex(:test) @graph.reversal.should_not equal(rev) end it "should ignore already-present vertices when asked to add a vertex" do @graph.add_vertex(:test) proc { @graph.add_vertex(:test) }.should_not raise_error end it "should return true when asked if a vertex is present" do @graph.add_vertex(:test) @graph.vertex?(:test).should be_true end it "should return false when asked if a non-vertex is present" do @graph.vertex?(:test).should be_false end it "should return all set vertices when asked" do @graph.add_vertex(:one) @graph.add_vertex(:two) @graph.vertices.length.should == 2 @graph.vertices.should include(:one) @graph.vertices.should include(:two) end it "should remove a given vertex when asked" do @graph.add_vertex(:one) @graph.remove_vertex!(:one) @graph.vertex?(:one).should be_false end it "should do nothing when a non-vertex is asked to be removed" do proc { @graph.remove_vertex!(:one) }.should_not raise_error end end describe "when managing edges" do before do @graph = Puppet::SimpleGraph.new end it "should provide a method to test whether a given vertex pair is an edge" do @graph.should respond_to(:edge?) end it "should reset its reversed graph when edges are added" do rev = @graph.reversal @graph.add_edge(:one, :two) @graph.reversal.should_not equal(rev) end it "should provide a method to add an edge as an instance of the edge class" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.edge?(:one, :two).should be_true end it "should provide a method to add an edge by specifying the two vertices" do @graph.add_edge(:one, :two) @graph.edge?(:one, :two).should be_true end it "should provide a method to add an edge by specifying the two vertices and a label" do @graph.add_edge(:one, :two, :callback => :awesome) @graph.edge?(:one, :two).should be_true end describe "when retrieving edges between two nodes" do it "should handle the case of nodes not in the graph" do @graph.edges_between(:one, :two).should == [] end it "should handle the case of nodes with no edges between them" do @graph.add_vertex(:one) @graph.add_vertex(:two) @graph.edges_between(:one, :two).should == [] end it "should handle the case of nodes connected by a single edge" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.edges_between(:one, :two).length.should == 1 @graph.edges_between(:one, :two)[0].should equal(edge) end it "should handle the case of nodes connected by multiple edges" do edge1 = Puppet::Relationship.new(:one, :two, :callback => :foo) edge2 = Puppet::Relationship.new(:one, :two, :callback => :bar) @graph.add_edge(edge1) @graph.add_edge(edge2) Set.new(@graph.edges_between(:one, :two)).should == Set.new([edge1, edge2]) end end it "should add the edge source as a vertex if it is not already" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.vertex?(:one).should be_true end it "should add the edge target as a vertex if it is not already" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.vertex?(:two).should be_true end it "should return all edges as edge instances when asked" do one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) @graph.add_edge(one) @graph.add_edge(two) edges = @graph.edges edges.should be_instance_of(Array) edges.length.should == 2 edges.should include(one) edges.should include(two) end it "should remove an edge when asked" do edge = Puppet::Relationship.new(:one, :two) @graph.add_edge(edge) @graph.remove_edge!(edge) @graph.edge?(edge.source, edge.target).should be_false end it "should remove all related edges when a vertex is removed" do one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) @graph.add_edge(one) @graph.add_edge(two) @graph.remove_vertex!(:two) @graph.edge?(:one, :two).should be_false @graph.edge?(:two, :three).should be_false @graph.edges.length.should == 0 end end describe "when finding adjacent vertices" do before do @graph = Puppet::SimpleGraph.new @one_two = Puppet::Relationship.new(:one, :two) @two_three = Puppet::Relationship.new(:two, :three) @one_three = Puppet::Relationship.new(:one, :three) @graph.add_edge(@one_two) @graph.add_edge(@one_three) @graph.add_edge(@two_three) end it "should return adjacent vertices" do adj = @graph.adjacent(:one) adj.should be_include(:three) adj.should be_include(:two) end it "should default to finding :out vertices" do @graph.adjacent(:two).should == [:three] end it "should support selecting :in vertices" do @graph.adjacent(:two, :direction => :in).should == [:one] end it "should default to returning the matching vertices as an array of vertices" do @graph.adjacent(:two).should == [:three] end it "should support returning an array of matching edges" do @graph.adjacent(:two, :type => :edges).should == [@two_three] end # Bug #2111 it "should not consider a vertex adjacent just because it was asked about previously" do @graph = Puppet::SimpleGraph.new @graph.add_vertex("a") @graph.add_vertex("b") @graph.edge?("a", "b") @graph.adjacent("a").should == [] end end describe "when clearing" do before do @graph = Puppet::SimpleGraph.new one = Puppet::Relationship.new(:one, :two) two = Puppet::Relationship.new(:two, :three) @graph.add_edge(one) @graph.add_edge(two) @graph.clear end it "should remove all vertices" do @graph.vertices.should be_empty end it "should remove all edges" do @graph.edges.should be_empty end end describe "when reversing graphs" do before do @graph = Puppet::SimpleGraph.new end it "should provide a method for reversing the graph" do @graph.add_edge(:one, :two) @graph.reversal.edge?(:two, :one).should be_true end it "should add all vertices to the reversed graph" do @graph.add_edge(:one, :two) @graph.vertex?(:one).should be_true @graph.vertex?(:two).should be_true end it "should retain labels on edges" do @graph.add_edge(:one, :two, :callback => :awesome) edge = @graph.reversal.edges_between(:two, :one)[0] edge.label.should == {:callback => :awesome} end end describe "when reporting cycles in the graph" do before do @graph = Puppet::SimpleGraph.new end # This works with `add_edges` to auto-vivify the resource instances. let :vertex do Hash.new do |hash, key| hash[key] = Puppet::Type.type(:notify).new(:name => key.to_s) end end def add_edges(hash) hash.each do |a,b| @graph.add_edge(vertex[a], vertex[b]) end end def simplify(cycles) cycles.map do |x| x.map do |y| y.to_s.match(/^Notify\[(.*)\]$/)[1] end end end it "should fail on two-vertex loops" do add_edges :a => :b, :b => :a proc { @graph.report_cycles_in_graph }.should raise_error(Puppet::Error) end it "should fail on multi-vertex loops" do add_edges :a => :b, :b => :c, :c => :a proc { @graph.report_cycles_in_graph }.should raise_error(Puppet::Error) end it "should fail when a larger tree contains a small cycle" do add_edges :a => :b, :b => :a, :c => :a, :d => :c proc { @graph.report_cycles_in_graph }.should raise_error(Puppet::Error) end it "should succeed on trees with no cycles" do add_edges :a => :b, :b => :e, :c => :a, :d => :c proc { @graph.report_cycles_in_graph }.should_not raise_error end it "should produce the correct relationship text" do add_edges :a => :b, :b => :a # cycle detection starts from a or b randomly # so we need to check for either ordering in the error message want = %r{Found 1 dependency cycle:\n\((Notify\[a\] => Notify\[b\] => Notify\[a\]|Notify\[b\] => Notify\[a\] => Notify\[b\])\)\nTry} expect { @graph.report_cycles_in_graph }.to raise_error(Puppet::Error, want) end it "cycle discovery should be the minimum cycle for a simple graph" do add_edges "a" => "b" add_edges "b" => "a" add_edges "b" => "c" cycles = nil expect { cycles = @graph.find_cycles_in_graph }.should_not raise_error simplify(cycles).should be == [["a", "b"]] end it "cycle discovery should handle two distinct cycles" do add_edges "a" => "a1", "a1" => "a" add_edges "b" => "b1", "b1" => "b" cycles = nil expect { cycles = @graph.find_cycles_in_graph }.should_not raise_error simplify(cycles).should be == [["a1", "a"], ["b1", "b"]] end it "cycle discovery should handle two cycles in a connected graph" do add_edges "a" => "b", "b" => "c", "c" => "d" add_edges "a" => "a1", "a1" => "a" add_edges "c" => "c1", "c1" => "c2", "c2" => "c3", "c3" => "c" cycles = nil expect { cycles = @graph.find_cycles_in_graph }.should_not raise_error simplify(cycles).should be == [%w{a1 a}, %w{c1 c2 c3 c}] end it "cycle discovery should handle a complicated cycle" do add_edges "a" => "b", "b" => "c" add_edges "a" => "c" add_edges "c" => "c1", "c1" => "a" add_edges "c" => "c2", "c2" => "b" cycles = nil expect { cycles = @graph.find_cycles_in_graph }.should_not raise_error simplify(cycles).should be == [%w{a b c1 c2 c}] end it "cycle discovery should not fail with large data sets" do limit = 3000 (1..(limit - 1)).each do |n| add_edges n.to_s => (n+1).to_s end cycles = nil expect { cycles = @graph.find_cycles_in_graph }.should_not raise_error simplify(cycles).should be == [] end it "path finding should work with a simple cycle" do add_edges "a" => "b", "b" => "c", "c" => "a" cycles = @graph.find_cycles_in_graph paths = @graph.paths_in_cycle(cycles.first, 100) simplify(paths).should be == [%w{a b c a}] end it "path finding should work with two independent cycles" do add_edges "a" => "b1" add_edges "a" => "b2" add_edges "b1" => "a", "b2" => "a" cycles = @graph.find_cycles_in_graph cycles.length.should be == 1 paths = @graph.paths_in_cycle(cycles.first, 100) simplify(paths).should be == [%w{a b1 a}, %w{a b2 a}] end it "path finding should prefer shorter paths in cycles" do add_edges "a" => "b", "b" => "c", "c" => "a" add_edges "b" => "a" cycles = @graph.find_cycles_in_graph cycles.length.should be == 1 paths = @graph.paths_in_cycle(cycles.first, 100) simplify(paths).should be == [%w{a b a}, %w{a b c a}] end it "path finding should respect the max_path value" do (1..20).each do |n| add_edges "a" => "b#{n}", "b#{n}" => "a" end cycles = @graph.find_cycles_in_graph cycles.length.should be == 1 (1..20).each do |n| paths = @graph.paths_in_cycle(cycles.first, n) paths.length.should be == n end paths = @graph.paths_in_cycle(cycles.first, 21) paths.length.should be == 20 end end describe "when writing dot files" do before do @graph = Puppet::SimpleGraph.new @name = :test @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") end it "should only write when graphing is enabled" do File.expects(:open).with(@file).never Puppet[:graph] = false @graph.write_graph(@name) end it "should write a dot file based on the passed name" do File.expects(:open).with(@file, "w").yields(stub("file", :puts => nil)) @graph.expects(:to_dot).with("name" => @name.to_s.capitalize) Puppet[:graph] = true @graph.write_graph(@name) end after do Puppet.settings.clear end end describe Puppet::SimpleGraph do before do @graph = Puppet::SimpleGraph.new end it "should correctly clear vertices and edges when asked" do @graph.add_edge("a", "b") @graph.add_vertex "c" @graph.clear @graph.vertices.should be_empty @graph.edges.should be_empty end end describe "when matching edges" do before do @graph = Puppet::SimpleGraph.new # The Ruby 1.8 semantics for String#[] are that treating it like an # array and asking for `"a"[:whatever]` returns `nil`. Ruby 1.9 # enforces that your index has to be numeric. # # Now, the real object here, a resource, implements [] and does # something sane, but we don't care about any of the things that get # asked for. Right now, anyway. # # So, in 1.8 we could just pass a string and it worked. For 1.9 we can # fake it well enough by stubbing out the operator to return nil no # matter what input we give. --daniel 2012-03-11 resource = "a" resource.stubs(:[]) @event = Puppet::Transaction::Event.new(:name => :yay, :resource => resource) @none = Puppet::Transaction::Event.new(:name => :NONE, :resource => resource) @edges = {} @edges["a/b"] = Puppet::Relationship.new("a", "b", {:event => :yay, :callback => :refresh}) @edges["a/c"] = Puppet::Relationship.new("a", "c", {:event => :yay, :callback => :refresh}) @graph.add_edge(@edges["a/b"]) end it "should match edges whose source matches the source of the event" do @graph.matching_edges(@event).should == [@edges["a/b"]] end it "should match always match nothing when the event is :NONE" do @graph.matching_edges(@none).should be_empty end it "should match multiple edges" do @graph.add_edge(@edges["a/c"]) edges = @graph.matching_edges(@event) edges.should be_include(@edges["a/b"]) edges.should be_include(@edges["a/c"]) end end describe "when determining dependencies" do before do @graph = Puppet::SimpleGraph.new @graph.add_edge("a", "b") @graph.add_edge("a", "c") @graph.add_edge("b", "d") end it "should find all dependents when they are on multiple levels" do @graph.dependents("a").sort.should == %w{b c d}.sort end it "should find single dependents" do @graph.dependents("b").sort.should == %w{d}.sort end it "should return an empty array when there are no dependents" do @graph.dependents("c").sort.should == [].sort end it "should find all dependencies when they are on multiple levels" do @graph.dependencies("d").sort.should == %w{a b} end it "should find single dependencies" do @graph.dependencies("c").sort.should == %w{a} end it "should return an empty array when there are no dependencies" do @graph.dependencies("a").sort.should == [] end end require 'puppet/util/graph' class Container < Puppet::Type::Component include Puppet::Util::Graph include Enumerable attr_accessor :name def each @children.each do |c| yield c end end def initialize(name, ary) @name = name @children = ary end def push(*ary) ary.each { |c| @children.push(c)} end def to_s @name end def ref "Container[#{self}]" end end require "puppet/resource/catalog" describe "when splicing the graph" do def container_graph @one = Container.new("one", %w{a b}) @two = Container.new("two", ["c", "d"]) @three = Container.new("three", ["i", "j"]) @middle = Container.new("middle", ["e", "f", @two]) @top = Container.new("top", ["g", "h", @middle, @one, @three]) @empty = Container.new("empty", []) @whit = Puppet::Type.type(:whit) @stage = Puppet::Type.type(:stage).new(:name => "foo") @contgraph = @top.to_graph(Puppet::Resource::Catalog.new) # We have to add the container to the main graph, else it won't # be spliced in the dependency graph. @contgraph.add_vertex(@empty) end def containers @contgraph.vertices.select { |x| !x.is_a? String } end def contents_of(x) @contgraph.direct_dependents_of(x) end def dependency_graph @depgraph = Puppet::SimpleGraph.new @contgraph.vertices.each do |v| @depgraph.add_vertex(v) end # We have to specify a relationship to our empty container, else it # never makes it into the dep graph in the first place. @explicit_dependencies = {@one => @two, "f" => "c", "h" => @middle, "c" => @empty} @explicit_dependencies.each do |source, target| @depgraph.add_edge(source, target, :callback => :refresh) end end def splice @contgraph.splice!(@depgraph) end def whit_called(name) x = @depgraph.vertices.find { |v| v.is_a?(@whit) && v.name =~ /#{Regexp.escape(name)}/ } x.should_not be_nil def x.to_s "Whit[#{name}]" end def x.inspect to_s end x end def admissible_sentinel_of(x) @depgraph.vertex?(x) ? x : whit_called("admissible_#{x.ref}") end def completed_sentinel_of(x) @depgraph.vertex?(x) ? x : whit_called("completed_#{x.ref}") end before do container_graph dependency_graph splice end # This is the real heart of splicing -- replacing all containers X in our # relationship graph with a pair of whits { admissible_X and completed_X } # such that that # # 0) completed_X depends on admissible_X # 1) contents of X each depend on admissible_X # 2) completed_X depends on each on the contents of X # 3) everything which depended on X depends on completed_X # 4) admissible_X depends on everything X depended on # 5) the containers and their edges must be removed # # Note that this requires attention to the possible case of containers # which contain or depend on other containers. # # Point by point: # 0) completed_X depends on admissible_X # it "every container's completed sentinel should depend on its admissible sentinel" do containers.each { |container| @depgraph.path_between(admissible_sentinel_of(container),completed_sentinel_of(container)).should be } end # 1) contents of X each depend on admissible_X # it "all contained objects should depend on their container's admissible sentinel" do containers.each { |container| contents_of(container).each { |leaf| @depgraph.should be_edge(admissible_sentinel_of(container),admissible_sentinel_of(leaf)) } } end # 2) completed_X depends on each on the contents of X # it "completed sentinels should depend on their container's contents" do containers.each { |container| contents_of(container).each { |leaf| @depgraph.should be_edge(completed_sentinel_of(leaf),completed_sentinel_of(container)) } } end # # 3) everything which depended on X depends on completed_X # # 4) admissible_X depends on everything X depended on # 5) the containers and their edges must be removed # it "should remove all Container objects from the dependency graph" do @depgraph.vertices.find_all { |v| v.is_a?(Container) }.should be_empty end it "should remove all Stage resources from the dependency graph" do @depgraph.vertices.find_all { |v| v.is_a?(Puppet::Type.type(:stage)) }.should be_empty end it "should no longer contain anything but the non-container objects" do @depgraph.vertices.find_all { |v| ! v.is_a?(String) and ! v.is_a?(@whit)}.should be_empty end it "should retain labels on non-containment edges" do @explicit_dependencies.each { |f,t| @depgraph.edges_between(completed_sentinel_of(f),admissible_sentinel_of(t))[0].label.should == {:callback => :refresh} } end it "should not add labels to edges that have none" do @depgraph.add_edge(@two, @three) splice @depgraph.path_between("c", "i").any? {|segment| segment.all? {|e| e.label == {} }}.should be end it "should copy labels over edges that have none" do @depgraph.add_edge("c", @three, {:callback => :refresh}) splice # And make sure the label got copied. @depgraph.path_between("c", "i").flatten.select {|e| e.label == {:callback => :refresh} }.should_not be_empty end it "should not replace a label with a nil label" do # Lastly, add some new label-less edges and make sure the label stays. @depgraph.add_edge(@middle, @three) @depgraph.add_edge("c", @three, {:callback => :refresh}) splice @depgraph.path_between("c","i").flatten.select {|e| e.label == {:callback => :refresh} }.should_not be_empty end it "should copy labels to all created edges" do @depgraph.add_edge(@middle, @three) @depgraph.add_edge("c", @three, {:callback => :refresh}) splice @three.each do |child| edge = Puppet::Relationship.new("c", child) (path = @depgraph.path_between(edge.source, edge.target)).should be path.should_not be_empty path.flatten.select {|e| e.label == {:callback => :refresh} }.should_not be_empty end end end it "should serialize to YAML using the old format by default" do Puppet::SimpleGraph.use_new_yaml_format.should == false end describe "(yaml tests)" do def empty_graph(graph) end def one_vertex_graph(graph) graph.add_vertex(:a) end def graph_without_edges(graph) [:a, :b, :c].each { |x| graph.add_vertex(x) } end def one_edge_graph(graph) graph.add_edge(:a, :b) end def many_edge_graph(graph) graph.add_edge(:a, :b) graph.add_edge(:a, :c) graph.add_edge(:b, :d) graph.add_edge(:c, :d) end def labeled_edge_graph(graph) graph.add_edge(:a, :b, :callback => :foo, :event => :bar) end def overlapping_edge_graph(graph) graph.add_edge(:a, :b, :callback => :foo, :event => :bar) graph.add_edge(:a, :b, :callback => :biz, :event => :baz) end def self.all_test_graphs [:empty_graph, :one_vertex_graph, :graph_without_edges, :one_edge_graph, :many_edge_graph, :labeled_edge_graph, :overlapping_edge_graph] end def object_ids(enumerable) # Return a sorted list of the object id's of the elements of an # enumerable. enumerable.collect { |x| x.object_id }.sort end def graph_to_yaml(graph, which_format) previous_use_new_yaml_format = Puppet::SimpleGraph.use_new_yaml_format Puppet::SimpleGraph.use_new_yaml_format = (which_format == :new) ZAML.dump(graph) ensure Puppet::SimpleGraph.use_new_yaml_format = previous_use_new_yaml_format end # Test serialization of graph to YAML. [:old, :new].each do |which_format| all_test_graphs.each do |graph_to_test| it "should be able to serialize #{graph_to_test} to YAML (#{which_format} format)", :if => (RUBY_VERSION[0,3] == '1.8' or YAML::ENGINE.syck?) do graph = Puppet::SimpleGraph.new send(graph_to_test, graph) yaml_form = graph_to_yaml(graph, which_format) # Hack the YAML so that objects in the Puppet namespace get # changed to YAML::DomainType objects. This lets us inspect # the serialized objects easily without invoking any # yaml_initialize hooks. yaml_form.gsub!('!ruby/object:Puppet::', '!hack/object:Puppet::') serialized_object = YAML.load(yaml_form) # Check that the object contains instance variables @edges and # @vertices only. @reversal is also permitted, but we don't # check it, because it is going to be phased out. serialized_object.type_id.should == 'object:Puppet::SimpleGraph' serialized_object.value.keys.reject { |x| x == 'reversal' }.sort.should == ['edges', 'vertices'] # Check edges by forming a set of tuples (source, target, # callback, event) based on the graph and the YAML and make sure # they match. edges = serialized_object.value['edges'] edges.should be_a(Array) expected_edge_tuples = graph.edges.collect { |edge| [edge.source, edge.target, edge.callback, edge.event] } actual_edge_tuples = edges.collect do |edge| edge.type_id.should == 'object:Puppet::Relationship' %w{source target}.each { |x| edge.value.keys.should include(x) } edge.value.keys.each { |x| ['source', 'target', 'callback', 'event'].should include(x) } %w{source target callback event}.collect { |x| edge.value[x] } end Set.new(actual_edge_tuples).should == Set.new(expected_edge_tuples) actual_edge_tuples.length.should == expected_edge_tuples.length # Check vertices one by one. vertices = serialized_object.value['vertices'] if which_format == :old vertices.should be_a(Hash) Set.new(vertices.keys).should == Set.new(graph.vertices) vertices.each do |key, value| value.type_id.should == 'object:Puppet::SimpleGraph::VertexWrapper' value.value.keys.sort.should == %w{adjacencies vertex} value.value['vertex'].should equal(key) adjacencies = value.value['adjacencies'] adjacencies.should be_a(Hash) Set.new(adjacencies.keys).should == Set.new([:in, :out]) [:in, :out].each do |direction| adjacencies[direction].should be_a(Hash) expected_adjacent_vertices = Set.new(graph.adjacent(key, :direction => direction, :type => :vertices)) Set.new(adjacencies[direction].keys).should == expected_adjacent_vertices adjacencies[direction].each do |adj_key, adj_value| # Since we already checked edges, just check consistency # with edges. desired_source = direction == :in ? adj_key : key desired_target = direction == :in ? key : adj_key expected_edges = edges.select do |edge| edge.value['source'] == desired_source && edge.value['target'] == desired_target end adj_value.should be_a(Set) if object_ids(adj_value) != object_ids(expected_edges) raise "For vertex #{key.inspect}, direction #{direction.inspect}: expected adjacencies #{expected_edges.inspect} but got #{adj_value.inspect}" end end end end else vertices.should be_a(Array) Set.new(vertices).should == Set.new(graph.vertices) vertices.length.should == graph.vertices.length end end end # Test deserialization of graph from YAML. This presumes the # correctness of serialization to YAML, which has already been # tested. all_test_graphs.each do |graph_to_test| it "should be able to deserialize #{graph_to_test} from YAML (#{which_format} format)" do reference_graph = Puppet::SimpleGraph.new send(graph_to_test, reference_graph) yaml_form = graph_to_yaml(reference_graph, which_format) recovered_graph = YAML.load(yaml_form) # Test that the recovered vertices match the vertices in the # reference graph. expected_vertices = reference_graph.vertices.to_a recovered_vertices = recovered_graph.vertices.to_a Set.new(recovered_vertices).should == Set.new(expected_vertices) recovered_vertices.length.should == expected_vertices.length # Test that the recovered edges match the edges in the # reference graph. expected_edge_tuples = reference_graph.edges.collect do |edge| [edge.source, edge.target, edge.callback, edge.event] end recovered_edge_tuples = recovered_graph.edges.collect do |edge| [edge.source, edge.target, edge.callback, edge.event] end Set.new(recovered_edge_tuples).should == Set.new(expected_edge_tuples) recovered_edge_tuples.length.should == expected_edge_tuples.length # We ought to test that the recovered graph is self-consistent # too. But we're not going to bother with that yet because # the internal representation of the graph is about to change. end end it "should be able to serialize a graph where the vertices contain backreferences to the graph (#{which_format} format)" do reference_graph = Puppet::SimpleGraph.new vertex = Object.new vertex.instance_eval { @graph = reference_graph } reference_graph.add_edge(vertex, :other_vertex) yaml_form = graph_to_yaml(reference_graph, which_format) recovered_graph = YAML.load(yaml_form) recovered_graph.vertices.length.should == 2 recovered_vertex = recovered_graph.vertices.reject { |x| x.is_a?(Symbol) }[0] recovered_vertex.instance_eval { @graph }.should equal(recovered_graph) recovered_graph.edges.length.should == 1 recovered_edge = recovered_graph.edges[0] recovered_edge.source.should equal(recovered_vertex) recovered_edge.target.should == :other_vertex end end it "should serialize properly when used as a base class" do class Puppet::TestDerivedClass < Puppet::SimpleGraph attr_accessor :foo end derived = Puppet::TestDerivedClass.new derived.add_edge(:a, :b) derived.foo = 1234 recovered_derived = YAML.load(YAML.dump(derived)) recovered_derived.class.should equal(Puppet::TestDerivedClass) recovered_derived.edges.length.should == 1 recovered_derived.edges[0].source.should == :a recovered_derived.edges[0].target.should == :b recovered_derived.vertices.length.should == 2 recovered_derived.foo.should == 1234 end end end diff --git a/spec/unit/ssl/base_spec.rb b/spec/unit/ssl/base_spec.rb index 837fd61f8..171d14790 100755 --- a/spec/unit/ssl/base_spec.rb +++ b/spec/unit/ssl/base_spec.rb @@ -1,42 +1,42 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate' class TestCertificate < Puppet::SSL::Base; end describe Puppet::SSL::Certificate do before :each do @base = TestCertificate.new("name") end describe "when fingerprinting content" do before :each do @cert = stub 'cert', :to_der => "DER" @base.stubs(:content).returns(@cert) OpenSSL::Digest.stubs(:constants).returns ["MD5", "SHA1", "SHA256", "SHA512", "DIGEST"] @digest = stub_everything OpenSSL::Digest.stubs(:const_get).returns @digest end it "should digest the certificate DER value and return a ':' seperated nibblet string" do @cert.expects(:to_der).returns("DER") @digest.expects(:hexdigest).with("DER").returns "digest" @base.fingerprint.should == "DI:GE:ST" end it "should raise an error if the digest algorithm is not defined" do OpenSSL::Digest.expects(:constants).returns [] lambda { @base.fingerprint }.should raise_error end it "should use the given digest algorithm" do OpenSSL::Digest.stubs(:const_get).with("DIGEST").returns @digest @digest.expects(:hexdigest).with("DER").returns "digest" @base.fingerprint(:digest).should == "DI:GE:ST" end end end diff --git a/spec/unit/ssl/certificate_authority/interface_spec.rb b/spec/unit/ssl/certificate_authority/interface_spec.rb index dbb872708..2d1541a65 100755 --- a/spec/unit/ssl/certificate_authority/interface_spec.rb +++ b/spec/unit/ssl/certificate_authority/interface_spec.rb @@ -1,375 +1,375 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_authority' shared_examples_for "a normal interface method" do it "should call the method on the CA for each host specified if an array was provided" do @ca.expects(@method).with("host1") @ca.expects(@method).with("host2") @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => %w{host1 host2}) @applier.apply(@ca) end it "should call the method on the CA for all existing certificates if :all was provided" do @ca.expects(:list).returns %w{host1 host2} @ca.expects(@method).with("host1") @ca.expects(@method).with("host2") @applier = Puppet::SSL::CertificateAuthority::Interface.new(@method, :to => :all) @applier.apply(@ca) end end describe Puppet::SSL::CertificateAuthority::Interface do before do @class = Puppet::SSL::CertificateAuthority::Interface end describe "when initializing" do it "should set its method using its settor" do instance = @class.new(:generate, :to => :all) instance.method.should == :generate end it "should set its subjects using the settor" do instance = @class.new(:generate, :to => :all) instance.subjects.should == :all end it "should set the digest if given" do interface = @class.new(:generate, :to => :all, :digest => :digest) interface.digest.should == :digest end it "should set the digest to SHA256 if none given" do interface = @class.new(:generate, :to => :all) interface.digest.should == :SHA256 end end describe "when setting the method" do it "should set the method" do instance = @class.new(:generate, :to => :all) instance.method = :list instance.method.should == :list end it "should fail if the method isn't a member of the INTERFACE_METHODS array" do lambda { @class.new(:thing, :to => :all) }.should raise_error(ArgumentError, /Invalid method thing to apply/) end end describe "when setting the subjects" do it "should set the subjects" do instance = @class.new(:generate, :to => :all) instance.subjects = :signed instance.subjects.should == :signed end it "should fail if the subjects setting isn't :all or an array" do lambda { @class.new(:generate, :to => "other") }.should raise_error(ArgumentError, /Subjects must be an array or :all; not other/) end end it "should have a method for triggering the application" do @class.new(:generate, :to => :all).should respond_to(:apply) end describe "when applying" do before do # We use a real object here, because :verify can't be stubbed, apparently. @ca = Object.new end it "should raise InterfaceErrors" do @applier = @class.new(:revoke, :to => :all) @ca.expects(:list).raises Puppet::SSL::CertificateAuthority::Interface::InterfaceError lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) end it "should log non-Interface failures rather than failing" do @applier = @class.new(:revoke, :to => :all) @ca.expects(:list).raises ArgumentError Puppet.expects(:err) lambda { @applier.apply(@ca) }.should_not raise_error end describe "with an empty array specified and the method is not list" do it "should fail" do @applier = @class.new(:sign, :to => []) lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) end end describe ":generate" do it "should fail if :all was specified" do @applier = @class.new(:generate, :to => :all) lambda { @applier.apply(@ca) }.should raise_error(ArgumentError) end it "should call :generate on the CA for each host specified" do @applier = @class.new(:generate, :to => %w{host1 host2}) @ca.expects(:generate).with("host1", {}) @ca.expects(:generate).with("host2", {}) @applier.apply(@ca) end end describe ":verify" do before { @method = :verify } #it_should_behave_like "a normal interface method" it "should call the method on the CA for each host specified if an array was provided" do # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. end it "should call the method on the CA for all existing certificates if :all was provided" do # LAK:NOTE Mocha apparently doesn't allow you to mock :verify, but I'm confident this works in real life. end end describe ":destroy" do before { @method = :destroy } it_should_behave_like "a normal interface method" end describe ":revoke" do before { @method = :revoke } it_should_behave_like "a normal interface method" end describe ":sign" do describe "and an array of names was provided" do let(:applier) { @class.new(:sign, @options.merge(:to => %w{host1 host2})) } it "should sign the specified waiting certificate requests" do @options = {:allow_dns_alt_names => false} @ca.expects(:sign).with("host1", false) @ca.expects(:sign).with("host2", false) applier.apply(@ca) end it "should sign the certificate requests with alt names if specified" do @options = {:allow_dns_alt_names => true} @ca.expects(:sign).with("host1", true) @ca.expects(:sign).with("host2", true) applier.apply(@ca) end end describe "and :all was provided" do it "should sign all waiting certificate requests" do @ca.stubs(:waiting?).returns(%w{cert1 cert2}) @ca.expects(:sign).with("cert1", nil) @ca.expects(:sign).with("cert2", nil) @applier = @class.new(:sign, :to => :all) @applier.apply(@ca) end it "should fail if there are no waiting certificate requests" do @ca.stubs(:waiting?).returns([]) @applier = @class.new(:sign, :to => :all) lambda { @applier.apply(@ca) }.should raise_error(Puppet::SSL::CertificateAuthority::Interface::InterfaceError) end end end describe ":list" do before :each do @cert = Puppet::SSL::Certificate.new 'foo' @csr = Puppet::SSL::CertificateRequest.new 'bar' @cert.stubs(:subject_alt_names).returns [] @csr.stubs(:subject_alt_names).returns [] Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert Puppet::SSL::CertificateRequest.indirection.stubs(:find).returns @csr @ca.expects(:waiting?).returns %w{host1 host2 host3} @ca.expects(:list).returns %w{host4 host5 host6} @ca.stubs(:fingerprint).returns "fingerprint" @ca.stubs(:verify) end describe "and an empty array was provided" do it "should print all certificate requests" do applier = @class.new(:list, :to => []) applier.expects(:puts).with(<<-OUTPUT.chomp) host1 (fingerprint) host2 (fingerprint) host3 (fingerprint) OUTPUT applier.apply(@ca) end end describe "and :all was provided" do it "should print a string containing all certificate requests and certificates" do @ca.stubs(:verify).with("host4").raises(Puppet::SSL::CertificateAuthority::CertificateVerificationError.new(23), "certificate revoked") applier = @class.new(:list, :to => :all) applier.expects(:puts).with(<<-OUTPUT.chomp) host1 (fingerprint) host2 (fingerprint) host3 (fingerprint) + host5 (fingerprint) + host6 (fingerprint) - host4 (fingerprint) (certificate revoked) OUTPUT applier.apply(@ca) end end describe "and :signed was provided" do it "should print a string containing all signed certificate requests and certificates" do applier = @class.new(:list, :to => :signed) applier.expects(:puts).with(<<-OUTPUT.chomp) + host4 (fingerprint) + host5 (fingerprint) + host6 (fingerprint) OUTPUT applier.apply(@ca) end it "should include subject alt names if they are on the certificate request" do @csr.stubs(:subject_alt_names).returns ["DNS:foo", "DNS:bar"] applier = @class.new(:list, :to => ['host1']) applier.expects(:puts).with(<<-OUTPUT.chomp) host1 (fingerprint) (alt names: DNS:foo, DNS:bar) OUTPUT applier.apply(@ca) end end describe "and an array of names was provided" do it "should print all named hosts" do applier = @class.new(:list, :to => %w{host1 host2 host4 host5}) applier.expects(:puts).with(<<-OUTPUT.chomp) host1 (fingerprint) host2 (fingerprint) + host4 (fingerprint) + host5 (fingerprint) OUTPUT applier.apply(@ca) end end end describe ":print" do describe "and :all was provided" do it "should print all certificates" do @ca.expects(:list).returns %w{host1 host2} @applier = @class.new(:print, :to => :all) @ca.expects(:print).with("host1").returns "h1" @applier.expects(:puts).with "h1" @ca.expects(:print).with("host2").returns "h2" @applier.expects(:puts).with "h2" @applier.apply(@ca) end end describe "and an array of names was provided" do it "should print each named certificate if found" do @applier = @class.new(:print, :to => %w{host1 host2}) @ca.expects(:print).with("host1").returns "h1" @applier.expects(:puts).with "h1" @ca.expects(:print).with("host2").returns "h2" @applier.expects(:puts).with "h2" @applier.apply(@ca) end it "should log any named but not found certificates" do @applier = @class.new(:print, :to => %w{host1 host2}) @ca.expects(:print).with("host1").returns "h1" @applier.expects(:puts).with "h1" @ca.expects(:print).with("host2").returns nil Puppet.expects(:err).with { |msg| msg.include?("host2") } @applier.apply(@ca) end end end describe ":fingerprint" do it "should fingerprint with the set digest algorithm" do @applier = @class.new(:fingerprint, :to => %w{host1}, :digest => :digest) @ca.expects(:fingerprint).with("host1", :digest).returns "fingerprint1" @applier.expects(:puts).with "host1 fingerprint1" @applier.apply(@ca) end describe "and :all was provided" do it "should fingerprint all certificates (including waiting ones)" do @ca.expects(:list).returns %w{host1} @ca.expects(:waiting?).returns %w{host2} @applier = @class.new(:fingerprint, :to => :all) @ca.expects(:fingerprint).with("host1", :SHA256).returns "fingerprint1" @applier.expects(:puts).with "host1 fingerprint1" @ca.expects(:fingerprint).with("host2", :SHA256).returns "fingerprint2" @applier.expects(:puts).with "host2 fingerprint2" @applier.apply(@ca) end end describe "and an array of names was provided" do it "should print each named certificate if found" do @applier = @class.new(:fingerprint, :to => %w{host1 host2}) @ca.expects(:fingerprint).with("host1", :SHA256).returns "fingerprint1" @applier.expects(:puts).with "host1 fingerprint1" @ca.expects(:fingerprint).with("host2", :SHA256).returns "fingerprint2" @applier.expects(:puts).with "host2 fingerprint2" @applier.apply(@ca) end end end end end diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb index 33b692970..00098cb92 100755 --- a/spec/unit/ssl/certificate_authority_spec.rb +++ b/spec/unit/ssl/certificate_authority_spec.rb @@ -1,882 +1,882 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_authority' describe Puppet::SSL::CertificateAuthority do after do Puppet::SSL::CertificateAuthority.instance_variable_set(:@singleton_instance, nil) Puppet.settings.clearused end def stub_ca_host @key = mock 'key' @key.stubs(:content).returns "cakey" @cacert = mock 'certificate' @cacert.stubs(:content).returns "cacertificate" @host = stub 'ssl_host', :key => @key, :certificate => @cacert, :name => Puppet::SSL::Host.ca_name end it "should have a class method for returning a singleton instance" do Puppet::SSL::CertificateAuthority.should respond_to(:instance) end describe "when finding an existing instance" do describe "and the host is a CA host and the run_mode is master" do before do Puppet[:ca] = true Puppet.run_mode.stubs(:master?).returns true @ca = mock('ca') Puppet::SSL::CertificateAuthority.stubs(:new).returns @ca end it "should return an instance" do Puppet::SSL::CertificateAuthority.instance.should equal(@ca) end it "should always return the same instance" do Puppet::SSL::CertificateAuthority.instance.should equal(Puppet::SSL::CertificateAuthority.instance) end end describe "and the host is not a CA host" do it "should return nil" do Puppet.settings.stubs(:value).with(:ca).returns false Puppet.run_mode.stubs(:master?).returns true ca = mock('ca') Puppet::SSL::CertificateAuthority.expects(:new).never Puppet::SSL::CertificateAuthority.instance.should be_nil end end describe "and the run_mode is not master" do it "should return nil" do Puppet.settings.stubs(:value).with(:ca).returns true Puppet.run_mode.stubs(:master?).returns false ca = mock('ca') Puppet::SSL::CertificateAuthority.expects(:new).never Puppet::SSL::CertificateAuthority.instance.should be_nil end end end describe "when initializing" do before do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "ca_testing" Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) end it "should always set its name to the value of :certname" do Puppet.settings.expects(:value).with(:certname).returns "ca_testing" Puppet::SSL::CertificateAuthority.new.name.should == "ca_testing" end it "should create an SSL::Host instance whose name is the 'ca_name'" do Puppet::SSL::Host.expects(:ca_name).returns "caname" host = stub 'host' Puppet::SSL::Host.expects(:new).with("caname").returns host Puppet::SSL::CertificateAuthority.new end it "should use the :main, :ca, and :ssl settings sections" do Puppet.settings.expects(:use).with(:main, :ssl, :ca) Puppet::SSL::CertificateAuthority.new end it "should create an inventory instance" do Puppet::SSL::Inventory.expects(:new).returns "inventory" Puppet::SSL::CertificateAuthority.new.inventory.should == "inventory" end it "should make sure the CA is set up" do Puppet::SSL::CertificateAuthority.any_instance.expects(:setup) Puppet::SSL::CertificateAuthority.new end end describe "when setting itself up" do it "should generate the CA certificate if it does not have one" do Puppet.settings.stubs :use host = stub 'host' Puppet::SSL::Host.stubs(:new).returns host host.expects(:certificate).returns nil Puppet::SSL::CertificateAuthority.any_instance.expects(:generate_ca_certificate) Puppet::SSL::CertificateAuthority.new end end describe "when retrieving the certificate revocation list" do before do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "ca_testing" Puppet.settings.stubs(:value).with(:cacrl).returns "/my/crl" cert = stub("certificate", :content => "real_cert") key = stub("key", :content => "real_key") @host = stub 'host', :certificate => cert, :name => "hostname", :key => key Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) @ca = Puppet::SSL::CertificateAuthority.new @ca.stubs(:host).returns @host end it "should return any found CRL instance" do crl = mock 'crl' Puppet::SSL::CertificateRevocationList.indirection.expects(:find).returns crl @ca.crl.should equal(crl) end it "should create, generate, and save a new CRL instance of no CRL can be found" do crl = Puppet::SSL::CertificateRevocationList.new("fakename") Puppet::SSL::CertificateRevocationList.indirection.expects(:find).returns nil Puppet::SSL::CertificateRevocationList.expects(:new).returns crl crl.expects(:generate).with(@ca.host.certificate.content, @ca.host.key.content) Puppet::SSL::CertificateRevocationList.indirection.expects(:save).with(crl) @ca.crl.should equal(crl) end end describe "when generating a self-signed CA certificate" do before do Puppet.settings.stubs(:use) Puppet.settings.stubs(:value).returns "ca_testing" Puppet::SSL::CertificateAuthority.any_instance.stubs(:setup) Puppet::SSL::CertificateAuthority.any_instance.stubs(:crl) @ca = Puppet::SSL::CertificateAuthority.new @host = stub 'host', :key => mock("key"), :name => "hostname", :certificate => mock('certificate') Puppet::SSL::CertificateRequest.any_instance.stubs(:generate) @ca.stubs(:host).returns @host end it "should create and store a password at :capass" do Puppet.settings.expects(:value).with(:capass).returns "/path/to/pass" FileTest.expects(:exist?).with("/path/to/pass").returns false fh = mock 'filehandle' Puppet.settings.expects(:write).with(:capass).yields fh fh.expects(:print).with { |s| s.length > 18 } @ca.stubs(:sign) @ca.generate_ca_certificate end it "should generate a key if one does not exist" do @ca.stubs :generate_password @ca.stubs :sign @ca.host.expects(:key).returns nil @ca.host.expects(:generate_key) @ca.generate_ca_certificate end it "should create and sign a self-signed cert using the CA name" do request = mock 'request' Puppet::SSL::CertificateRequest.expects(:new).with(@ca.host.name).returns request request.expects(:generate).with(@ca.host.key) request.stubs(:request_extensions => []) @ca.expects(:sign).with(@host.name, false, request) @ca.stubs :generate_password @ca.generate_ca_certificate end it "should generate its CRL" do @ca.stubs :generate_password @ca.stubs :sign @ca.host.expects(:key).returns nil @ca.host.expects(:generate_key) @ca.expects(:crl) @ca.generate_ca_certificate end end describe "when signing" do before do Puppet.settings.stubs(:use) Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true stub_ca_host Puppet::SSL::Host.expects(:new).with(Puppet::SSL::Host.ca_name).returns @host @ca = Puppet::SSL::CertificateAuthority.new @name = "myhost" @real_cert = stub 'realcert', :sign => nil @cert = Puppet::SSL::Certificate.new(@name) @cert.content = @real_cert Puppet::SSL::Certificate.stubs(:new).returns @cert @cert.stubs(:content=) Puppet::SSL::Certificate.indirection.stubs(:save) # Stub out the factory Puppet::SSL::CertificateFactory.stubs(:build).returns "my real cert" @request_content = stub "request content stub", :subject => @name @request = stub 'request', :name => @name, :request_extensions => [], :subject_alt_names => [], :content => @request_content # And the inventory @inventory = stub 'inventory', :add => nil @ca.stubs(:inventory).returns @inventory Puppet::SSL::CertificateRequest.indirection.stubs(:destroy) end describe "and calculating the next certificate serial number" do before do @path = "/path/to/serial" Puppet.settings.stubs(:value).with(:serial).returns @path @filehandle = stub 'filehandle', :<< => @filehandle Puppet.settings.stubs(:readwritelock).with(:serial).yields @filehandle end it "should default to 0x1 for the first serial number" do @ca.next_serial.should == 0x1 end it "should return the current content of the serial file" do FileTest.stubs(:exist?).with(@path).returns true File.expects(:read).with(@path).returns "0002" @ca.next_serial.should == 2 end it "should write the next serial number to the serial file as hex" do @filehandle.expects(:<<).with("0002") @ca.next_serial end it "should lock the serial file while writing" do Puppet.settings.expects(:readwritelock).with(:serial) @ca.next_serial end end describe "its own certificate" do before do @serial = 10 @ca.stubs(:next_serial).returns @serial end it "should not look up a certificate request for the host" do Puppet::SSL::CertificateRequest.indirection.expects(:find).never @ca.sign(@name, true, @request) end it "should use a certificate type of :ca" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[0] == :ca end.returns "my real cert" @ca.sign(@name, :ca, @request) end it "should pass the provided CSR as the CSR" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[1] == @request end.returns "my real cert" @ca.sign(@name, :ca, @request) end it "should use the provided CSR's content as the issuer" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[2].subject == "myhost" end.returns "my real cert" @ca.sign(@name, :ca, @request) end it "should pass the next serial as the serial number" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[3] == @serial end.returns "my real cert" @ca.sign(@name, :ca, @request) end it "should sign the certificate request even if it contains alt names" do @request.stubs(:subject_alt_names).returns %w[DNS:foo DNS:bar DNS:baz] expect do @ca.sign(@name, false, @request) end.should_not raise_error(Puppet::SSL::CertificateAuthority::CertificateSigningError) end it "should save the resulting certificate" do Puppet::SSL::Certificate.indirection.expects(:save).with(@cert) @ca.sign(@name, :ca, @request) end end describe "another host's certificate" do before do @serial = 10 @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::CertificateRequest.indirection.stubs :save end it "should use a certificate type of :server" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[0] == :server end.returns "my real cert" @ca.sign(@name) end it "should use look up a CSR for the host in the :ca_file terminus" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with(@name).returns @request @ca.sign(@name) end it "should fail if no CSR can be found for the host" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with(@name).returns nil lambda { @ca.sign(@name) }.should raise_error(ArgumentError) end it "should fail if an unknown request extension is present" do @request.stubs :request_extensions => [{ "oid" => "bananas", "value" => "delicious" }] expect { @ca.sign(@name) }. should raise_error(/CSR has request extensions that are not permitted/) end it "should fail if the CSR contains alt names and they are not expected" do @request.stubs(:subject_alt_names).returns %w[DNS:foo DNS:bar DNS:baz] expect do @ca.sign(@name, false) end.to raise_error(Puppet::SSL::CertificateAuthority::CertificateSigningError, /CSR '#{@name}' contains subject alternative names \(.*?\), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{@name}` to sign this request./) end it "should not fail if the CSR does not contain alt names and they are expected" do @request.stubs(:subject_alt_names).returns [] expect { @ca.sign(@name, true) }.should_not raise_error end it "should reject alt names by default" do @request.stubs(:subject_alt_names).returns %w[DNS:foo DNS:bar DNS:baz] expect do @ca.sign(@name) end.to raise_error(Puppet::SSL::CertificateAuthority::CertificateSigningError, /CSR '#{@name}' contains subject alternative names \(.*?\), which are disallowed. Use `puppet cert --allow-dns-alt-names sign #{@name}` to sign this request./) end it "should use the CA certificate as the issuer" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[2] == @cacert.content end.returns "my real cert" @ca.sign(@name) end it "should pass the next serial as the serial number" do Puppet::SSL::CertificateFactory.expects(:build).with do |*args| args[3] == @serial end.returns "my real cert" @ca.sign(@name) end it "should sign the resulting certificate using its real key and a digest" do digest = mock 'digest' OpenSSL::Digest::SHA256.expects(:new).returns digest key = stub 'key', :content => "real_key" @ca.host.stubs(:key).returns key @cert.content.expects(:sign).with("real_key", digest) @ca.sign(@name) end it "should save the resulting certificate" do Puppet::SSL::Certificate.indirection.stubs(:save).with(@cert) @ca.sign(@name) end it "should remove the host's certificate request" do Puppet::SSL::CertificateRequest.indirection.expects(:destroy).with(@name) @ca.sign(@name) end it "should check the internal signing policies" do @ca.expects(:check_internal_signing_policies).returns true @ca.sign(@name) end end context "#check_internal_signing_policies" do before do @serial = 10 @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request @cert.stubs :save end it "should reject a critical extension that isn't on the whitelist" do @request.stubs(:request_extensions).returns [{ "oid" => "banana", "value" => "yumm", "critical" => true }] expect { @ca.sign(@name) }.to raise_error( Puppet::SSL::CertificateAuthority::CertificateSigningError, /request extensions that are not permitted/ ) end it "should reject a non-critical extension that isn't on the whitelist" do @request.stubs(:request_extensions).returns [{ "oid" => "peach", "value" => "meh", "critical" => false }] expect { @ca.sign(@name) }.to raise_error( Puppet::SSL::CertificateAuthority::CertificateSigningError, /request extensions that are not permitted/ ) end it "should reject non-whitelist extensions even if a valid extension is present" do @request.stubs(:request_extensions).returns [{ "oid" => "peach", "value" => "meh", "critical" => false }, { "oid" => "subjectAltName", "value" => "DNS:foo", "critical" => true }] expect { @ca.sign(@name) }.to raise_error( Puppet::SSL::CertificateAuthority::CertificateSigningError, /request extensions that are not permitted/ ) end it "should reject a subjectAltName for a non-DNS value" do @request.stubs(:subject_alt_names).returns ['DNS:foo', 'email:bar@example.com'] expect { @ca.sign(@name, true) }.to raise_error( Puppet::SSL::CertificateAuthority::CertificateSigningError, /subjectAltName outside the DNS label space/ ) end it "should reject a wildcard subject" do @request.content.stubs(:subject). returns(OpenSSL::X509::Name.new([["CN", "*.local"]])) expect { @ca.sign(@name) }.to raise_error( Puppet::SSL::CertificateAuthority::CertificateSigningError, /subject contains a wildcard/ ) end it "should reject a wildcard subjectAltName" do @request.stubs(:subject_alt_names).returns ['DNS:foo', 'DNS:*.bar'] expect { @ca.sign(@name, true) }.to raise_error( Puppet::SSL::CertificateAuthority::CertificateSigningError, /subjectAltName contains a wildcard/ ) end end it "should create a certificate instance with the content set to the newly signed x509 certificate" do @serial = 10 @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::Certificate.indirection.stubs :save Puppet::SSL::Certificate.expects(:new).with(@name).returns @cert @ca.sign(@name) end it "should return the certificate instance" do @ca.stubs(:next_serial).returns @serial Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::Certificate.indirection.stubs :save @ca.sign(@name).should equal(@cert) end it "should add the certificate to its inventory" do @ca.stubs(:next_serial).returns @serial @inventory.expects(:add).with(@cert) Puppet::SSL::CertificateRequest.indirection.stubs(:find).with(@name).returns @request Puppet::SSL::Certificate.indirection.stubs :save @ca.sign(@name) end it "should have a method for triggering autosigning of available CSRs" do @ca.should respond_to(:autosign) end describe "when autosigning certificates" do it "should do nothing if autosign is disabled" do Puppet.settings.expects(:value).with(:autosign).returns 'false' Puppet::SSL::CertificateRequest.indirection.expects(:search).never @ca.autosign end it "should do nothing if no autosign.conf exists" do Puppet.settings.expects(:value).with(:autosign).returns '/auto/sign' FileTest.expects(:exist?).with("/auto/sign").returns false Puppet::SSL::CertificateRequest.indirection.expects(:search).never @ca.autosign end describe "and autosign is enabled and the autosign.conf file exists" do before do Puppet.settings.stubs(:value).with(:autosign).returns '/auto/sign' FileTest.stubs(:exist?).with("/auto/sign").returns true File.stubs(:readlines).with("/auto/sign").returns ["one\n", "two\n"] Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [] @store = stub 'store', :allow => nil Puppet::Network::AuthStore.stubs(:new).returns @store end describe "when creating the AuthStore instance to verify autosigning" do it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do Puppet::Network::AuthStore.expects(:new).returns @store @store.expects(:allow).with("one") @store.expects(:allow).with("two") @ca.autosign end it "should reparse the autosign configuration on each call" do Puppet::Network::AuthStore.expects(:new).times(2).returns @store @ca.autosign @ca.autosign end it "should ignore comments" do File.stubs(:readlines).with("/auto/sign").returns ["one\n", "#two\n"] @store.expects(:allow).with("one") @ca.autosign end it "should ignore blank lines" do File.stubs(:readlines).with("/auto/sign").returns ["one\n", "\n"] @store.expects(:allow).with("one") @ca.autosign end end it "should sign all CSRs whose hostname matches the autosign configuration" do csr1 = mock 'csr1' csr2 = mock 'csr2' Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [csr1, csr2] end it "should not sign CSRs whose hostname does not match the autosign configuration" do csr1 = mock 'csr1' csr2 = mock 'csr2' Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [csr1, csr2] end end end end describe "when managing certificate clients" do before do Puppet.settings.stubs(:use) Puppet::SSL::CertificateAuthority.any_instance.stubs(:password?).returns true stub_ca_host Puppet::SSL::Host.expects(:new).returns @host Puppet::SSL::CertificateAuthority.any_instance.stubs(:host).returns @host @cacert = mock 'certificate' @cacert.stubs(:content).returns "cacertificate" @ca = Puppet::SSL::CertificateAuthority.new end it "should have a method for acting on the SSL files" do @ca.should respond_to(:apply) end describe "when applying a method to a set of hosts" do it "should fail if no subjects have been specified" do lambda { @ca.apply(:generate) }.should raise_error(ArgumentError) end it "should create an Interface instance with the specified method and the options" do Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :to => :host).returns(stub('applier', :apply => nil)) @ca.apply(:generate, :to => :host) end it "should apply the Interface with itself as the argument" do applier = stub('applier') applier.expects(:apply).with(@ca) Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns applier @ca.apply(:generate, :to => :ca_testing) end end it "should be able to list waiting certificate requests" do req1 = stub 'req1', :name => "one" req2 = stub 'req2', :name => "two" Puppet::SSL::CertificateRequest.indirection.expects(:search).with("*").returns [req1, req2] @ca.waiting?.should == %w{one two} end it "should delegate removing hosts to the Host class" do Puppet::SSL::Host.expects(:destroy).with("myhost") @ca.destroy("myhost") end it "should be able to verify certificates" do @ca.should respond_to(:verify) end it "should list certificates as the sorted list of all existing signed certificates" do cert1 = stub 'cert1', :name => "cert1" cert2 = stub 'cert2', :name => "cert2" Puppet::SSL::Certificate.indirection.expects(:search).with("*").returns [cert1, cert2] @ca.list.should == %w{cert1 cert2} end describe "and printing certificates" do it "should return nil if the certificate cannot be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns nil @ca.print("myhost").should be_nil end it "should print certificates by calling :to_text on the host's certificate" do cert1 = stub 'cert1', :name => "cert1", :to_text => "mytext" Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns cert1 @ca.print("myhost").should == "mytext" end end describe "and fingerprinting certificates" do before :each do @cert = stub 'cert', :name => "cert", :fingerprint => "DIGEST" Puppet::SSL::Certificate.indirection.stubs(:find).with("myhost").returns @cert Puppet::SSL::CertificateRequest.indirection.stubs(:find).with("myhost") end it "should raise an error if the certificate or CSR cannot be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns nil Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myhost").returns nil lambda { @ca.fingerprint("myhost") }.should raise_error end it "should try to find a CSR if no certificate can be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("myhost").returns nil Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myhost").returns @cert @cert.expects(:fingerprint) @ca.fingerprint("myhost") end it "should delegate to the certificate fingerprinting" do @cert.expects(:fingerprint) @ca.fingerprint("myhost") end it "should propagate the digest algorithm to the certificate fingerprinting system" do @cert.expects(:fingerprint).with(:digest) @ca.fingerprint("myhost", :digest) end end describe "and verifying certificates" do before do @store = stub 'store', :verify => true, :add_file => nil, :purpose= => nil, :add_crl => true, :flags= => nil OpenSSL::X509::Store.stubs(:new).returns @store Puppet.settings.stubs(:value).returns "crtstuff" @cert = stub 'cert', :content => "mycert" Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert @crl = stub('crl', :content => "mycrl") @ca.stubs(:crl).returns @crl end it "should fail if the host's certificate cannot be found" do Puppet::SSL::Certificate.indirection.expects(:find).with("me").returns(nil) lambda { @ca.verify("me") }.should raise_error(ArgumentError) end it "should create an SSL Store to verify" do OpenSSL::X509::Store.expects(:new).returns @store @ca.verify("me") end it "should add the CA Certificate to the store" do Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" @store.expects(:add_file).with "/ca/cert" @ca.verify("me") end it "should add the CRL to the store if the crl is enabled" do @store.expects(:add_crl).with "mycrl" @ca.verify("me") end it "should set the store purpose to OpenSSL::X509::PURPOSE_SSL_CLIENT" do Puppet.settings.stubs(:value).with(:cacert).returns "/ca/cert" @store.expects(:add_file).with "/ca/cert" @ca.verify("me") end it "should set the store flags to check the crl" do @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK @ca.verify("me") end it "should use the store to verify the certificate" do @cert.expects(:content).returns "mycert" @store.expects(:verify).with("mycert").returns true @ca.verify("me") end it "should fail if the verification returns false" do @cert.expects(:content).returns "mycert" @store.expects(:verify).with("mycert").returns false lambda { @ca.verify("me") }.should raise_error end end describe "and revoking certificates" do before do @crl = mock 'crl' @ca.stubs(:crl).returns @crl @ca.stubs(:next_serial).returns 10 @real_cert = stub 'real_cert', :serial => 15 @cert = stub 'cert', :content => @real_cert Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert end it "should fail if the certificate revocation list is disabled" do @ca.stubs(:crl).returns false lambda { @ca.revoke('ca_testing') }.should raise_error(ArgumentError) end it "should delegate the revocation to its CRL" do @ca.crl.expects(:revoke) @ca.revoke('host') end it "should get the serial number from the local certificate if it exists" do @ca.crl.expects(:revoke).with { |serial, key| serial == 15 } Puppet::SSL::Certificate.indirection.expects(:find).with("host").returns @cert @ca.revoke('host') end it "should get the serial number from inventory if no local certificate exists" do real_cert = stub 'real_cert', :serial => 15 cert = stub 'cert', :content => real_cert Puppet::SSL::Certificate.indirection.expects(:find).with("host").returns nil @ca.inventory.expects(:serial).with("host").returns 16 @ca.crl.expects(:revoke).with { |serial, key| serial == 16 } @ca.revoke('host') end end it "should be able to generate a complete new SSL host" do @ca.should respond_to(:generate) end describe "and generating certificates" do before do @host = stub 'host', :generate_certificate_request => nil Puppet::SSL::Host.stubs(:new).returns @host Puppet::SSL::Certificate.indirection.stubs(:find).returns nil @ca.stubs(:sign) end it "should fail if a certificate already exists for the host" do Puppet::SSL::Certificate.indirection.expects(:find).with("him").returns "something" lambda { @ca.generate("him") }.should raise_error(ArgumentError) end it "should create a new Host instance with the correct name" do Puppet::SSL::Host.expects(:new).with("him").returns @host @ca.generate("him") end it "should use the Host to generate the certificate request" do @host.expects :generate_certificate_request @ca.generate("him") end it "should sign the generated request" do @ca.expects(:sign).with("him", false) @ca.generate("him") end end end end diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb index 88521b0fb..16e545b11 100755 --- a/spec/unit/ssl/certificate_factory_spec.rb +++ b/spec/unit/ssl/certificate_factory_spec.rb @@ -1,126 +1,126 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_factory' describe Puppet::SSL::CertificateFactory do let :serial do OpenSSL::BN.new('12') end let :name do "example.local" end let :x509_name do OpenSSL::X509::Name.new([['CN', name]]) end let :key do Puppet::SSL::Key.new(name).generate end let :csr do csr = Puppet::SSL::CertificateRequest.new(name) csr.generate(key) csr end let :issuer do cert = OpenSSL::X509::Certificate.new cert.subject = OpenSSL::X509::Name.new([["CN", 'issuer.local']]) cert end describe "when generating the certificate" do it "should return a new X509 certificate" do subject.build(:server, csr, issuer, serial).should_not == subject.build(:server, csr, issuer, serial) end it "should set the certificate's version to 2" do subject.build(:server, csr, issuer, serial).version.should == 2 end it "should set the certificate's subject to the CSR's subject" do cert = subject.build(:server, csr, issuer, serial) cert.subject.should eql x509_name end it "should set the certificate's issuer to the Issuer's subject" do cert = subject.build(:server, csr, issuer, serial) cert.issuer.should eql issuer.subject end it "should set the certificate's public key to the CSR's public key" do cert = subject.build(:server, csr, issuer, serial) cert.public_key.should be_public cert.public_key.to_s.should == csr.content.public_key.to_s end it "should set the certificate's serial number to the provided serial number" do cert = subject.build(:server, csr, issuer, serial) cert.serial.should == serial end it "should have 24 hours grace on the start of the cert" do cert = subject.build(:server, csr, issuer, serial) cert.not_before.should be_within(30).of(Time.now - 24*60*60) end it "should set the default TTL of the certificate" do ttl = Puppet::SSL::CertificateFactory.ttl cert = subject.build(:server, csr, issuer, serial) cert.not_after.should be_within(30).of(Time.now + ttl) end it "should respect a custom TTL for the CA" do Puppet[:ca_ttl] = 12 cert = subject.build(:server, csr, issuer, serial) cert.not_after.should be_within(30).of(Time.now + 12) end it "should build extensions for the certificate" do cert = subject.build(:server, csr, issuer, serial) cert.extensions.map {|x| x.to_h }.find {|x| x["oid"] == "nsComment" }.should == { "oid" => "nsComment", "value" => "Puppet Ruby/OpenSSL Internal Certificate", "critical" => false } end # See #2848 for why we are doing this: we need to make sure that # subjectAltName is set if the CSR has it, but *not* if it is set when the # certificate is built! it "should not add subjectAltNames from dns_alt_names" do Puppet[:dns_alt_names] = 'one, two' # Verify the CSR still has no extReq, just in case... csr.request_extensions.should == [] cert = subject.build(:server, csr, issuer, serial) cert.extensions.find {|x| x.oid == 'subjectAltName' }.should be_nil end it "should add subjectAltName when the CSR requests them" do Puppet[:dns_alt_names] = '' expect = %w{one two} + [name] csr = Puppet::SSL::CertificateRequest.new(name) csr.generate(key, :dns_alt_names => expect.join(', ')) csr.request_extensions.should_not be_nil csr.subject_alt_names.should =~ expect.map{|x| "DNS:#{x}"} cert = subject.build(:server, csr, issuer, serial) san = cert.extensions.find {|x| x.oid == 'subjectAltName' } san.should_not be_nil expect.each do |name| san.value.should =~ /DNS:#{name}\b/i end end # Can't check the CA here, since that requires way more infrastructure # that I want to build up at this time. We can verify the critical # values, though, which are non-CA certs. --daniel 2011-10-11 { :ca => 'CA:TRUE', :terminalsubca => ['CA:TRUE', 'pathlen:0'], :server => 'CA:FALSE', :ocsp => 'CA:FALSE', :client => 'CA:FALSE', }.each do |name, value| it "should set basicConstraints for #{name} #{value.inspect}" do cert = subject.build(name, csr, issuer, serial) bc = cert.extensions.find {|x| x.oid == 'basicConstraints' } bc.should be bc.value.split(/\s*,\s*/).should =~ Array(value) end end end end diff --git a/spec/unit/ssl/certificate_request_spec.rb b/spec/unit/ssl/certificate_request_spec.rb index 9731ed309..c7937343e 100755 --- a/spec/unit/ssl/certificate_request_spec.rb +++ b/spec/unit/ssl/certificate_request_spec.rb @@ -1,277 +1,277 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_request' require 'puppet/ssl/key' describe Puppet::SSL::CertificateRequest do before do @class = Puppet::SSL::CertificateRequest end it "should be extended with the Indirector module" do @class.singleton_class.should be_include(Puppet::Indirector) end it "should indirect certificate_request" do @class.indirection.name.should == :certificate_request end it "should use any provided name as its name" do @class.new("myname").name.should == "myname" end it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do it "should create a CSR instance with its name set to the CSR subject and its content set to the extracted CSR" do csr = stub 'csr', :subject => "/CN=Foo.madstop.com" OpenSSL::X509::Request.expects(:new).with("my csr").returns(csr) mycsr = stub 'sslcsr' mycsr.expects(:content=).with(csr) @class.expects(:new).with("foo.madstop.com").returns mycsr @class.from_s("my csr") end end describe "when managing instances" do before do @request = @class.new("myname") end it "should have a name attribute" do @request.name.should == "myname" end it "should downcase its name" do @class.new("MyName").name.should == "myname" end it "should have a content attribute" do @request.should respond_to(:content) end it "should be able to read requests from disk" do path = "/my/path" File.expects(:read).with(path).returns("my request") request = mock 'request' OpenSSL::X509::Request.expects(:new).with("my request").returns(request) @request.read(path).should equal(request) @request.content.should equal(request) end it "should return an empty string when converted to a string with no request" do @request.to_s.should == "" end it "should convert the request to pem format when converted to a string" do request = mock 'request', :to_pem => "pem" @request.content = request @request.to_s.should == "pem" end it "should have a :to_text method that it delegates to the actual key" do real_request = mock 'request' real_request.expects(:to_text).returns "requesttext" @request.content = real_request @request.to_text.should == "requesttext" end end describe "when generating" do before do @instance = @class.new("myname") key = Puppet::SSL::Key.new("myname") @key = key.generate @request = OpenSSL::X509::Request.new OpenSSL::X509::Request.expects(:new).returns(@request) @request.stubs(:verify).returns(true) end it "should use the content of the provided key if the key is a Puppet::SSL::Key instance" do key = Puppet::SSL::Key.new("test") key.expects(:content).returns @key @request.expects(:sign).with{ |key, digest| key == @key } @instance.generate(key) end it "should log that it is creating a new certificate request" do Puppet.expects(:info).twice @instance.generate(@key) end it "should set the subject to [CN, name]" do subject = mock 'subject' OpenSSL::X509::Name.expects(:new).with([["CN", @instance.name]]).returns(subject) @request.expects(:subject=).with(subject) @instance.generate(@key) end it "should set the CN to the CSR name when the CSR is not for a CA" do subject = mock 'subject' OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == @instance.name }.returns(subject) @request.expects(:subject=).with(subject) @instance.generate(@key) end it "should set the CN to the :ca_name setting when the CSR is for a CA" do subject = mock 'subject' Puppet[:ca_name] = "mycertname" OpenSSL::X509::Name.expects(:new).with { |subject| subject[0][1] == "mycertname" }.returns(subject) @request.expects(:subject=).with(subject) Puppet::SSL::CertificateRequest.new(Puppet::SSL::CA_NAME).generate(@key) end it "should set the version to 0" do @request.expects(:version=).with(0) @instance.generate(@key) end it "should set the public key to the provided key's public key" do # Yay, the private key extracts a new key each time. pubkey = @key.public_key @key.stubs(:public_key).returns pubkey @request.expects(:public_key=).with(@key.public_key) @instance.generate(@key) end context "without subjectAltName / dns_alt_names" do before :each do Puppet[:dns_alt_names] = "" end ["extreq", "msExtReq"].each do |name| it "should not add any #{name} attribute" do @request.expects(:add_attribute).never @request.expects(:attributes=).never @instance.generate(@key) end it "should return no subjectAltNames" do @instance.generate(@key) @instance.subject_alt_names.should be_empty end end end context "with dns_alt_names" do before :each do Puppet[:dns_alt_names] = "one, two, three" end ["extreq", "msExtReq"].each do |name| it "should not add any #{name} attribute" do @request.expects(:add_attribute).never @request.expects(:attributes=).never @instance.generate(@key) end it "should return no subjectAltNames" do @instance.generate(@key) @instance.subject_alt_names.should be_empty end end end context "with subjectAltName to generate request" do before :each do Puppet[:dns_alt_names] = "" end it "should add an extreq attribute" do @request.expects(:add_attribute).with do |arg| arg.value.value.all? do |x| x.value.all? do |y| y.value[0].value == "subjectAltName" end end end @instance.generate(@key, :dns_alt_names => 'one, two') end it "should return the subjectAltName values" do @instance.generate(@key, :dns_alt_names => 'one,two') @instance.subject_alt_names.should =~ ["DNS:myname", "DNS:one", "DNS:two"] end end it "should sign the csr with the provided key and a digest" do digest = mock 'digest' OpenSSL::Digest::SHA256.expects(:new).returns(digest) @request.expects(:sign).with(@key, digest) @instance.generate(@key) end it "should verify the generated request using the public key" do # Stupid keys don't have a competent == method. @request.expects(:verify).with { |public_key| public_key.to_s == @key.public_key.to_s }.returns true @instance.generate(@key) end it "should fail if verification fails" do @request.expects(:verify).returns false lambda { @instance.generate(@key) }.should raise_error(Puppet::Error) end it "should fingerprint the request" do @instance.expects(:fingerprint) @instance.generate(@key) end it "should display the fingerprint" do Puppet.stubs(:info) @instance.stubs(:fingerprint).returns("FINGERPRINT") Puppet.expects(:info).with { |s| s =~ /FINGERPRINT/ } @instance.generate(@key) end it "should return the generated request" do @instance.generate(@key).should equal(@request) end it "should set its content to the generated request" do @instance.generate(@key) @instance.content.should equal(@request) end end describe "when a CSR is saved" do describe "and a CA is available" do it "should save the CSR and trigger autosigning" do ca = mock 'ca', :autosign Puppet::SSL::CertificateAuthority.expects(:instance).returns ca csr = Puppet::SSL::CertificateRequest.new("me") terminus = mock 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |request| request.instance == csr && request.key == "me" } Puppet::SSL::CertificateRequest.indirection.save(csr) end end describe "and a CA is not available" do it "should save the CSR" do Puppet::SSL::CertificateAuthority.expects(:instance).returns nil csr = Puppet::SSL::CertificateRequest.new("me") terminus = mock 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |request| request.instance == csr && request.key == "me" } Puppet::SSL::CertificateRequest.indirection.save(csr) end end end end diff --git a/spec/unit/ssl/certificate_revocation_list_spec.rb b/spec/unit/ssl/certificate_revocation_list_spec.rb index 5b8e47b48..88dfc488e 100755 --- a/spec/unit/ssl/certificate_revocation_list_spec.rb +++ b/spec/unit/ssl/certificate_revocation_list_spec.rb @@ -1,167 +1,167 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate_revocation_list' describe Puppet::SSL::CertificateRevocationList do before do @cert = stub 'cert', :subject => "mysubject" @key = stub 'key', :private? => true @class = Puppet::SSL::CertificateRevocationList end it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do it "should create a CRL instance with its name set to 'foo' and its content set to the extracted CRL" do crl = stub 'crl' OpenSSL::X509::CRL.expects(:new).returns(crl) mycrl = stub 'sslcrl' mycrl.expects(:content=).with(crl) @class.expects(:new).with("foo").returns mycrl @class.from_s("my crl").should == mycrl end end describe "when an instance" do before do @class.any_instance.stubs(:read_or_generate) @crl = @class.new("whatever") end it "should always use 'crl' for its name" do @crl.name.should == "crl" end it "should have a content attribute" do @crl.should respond_to(:content) end end describe "when generating the crl" do before do @real_crl = mock 'crl' @real_crl.stub_everything OpenSSL::X509::CRL.stubs(:new).returns(@real_crl) @class.any_instance.stubs(:read_or_generate) @crl = @class.new("crl") end it "should set its issuer to the subject of the passed certificate" do @real_crl.expects(:issuer=).with(@cert.subject) @crl.generate(@cert, @key) end it "should set its version to 1" do @real_crl.expects(:version=).with(1) @crl.generate(@cert, @key) end it "should create an instance of OpenSSL::X509::CRL" do OpenSSL::X509::CRL.expects(:new).returns(@real_crl) @crl.generate(@cert, @key) end # The next three tests aren't good, but at least they # specify the behaviour. it "should add an extension for the CRL number" do @real_crl.expects(:extensions=) @crl.generate(@cert, @key) end it "should set the last update time" do @real_crl.expects(:last_update=) @crl.generate(@cert, @key) end it "should set the next update time" do @real_crl.expects(:next_update=) @crl.generate(@cert, @key) end it "should sign the CRL" do @real_crl.expects(:sign).with { |key, digest| key == @key } @crl.generate(@cert, @key) end it "should set the content to the generated crl" do @crl.generate(@cert, @key) @crl.content.should equal(@real_crl) end it "should return the generated crl" do @crl.generate(@cert, @key).should equal(@real_crl) end end # This test suite isn't exactly complete, because the # SSL stuff is very complicated. It just hits the high points. describe "when revoking a certificate" do before do @class.wrapped_class.any_instance.stubs(:issuer=) @class.wrapped_class.any_instance.stubs(:sign) @crl = @class.new("crl") @crl.generate(@cert, @key) @crl.content.stubs(:sign) Puppet::SSL::CertificateRevocationList.indirection.stubs :save @key = mock 'key' end it "should require a serial number and the CA's private key" do lambda { @crl.revoke }.should raise_error(ArgumentError) end it "should default to OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE as the revocation reason" do # This makes it a bit more of an integration test than we'd normally like, but that's life # with openssl. reason = OpenSSL::ASN1::Enumerated(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE) OpenSSL::ASN1.expects(:Enumerated).with(OpenSSL::OCSP::REVOKED_STATUS_KEYCOMPROMISE).returns reason @crl.revoke(1, @key) end it "should mark the CRL as updated at a time that makes it valid now" do time = Time.now Time.stubs(:now).returns time @crl.content.expects(:last_update=).with(time - 1) @crl.revoke(1, @key) end it "should mark the CRL valid for five years" do time = Time.now Time.stubs(:now).returns time @crl.content.expects(:next_update=).with(time + (5 * 365*24*60*60)) @crl.revoke(1, @key) end it "should sign the CRL with the CA's private key and a digest instance" do @crl.content.expects(:sign).with { |key, digest| key == @key and digest.is_a?(OpenSSL::Digest::SHA1) } @crl.revoke(1, @key) end it "should save the CRL" do Puppet::SSL::CertificateRevocationList.indirection.expects(:save).with(@crl, nil) @crl.revoke(1, @key) end end end diff --git a/spec/unit/ssl/certificate_spec.rb b/spec/unit/ssl/certificate_spec.rb index 1d0ddb934..64840dc03 100755 --- a/spec/unit/ssl/certificate_spec.rb +++ b/spec/unit/ssl/certificate_spec.rb @@ -1,154 +1,154 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/certificate' describe Puppet::SSL::Certificate do before do @class = Puppet::SSL::Certificate end after do @class.instance_variable_set("@ca_location", nil) end it "should be extended with the Indirector module" do @class.singleton_class.should be_include(Puppet::Indirector) end it "should indirect certificate" do @class.indirection.name.should == :certificate end it "should only support the text format" do @class.supported_formats.should == [:s] end describe "when converting from a string" do it "should create a certificate instance with its name set to the certificate subject and its content set to the extracted certificate" do cert = stub 'certificate', :subject => "/CN=Foo.madstop.com" OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(cert) mycert = stub 'sslcert' mycert.expects(:content=).with(cert) @class.expects(:new).with("foo.madstop.com").returns mycert @class.from_s("my certificate") end it "should create multiple certificate instances when asked" do cert1 = stub 'cert1' @class.expects(:from_s).with("cert1").returns cert1 cert2 = stub 'cert2' @class.expects(:from_s).with("cert2").returns cert2 @class.from_multiple_s("cert1\n---\ncert2").should == [cert1, cert2] end end describe "when converting to a string" do before do @certificate = @class.new("myname") end it "should return an empty string when it has no certificate" do @certificate.to_s.should == "" end it "should convert the certificate to pem format" do certificate = mock 'certificate', :to_pem => "pem" @certificate.content = certificate @certificate.to_s.should == "pem" end it "should be able to convert multiple instances to a string" do cert2 = @class.new("foo") @certificate.expects(:to_s).returns "cert1" cert2.expects(:to_s).returns "cert2" @class.to_multiple_s([@certificate, cert2]).should == "cert1\n---\ncert2" end end describe "when managing instances" do before do @certificate = @class.new("myname") end it "should have a name attribute" do @certificate.name.should == "myname" end it "should convert its name to a string and downcase it" do @class.new(:MyName).name.should == "myname" end it "should have a content attribute" do @certificate.should respond_to(:content) end describe "#subject_alt_names" do it "should list all alternate names when the extension is present" do key = Puppet::SSL::Key.new('quux') key.generate csr = Puppet::SSL::CertificateRequest.new('quux') csr.generate(key, :dns_alt_names => 'foo, bar,baz') raw_csr = csr.content cert = Puppet::SSL::CertificateFactory.build('server', csr, raw_csr, 14) certificate = @class.from_s(cert.to_pem) certificate.subject_alt_names. should =~ ['DNS:foo', 'DNS:bar', 'DNS:baz', 'DNS:quux'] end it "should return an empty list of names if the extension is absent" do key = Puppet::SSL::Key.new('quux') key.generate csr = Puppet::SSL::CertificateRequest.new('quux') csr.generate(key) raw_csr = csr.content cert = Puppet::SSL::CertificateFactory.build('client', csr, raw_csr, 14) certificate = @class.from_s(cert.to_pem) certificate.subject_alt_names.should be_empty end end it "should return a nil expiration if there is no actual certificate" do @certificate.stubs(:content).returns nil @certificate.expiration.should be_nil end it "should use the expiration of the certificate as its expiration date" do cert = stub 'cert' @certificate.stubs(:content).returns cert cert.expects(:not_after).returns "sometime" @certificate.expiration.should == "sometime" end it "should be able to read certificates from disk" do path = "/my/path" File.expects(:read).with(path).returns("my certificate") certificate = mock 'certificate' OpenSSL::X509::Certificate.expects(:new).with("my certificate").returns(certificate) @certificate.read(path).should equal(certificate) @certificate.content.should equal(certificate) end it "should have a :to_text method that it delegates to the actual key" do real_certificate = mock 'certificate' real_certificate.expects(:to_text).returns "certificatetext" @certificate.content = real_certificate @certificate.to_text.should == "certificatetext" end end end diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb index b3ea03e60..2deb5463b 100755 --- a/spec/unit/ssl/host_spec.rb +++ b/spec/unit/ssl/host_spec.rb @@ -1,863 +1,863 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/host' describe Puppet::SSL::Host do include PuppetSpec::Files before do Puppet::SSL::Host.indirection.terminus_class = :file # Get a safe temporary file dir = tmpdir("ssl_host_testing") Puppet.settings[:confdir] = dir Puppet.settings[:vardir] = dir Puppet.settings.use :main, :ssl @host = Puppet::SSL::Host.new("myname") end after do # Cleaned out any cached localhost instance. Puppet::SSL::Host.reset Puppet::SSL::Host.ca_location = :none end it "should use any provided name as its name" do @host.name.should == "myname" end it "should retrieve its public key from its private key" do realkey = mock 'realkey' key = stub 'key', :content => realkey Puppet::SSL::Key.indirection.stubs(:find).returns(key) pubkey = mock 'public_key' realkey.expects(:public_key).returns pubkey @host.public_key.should equal(pubkey) end it "should default to being a non-ca host" do @host.ca?.should be_false end it "should be a ca host if its name matches the CA_NAME" do Puppet::SSL::Host.stubs(:ca_name).returns "yayca" Puppet::SSL::Host.new("yayca").should be_ca end it "should have a method for determining the CA location" do Puppet::SSL::Host.should respond_to(:ca_location) end it "should have a method for specifying the CA location" do Puppet::SSL::Host.should respond_to(:ca_location=) end it "should have a method for retrieving the default ssl host" do Puppet::SSL::Host.should respond_to(:ca_location=) end it "should have a method for producing an instance to manage the local host's keys" do Puppet::SSL::Host.should respond_to(:localhost) end it "should allow to reset localhost" do previous_host = Puppet::SSL::Host.localhost Puppet::SSL::Host.reset Puppet::SSL::Host.localhost.should_not == previous_host end it "should generate the certificate for the localhost instance if no certificate is available" do host = stub 'host', :key => nil Puppet::SSL::Host.expects(:new).returns host host.expects(:certificate).returns nil host.expects(:generate) Puppet::SSL::Host.localhost.should equal(host) end it "should create a localhost cert if no cert is available and it is a CA with autosign and it is using DNS alt names", :unless => Puppet.features.microsoft_windows? do Puppet[:autosign] = true Puppet[:confdir] = tmpdir('conf') Puppet[:dns_alt_names] = "foo,bar,baz" ca = Puppet::SSL::CertificateAuthority.new Puppet::SSL::CertificateAuthority.stubs(:instance).returns ca localhost = Puppet::SSL::Host.localhost cert = localhost.certificate cert.should be_a(Puppet::SSL::Certificate) cert.subject_alt_names.should =~ %W[DNS:#{Puppet[:certname]} DNS:foo DNS:bar DNS:baz] end context "with dns_alt_names" do before :each do @key = stub('key content') key = stub('key', :generate => true, :content => @key) Puppet::SSL::Key.stubs(:new).returns key Puppet::SSL::Key.indirection.stubs(:save).with(key) @cr = stub('certificate request') Puppet::SSL::CertificateRequest.stubs(:new).returns @cr Puppet::SSL::CertificateRequest.indirection.stubs(:save).with(@cr) end describe "explicitly specified" do before :each do Puppet[:dns_alt_names] = 'one, two' end it "should not include subjectAltName if not the local node" do @cr.expects(:generate).with(@key, {}) Puppet::SSL::Host.new('not-the-' + Puppet[:certname]).generate end it "should include subjectAltName if I am a CA" do @cr.expects(:generate). with(@key, { :dns_alt_names => Puppet[:dns_alt_names] }) Puppet::SSL::Host.localhost end end describe "implicitly defaulted" do let(:ca) { stub('ca', :sign => nil) } before :each do Puppet[:dns_alt_names] = '' Puppet::SSL::CertificateAuthority.stubs(:instance).returns ca end it "should not include defaults if we're not the CA" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns false @cr.expects(:generate).with(@key, {}) Puppet::SSL::Host.localhost end it "should not include defaults if not the local node" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true @cr.expects(:generate).with(@key, {}) Puppet::SSL::Host.new('not-the-' + Puppet[:certname]).generate end it "should not include defaults if we can't resolve our fqdn" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true Facter.stubs(:value).with(:fqdn).returns nil @cr.expects(:generate).with(@key, {}) Puppet::SSL::Host.localhost end it "should provide defaults if we're bootstrapping the local master" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true Facter.stubs(:value).with(:fqdn).returns 'web.foo.com' Facter.stubs(:value).with(:domain).returns 'foo.com' @cr.expects(:generate).with(@key, {:dns_alt_names => "puppet, web.foo.com, puppet.foo.com"}) Puppet::SSL::Host.localhost end end end it "should always read the key for the localhost instance in from disk" do host = stub 'host', :certificate => "eh" Puppet::SSL::Host.expects(:new).returns host host.expects(:key) Puppet::SSL::Host.localhost end it "should cache the localhost instance" do host = stub 'host', :certificate => "eh", :key => 'foo' Puppet::SSL::Host.expects(:new).once.returns host Puppet::SSL::Host.localhost.should == Puppet::SSL::Host.localhost end it "should be able to verify its certificate matches its key" do Puppet::SSL::Host.new("foo").should respond_to(:validate_certificate_with_key) end it "should consider the certificate invalid if it cannot find a key" do host = Puppet::SSL::Host.new("foo") certificate = mock('cert', :fingerprint => 'DEADBEEF') host.expects(:certificate).twice.returns certificate host.expects(:key).returns nil lambda { host.validate_certificate_with_key }.should raise_error(Puppet::Error, "No private key with which to validate certificate with fingerprint: DEADBEEF") end it "should consider the certificate invalid if it cannot find a certificate" do host = Puppet::SSL::Host.new("foo") host.expects(:key).never host.expects(:certificate).returns nil lambda { host.validate_certificate_with_key }.should raise_error(Puppet::Error, "No certificate to validate.") end it "should consider the certificate invalid if the SSL certificate's key verification fails" do host = Puppet::SSL::Host.new("foo") key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', {:content => sslcert, :fingerprint => 'DEADBEEF'} host.stubs(:key).returns key host.stubs(:certificate).returns certificate sslcert.expects(:check_private_key).with("private_key").returns false lambda { host.validate_certificate_with_key }.should raise_error(Puppet::Error, /DEADBEEF/) end it "should consider the certificate valid if the SSL certificate's key verification succeeds" do host = Puppet::SSL::Host.new("foo") key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', :content => sslcert host.stubs(:key).returns key host.stubs(:certificate).returns certificate sslcert.expects(:check_private_key).with("private_key").returns true lambda{ host.validate_certificate_with_key }.should_not raise_error end describe "when specifying the CA location" do it "should support the location ':local'" do lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error end it "should support the location ':remote'" do lambda { Puppet::SSL::Host.ca_location = :remote }.should_not raise_error end it "should support the location ':none'" do lambda { Puppet::SSL::Host.ca_location = :none }.should_not raise_error end it "should support the location ':only'" do lambda { Puppet::SSL::Host.ca_location = :only }.should_not raise_error end it "should not support other modes" do lambda { Puppet::SSL::Host.ca_location = :whatever }.should raise_error(ArgumentError) end describe "as 'local'" do before do Puppet::SSL::Host.ca_location = :local end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Certificate.indirection.cache_class.should == :file Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file end it "should set the terminus class for Key and Host as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file Puppet::SSL::Host.indirection.terminus_class.should == :file end it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do Puppet::SSL::Certificate.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca end end describe "as 'remote'" do before do Puppet::SSL::Host.ca_location = :remote end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Certificate.indirection.cache_class.should == :file Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file end it "should set the terminus class for Key as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file end it "should set the terminus class for Host, Certificate, CertificateRevocationList, and CertificateRequest as :rest" do Puppet::SSL::Host.indirection.terminus_class.should == :rest Puppet::SSL::Certificate.indirection.terminus_class.should == :rest Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :rest Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :rest end end describe "as 'only'" do before do Puppet::SSL::Host.ca_location = :only end it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do Puppet::SSL::Key.indirection.terminus_class.should == :ca Puppet::SSL::Certificate.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do Puppet::SSL::Certificate.indirection.cache_class.should be_nil Puppet::SSL::CertificateRequest.indirection.cache_class.should be_nil Puppet::SSL::CertificateRevocationList.indirection.cache_class.should be_nil end it "should set the terminus class for Host to :file" do Puppet::SSL::Host.indirection.terminus_class.should == :file end end describe "as 'none'" do before do Puppet::SSL::Host.ca_location = :none end it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file Puppet::SSL::Certificate.indirection.terminus_class.should == :file Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :file end it "should set the terminus class for Host to 'none'" do lambda { Puppet::SSL::Host.indirection.terminus_class }.should raise_error(Puppet::DevError) end end end it "should have a class method for destroying all files related to a given host" do Puppet::SSL::Host.should respond_to(:destroy) end describe "when destroying a host's SSL files" do before do Puppet::SSL::Key.indirection.stubs(:destroy).returns false Puppet::SSL::Certificate.indirection.stubs(:destroy).returns false Puppet::SSL::CertificateRequest.indirection.stubs(:destroy).returns false end it "should destroy its certificate, certificate request, and key" do Puppet::SSL::Key.indirection.expects(:destroy).with("myhost") Puppet::SSL::Certificate.indirection.expects(:destroy).with("myhost") Puppet::SSL::CertificateRequest.indirection.expects(:destroy).with("myhost") Puppet::SSL::Host.destroy("myhost") end it "should return true if any of the classes returned true" do Puppet::SSL::Certificate.indirection.expects(:destroy).with("myhost").returns true Puppet::SSL::Host.destroy("myhost").should be_true end it "should report that nothing was deleted if none of the classes returned true" do Puppet::SSL::Host.destroy("myhost").should == "Nothing was deleted" end end describe "when initializing" do it "should default its name to the :certname setting" do Puppet.settings.expects(:value).with(:certname).returns "myname" Puppet::SSL::Host.new.name.should == "myname" end it "should downcase a passed in name" do Puppet::SSL::Host.new("Host.Domain.Com").name.should == "host.domain.com" end it "should downcase the certname if it's used" do Puppet.settings.expects(:value).with(:certname).returns "Host.Domain.Com" Puppet::SSL::Host.new.name.should == "host.domain.com" end it "should indicate that it is a CA host if its name matches the ca_name constant" do Puppet::SSL::Host.stubs(:ca_name).returns "myca" Puppet::SSL::Host.new("myca").should be_ca end end describe "when managing its private key" do before do @realkey = "mykey" @key = Puppet::SSL::Key.new("mykey") @key.content = @realkey end it "should return nil if the key is not set and cannot be found" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(nil) @host.key.should be_nil end it "should find the key in the Key class and return the Puppet instance" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(@key) @host.key.should equal(@key) end it "should be able to generate and save a new key" do Puppet::SSL::Key.expects(:new).with("myname").returns(@key) @key.expects(:generate) Puppet::SSL::Key.indirection.expects(:save) @host.generate_key.should be_true @host.key.should equal(@key) end it "should not retain keys that could not be saved" do Puppet::SSL::Key.expects(:new).with("myname").returns(@key) @key.stubs(:generate) Puppet::SSL::Key.indirection.expects(:save).raises "eh" lambda { @host.generate_key }.should raise_error @host.key.should be_nil end it "should return any previously found key without requerying" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(@key).once @host.key.should equal(@key) @host.key.should equal(@key) end end describe "when managing its certificate request" do before do @realrequest = "real request" @request = Puppet::SSL::CertificateRequest.new("myname") @request.content = @realrequest end it "should return nil if the key is not set and cannot be found" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns(nil) @host.certificate_request.should be_nil end it "should find the request in the Key class and return it and return the Puppet SSL request" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns @request @host.certificate_request.should equal(@request) end it "should generate a new key when generating the cert request if no key exists" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.expects(:key).times(2).returns(nil).then.returns(key) @host.expects(:generate_key).returns(key) @request.stubs(:generate) Puppet::SSL::CertificateRequest.indirection.stubs(:save) @host.generate_certificate_request end it "should be able to generate and save a new request using the private key" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.stubs(:key).returns(key) @request.expects(:generate).with("mycontent", {}) Puppet::SSL::CertificateRequest.indirection.expects(:save).with(@request) @host.generate_certificate_request.should be_true @host.certificate_request.should equal(@request) end it "should return any previously found request without requerying" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns(@request).once @host.certificate_request.should equal(@request) @host.certificate_request.should equal(@request) end it "should not keep its certificate request in memory if the request cannot be saved" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.stubs(:key).returns(key) @request.stubs(:generate) @request.stubs(:name).returns("myname") terminus = stub 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |req| req.instance == @request && req.key == "myname" }.raises "eh" lambda { @host.generate_certificate_request }.should raise_error @host.instance_eval { @certificate_request }.should be_nil end end describe "when managing its certificate" do before do @realcert = mock 'certificate' @cert = stub 'cert', :content => @realcert @host.stubs(:key).returns mock("key") @host.stubs(:validate_certificate_with_key) end it "should find the CA certificate if it does not have a certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.stubs(:find).with("myname").returns @cert @host.certificate end it "should not find the CA certificate if it is the CA host" do @host.expects(:ca?).returns true Puppet::SSL::Certificate.indirection.stubs(:find) Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).never @host.certificate end it "should return nil if it cannot find a CA certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns nil Puppet::SSL::Certificate.indirection.expects(:find).with("myname").never @host.certificate.should be_nil end it "should find the key if it does not have one" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns mock("key") @host.certificate end it "should generate the key if one cannot be found" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns nil @host.expects(:generate_key) @host.certificate end it "should find the certificate in the Certificate class and return the Puppet certificate instance" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns @cert @host.certificate.should equal(@cert) end it "should return any previously found certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns(@cert).once @host.certificate.should equal(@cert) @host.certificate.should equal(@cert) end end it "should have a method for listing certificate hosts" do Puppet::SSL::Host.should respond_to(:search) end describe "when listing certificate hosts" do it "should default to listing all clients with any file types" do Puppet::SSL::Key.indirection.expects(:search).returns [] Puppet::SSL::Certificate.indirection.expects(:search).returns [] Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [] Puppet::SSL::Host.search end it "should be able to list only clients with a key" do Puppet::SSL::Key.indirection.expects(:search).returns [] Puppet::SSL::Certificate.indirection.expects(:search).never Puppet::SSL::CertificateRequest.indirection.expects(:search).never Puppet::SSL::Host.search :for => Puppet::SSL::Key end it "should be able to list only clients with a certificate" do Puppet::SSL::Key.indirection.expects(:search).never Puppet::SSL::Certificate.indirection.expects(:search).returns [] Puppet::SSL::CertificateRequest.indirection.expects(:search).never Puppet::SSL::Host.search :for => Puppet::SSL::Certificate end it "should be able to list only clients with a certificate request" do Puppet::SSL::Key.indirection.expects(:search).never Puppet::SSL::Certificate.indirection.expects(:search).never Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [] Puppet::SSL::Host.search :for => Puppet::SSL::CertificateRequest end it "should return a Host instance created with the name of each found instance" do key = stub 'key', :name => "key", :to_ary => nil cert = stub 'cert', :name => "cert", :to_ary => nil csr = stub 'csr', :name => "csr", :to_ary => nil Puppet::SSL::Key.indirection.expects(:search).returns [key] Puppet::SSL::Certificate.indirection.expects(:search).returns [cert] Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [csr] returned = [] %w{key cert csr}.each do |name| result = mock(name) returned << result Puppet::SSL::Host.expects(:new).with(name).returns result end result = Puppet::SSL::Host.search returned.each do |r| result.should be_include(r) end end end it "should have a method for generating all necessary files" do Puppet::SSL::Host.new("me").should respond_to(:generate) end describe "when generating files" do before do @host = Puppet::SSL::Host.new("me") @host.stubs(:generate_key) @host.stubs(:generate_certificate_request) end it "should generate a key if one is not present" do @host.stubs(:key).returns nil @host.expects(:generate_key) @host.generate end it "should generate a certificate request if one is not present" do @host.expects(:certificate_request).returns nil @host.expects(:generate_certificate_request) @host.generate end describe "and it can create a certificate authority" do before do @ca = mock 'ca' Puppet::SSL::CertificateAuthority.stubs(:instance).returns @ca end it "should use the CA to sign its certificate request if it does not have a certificate" do @host.expects(:certificate).returns nil @ca.expects(:sign).with(@host.name, true) @host.generate end end describe "and it cannot create a certificate authority" do before do Puppet::SSL::CertificateAuthority.stubs(:instance).returns nil end it "should seek its certificate" do @host.expects(:certificate) @host.generate end end end it "should have a method for creating an SSL store" do Puppet::SSL::Host.new("me").should respond_to(:ssl_store) end it "should always return the same store" do host = Puppet::SSL::Host.new("foo") store = mock 'store' store.stub_everything OpenSSL::X509::Store.expects(:new).returns store host.ssl_store.should equal(host.ssl_store) end describe "when creating an SSL store" do before do @host = Puppet::SSL::Host.new("me") @store = mock 'store' @store.stub_everything OpenSSL::X509::Store.stubs(:new).returns @store Puppet.settings.stubs(:value).with(:localcacert).returns "ssl_host_testing" Puppet::SSL::CertificateRevocationList.indirection.stubs(:find).returns(nil) end it "should accept a purpose" do @store.expects(:purpose=).with "my special purpose" @host.ssl_store("my special purpose") end it "should default to OpenSSL::X509::PURPOSE_ANY as the purpose" do @store.expects(:purpose=).with OpenSSL::X509::PURPOSE_ANY @host.ssl_store end it "should add the local CA cert file" do Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" @store.expects(:add_file).with "/ca/cert/file" @host.ssl_store end describe "and a CRL is available" do before do @crl = stub 'crl', :content => "real_crl" Puppet::SSL::CertificateRevocationList.indirection.stubs(:find).returns @crl Puppet.settings.stubs(:value).with(:certificate_revocation).returns true end it "should add the CRL" do @store.expects(:add_crl).with "real_crl" @host.ssl_store end it "should set the flags to OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK" do @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK @host.ssl_store end end end describe "when waiting for a cert" do before do @host = Puppet::SSL::Host.new("me") end it "should generate its certificate request and attempt to read the certificate again if no certificate is found" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate) @host.wait_for_cert(1) end it "should catch and log errors during CSR saving" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate).raises(RuntimeError).then.returns nil @host.stubs(:sleep) @host.wait_for_cert(1) end it "should sleep and retry after failures saving the CSR if waitforcert is enabled" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate).raises(RuntimeError).then.returns nil @host.expects(:sleep).with(1) @host.wait_for_cert(1) end it "should exit after failures saving the CSR of waitforcert is disabled" do @host.expects(:certificate).returns(nil) @host.expects(:generate).raises(RuntimeError) @host.expects(:puts) expect { @host.wait_for_cert(0) }.to exit_with 1 end it "should exit if the wait time is 0 and it can neither find nor retrieve a certificate" do @host.stubs(:certificate).returns nil @host.expects(:generate) @host.expects(:puts) expect { @host.wait_for_cert(0) }.to exit_with 1 end it "should sleep for the specified amount of time if no certificate is found after generating its certificate request" do @host.expects(:certificate).times(3).returns(nil).then.returns(nil).then.returns "foo" @host.expects(:generate) @host.expects(:sleep).with(1) @host.wait_for_cert(1) end it "should catch and log exceptions during certificate retrieval" do @host.expects(:certificate).times(3).returns(nil).then.raises(RuntimeError).then.returns("foo") @host.stubs(:generate) @host.stubs(:sleep) Puppet.expects(:err) @host.wait_for_cert(1) end end describe "when handling PSON", :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before do Puppet[:vardir] = tmpdir("ssl_test_vardir") Puppet[:ssldir] = tmpdir("ssl_test_ssldir") # localcacert is where each client stores the CA certificate # cacert is where the master stores the CA certificate # Since we need to play the role of both for testing we need them to be the same and exist Puppet[:cacert] = Puppet[:localcacert] @ca=Puppet::SSL::CertificateAuthority.new end describe "when converting to PSON" do it "should be able to identify a host with an unsigned certificate request" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request pson_hash = { "fingerprint" => host.certificate_request.fingerprint, "desired_state" => 'requested', "name" => host.name } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end it "should be able to identify a host with a signed certificate" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request @ca.sign(host.name) pson_hash = { "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint, "desired_state" => 'signed', "name" => host.name, } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end it "should be able to identify a host with a revoked certificate" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request @ca.sign(host.name) @ca.revoke(host.name) pson_hash = { "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint, "desired_state" => 'revoked', "name" => host.name, } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end end describe "when converting from PSON" do it "should return a Puppet::SSL::Host object with the specified desired state" do host = Puppet::SSL::Host.new("bazinga") host.desired_state="signed" pson_hash = { "name" => host.name, "desired_state" => host.desired_state, } generated_host = Puppet::SSL::Host.from_pson(pson_hash) generated_host.desired_state.should == host.desired_state generated_host.name.should == host.name end end end end diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb index fac601d42..8e31f9efd 100755 --- a/spec/unit/ssl/inventory_spec.rb +++ b/spec/unit/ssl/inventory_spec.rb @@ -1,179 +1,179 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/inventory' describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? do before do @class = Puppet::SSL::Inventory end it "should use the :certinventory setting for the path to the inventory file" do Puppet.settings.expects(:value).with(:cert_inventory).returns "/inven/tory" @class.any_instance.stubs(:rebuild) @class.new.path.should == "/inven/tory" end describe "when initializing" do it "should set its path to the inventory file" do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" @class.new.path.should == "/inven/tory" end end describe "when managing an inventory" do before do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" FileTest.stubs(:exist?).with("/inven/tory").returns true @inventory = @class.new @cert = mock 'cert' end describe "and creating the inventory file" do before do Puppet.settings.stubs(:write) FileTest.stubs(:exist?).with("/inven/tory").returns false Puppet::SSL::Certificate.indirection.stubs(:search).returns [] end it "should log that it is building a new inventory file" do Puppet.expects(:notice) @inventory.rebuild end it "should use the Settings to write to the file" do Puppet.settings.expects(:write).with(:cert_inventory) @inventory.rebuild end it "should add a header to the file" do fh = mock 'filehandle' Puppet.settings.stubs(:write).yields fh fh.expects(:print).with { |str| str =~ /^#/ } @inventory.rebuild end it "should add formatted information on all existing certificates" do cert1 = mock 'cert1' cert2 = mock 'cert2' Puppet::SSL::Certificate.indirection.expects(:search).with("*").returns [cert1, cert2] @class.any_instance.expects(:add).with(cert1) @class.any_instance.expects(:add).with(cert2) @inventory.rebuild end end describe "and adding a certificate" do it "should build the inventory file if one does not exist" do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" Puppet.settings.stubs(:write) FileTest.expects(:exist?).with("/inven/tory").returns false @inventory.expects(:rebuild) @inventory.add(@cert) end it "should use the Settings to write to the file" do Puppet.settings.expects(:write).with(:cert_inventory, "a") @inventory.add(@cert) end it "should use the actual certificate if it was passed a Puppet certificate" do cert = Puppet::SSL::Certificate.new("mycert") cert.content = @cert fh = stub 'filehandle', :print => nil Puppet.settings.stubs(:write).yields fh @inventory.expects(:format).with(@cert) @inventory.add(@cert) end it "should add formatted certificate information to the end of the file" do fh = mock 'filehandle' Puppet.settings.stubs(:write).yields fh @inventory.expects(:format).with(@cert).returns "myformat" fh.expects(:print).with("myformat") @inventory.add(@cert) end end describe "and formatting a certificate" do before do @cert = stub 'cert', :not_before => Time.now, :not_after => Time.now, :subject => "mycert", :serial => 15 end it "should print the serial number as a 4 digit hex number in the first field" do @inventory.format(@cert).split[0].should == "0x000f" # 15 in hex end it "should print the not_before date in '%Y-%m-%dT%H:%M:%S%Z' format in the second field" do @cert.not_before.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "before_time" @inventory.format(@cert).split[1].should == "before_time" end it "should print the not_after date in '%Y-%m-%dT%H:%M:%S%Z' format in the third field" do @cert.not_after.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "after_time" @inventory.format(@cert).split[2].should == "after_time" end it "should print the subject in the fourth field" do @inventory.format(@cert).split[3].should == "mycert" end it "should add a carriage return" do @inventory.format(@cert).should =~ /\n$/ end it "should produce a line consisting of the serial number, start date, expiration date, and subject" do # Just make sure our serial and subject bracket the lines. @inventory.format(@cert).should =~ /^0x.+mycert$/ end end it "should be able to find a given host's serial number" do @inventory.should respond_to(:serial) end describe "and finding a serial number" do it "should return nil if the inventory file is missing" do FileTest.expects(:exist?).with("/inven/tory").returns false @inventory.serial(:whatever).should be_nil end it "should return the serial number from the line matching the provided name" do File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] @inventory.serial("me").should == 15 end it "should return the number as an integer" do File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] @inventory.serial("me").should == 15 end end end end diff --git a/spec/unit/ssl/key_spec.rb b/spec/unit/ssl/key_spec.rb index cddf9f8ea..8ba602bb4 100755 --- a/spec/unit/ssl/key_spec.rb +++ b/spec/unit/ssl/key_spec.rb @@ -1,197 +1,197 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/ssl/key' describe Puppet::SSL::Key do before do @class = Puppet::SSL::Key end it "should be extended with the Indirector module" do @class.singleton_class.should be_include(Puppet::Indirector) end it "should indirect key" do @class.indirection.name.should == :key end it "should default to the :file terminus" do @class.indirection.terminus_class.should == :file end it "should only support the text format" do @class.supported_formats.should == [:s] end it "should have a method for determining whether it's a CA key" do @class.new("test").should respond_to(:ca?) end it "should consider itself a ca key if its name matches the CA_NAME" do @class.new(Puppet::SSL::Host.ca_name).should be_ca end describe "when initializing" do it "should set its password file to the :capass if it's a CA key" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:capass).returns "/ca/pass" key = Puppet::SSL::Key.new(Puppet::SSL::Host.ca_name) key.password_file.should == "/ca/pass" end it "should downcase its name" do @class.new("MyName").name.should == "myname" end it "should set its password file to the default password file if it is not the CA key" do Puppet.settings.stubs(:value).returns "whatever" Puppet.settings.stubs(:value).with(:passfile).returns "/normal/pass" key = Puppet::SSL::Key.new("notca") key.password_file.should == "/normal/pass" end end describe "when managing instances" do before do @key = @class.new("myname") end it "should have a name attribute" do @key.name.should == "myname" end it "should have a content attribute" do @key.should respond_to(:content) end it "should be able to read keys from disk" do path = "/my/path" File.expects(:read).with(path).returns("my key") key = mock 'key' OpenSSL::PKey::RSA.expects(:new).returns(key) @key.read(path).should equal(key) @key.content.should equal(key) end it "should not try to use the provided password file if the file does not exist" do FileTest.stubs(:exist?).returns false @key.password_file = "/path/to/password" path = "/my/path" File.stubs(:read).with(path).returns("my key") OpenSSL::PKey::RSA.expects(:new).with("my key", nil).returns(mock('key')) File.expects(:read).with("/path/to/password").never @key.read(path) end it "should read the key with the password retrieved from the password file if one is provided" do FileTest.stubs(:exist?).returns true @key.password_file = "/path/to/password" path = "/my/path" File.expects(:read).with(path).returns("my key") File.expects(:read).with("/path/to/password").returns("my password") key = mock 'key' OpenSSL::PKey::RSA.expects(:new).with("my key", "my password").returns(key) @key.read(path).should equal(key) @key.content.should equal(key) end it "should return an empty string when converted to a string with no key" do @key.to_s.should == "" end it "should convert the key to pem format when converted to a string" do key = mock 'key', :to_pem => "pem" @key.content = key @key.to_s.should == "pem" end it "should have a :to_text method that it delegates to the actual key" do real_key = mock 'key' real_key.expects(:to_text).returns "keytext" @key.content = real_key @key.to_text.should == "keytext" end end describe "when generating the private key" do before do @instance = @class.new("test") @key = mock 'key' end it "should create an instance of OpenSSL::PKey::RSA" do OpenSSL::PKey::RSA.expects(:new).returns(@key) @instance.generate end it "should create the private key with the keylength specified in the settings" do Puppet.settings.expects(:value).with(:keylength).returns("50") OpenSSL::PKey::RSA.expects(:new).with(50).returns(@key) @instance.generate end it "should set the content to the generated key" do OpenSSL::PKey::RSA.stubs(:new).returns(@key) @instance.generate @instance.content.should equal(@key) end it "should return the generated key" do OpenSSL::PKey::RSA.stubs(:new).returns(@key) @instance.generate.should equal(@key) end it "should return the key in pem format" do @instance.generate @instance.content.expects(:to_pem).returns "my normal key" @instance.to_s.should == "my normal key" end describe "with a password file set" do it "should return a nil password if the password file does not exist" do FileTest.expects(:exist?).with("/path/to/pass").returns false File.expects(:read).with("/path/to/pass").never @instance.password_file = "/path/to/pass" @instance.password.should be_nil end it "should return the contents of the password file as its password" do FileTest.expects(:exist?).with("/path/to/pass").returns true File.expects(:read).with("/path/to/pass").returns "my password" @instance.password_file = "/path/to/pass" @instance.password.should == "my password" end it "should export the private key to text using the password" do Puppet.settings.stubs(:value).with(:keylength).returns("50") @instance.password_file = "/path/to/pass" @instance.stubs(:password).returns "my password" OpenSSL::PKey::RSA.expects(:new).returns(@key) @instance.generate cipher = mock 'cipher' OpenSSL::Cipher::DES.expects(:new).with(:EDE3, :CBC).returns cipher @key.expects(:export).with(cipher, "my password").returns "my encrypted key" @instance.to_s.should == "my encrypted key" end end end end diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb index 0c572fd95..50e872fec 100755 --- a/spec/unit/status_spec.rb +++ b/spec/unit/status_spec.rb @@ -1,34 +1,34 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Status do it "should implement find" do Puppet::Status.indirection.find( :default ).should be_is_a(Puppet::Status) Puppet::Status.indirection.find( :default ).status["is_alive"].should == true end it "should default to is_alive is true" do Puppet::Status.new.status["is_alive"].should == true end it "should return a pson hash" do Puppet::Status.new.status.to_pson.should == '{"is_alive":true}' end it "should render to a pson hash" do PSON::pretty_generate(Puppet::Status.new).should =~ /"is_alive":\s*true/ end it "should accept a hash from pson" do status = Puppet::Status.new( { "is_alive" => false } ) status.status.should == { "is_alive" => false } end it "should have a name" do Puppet::Status.new.name end it "should allow a name to be set" do Puppet::Status.new.name = "status" end end diff --git a/spec/unit/transaction/event_manager_spec.rb b/spec/unit/transaction/event_manager_spec.rb index 37775997d..3142f224e 100755 --- a/spec/unit/transaction/event_manager_spec.rb +++ b/spec/unit/transaction/event_manager_spec.rb @@ -1,261 +1,261 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction/event_manager' describe Puppet::Transaction::EventManager do include PuppetSpec::Files describe "at initialization" do it "should require a transaction" do Puppet::Transaction::EventManager.new("trans").transaction.should == "trans" end end it "should delegate its relationship graph to the transaction" do transaction = stub 'transaction' manager = Puppet::Transaction::EventManager.new(transaction) transaction.expects(:relationship_graph).returns "mygraph" manager.relationship_graph.should == "mygraph" end describe "when queueing events" do before do @manager = Puppet::Transaction::EventManager.new(@transaction) @resource = Puppet::Type.type(:file).new :path => make_absolute("/my/file") @graph = stub 'graph', :matching_edges => [], :resource => @resource @manager.stubs(:relationship_graph).returns @graph @event = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource) end it "should store all of the events in its event list" do @event2 = Puppet::Transaction::Event.new(:name => :bar, :resource => @resource) @manager.queue_events(@resource, [@event, @event2]) @manager.events.should include(@event) @manager.events.should include(@event2) end it "should queue events for the target and callback of any matching edges" do edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1", :c1 => nil)) edge2 = stub("edge2", :callback => :c2, :source => stub("s2"), :target => stub("t2", :c2 => nil)) @graph.expects(:matching_edges).with { |event, resource| event == @event }.returns [edge1, edge2] @manager.expects(:queue_events_for_resource).with(@resource, edge1.target, edge1.callback, [@event]) @manager.expects(:queue_events_for_resource).with(@resource, edge2.target, edge2.callback, [@event]) @manager.queue_events(@resource, [@event]) end it "should queue events for the changed resource if the resource is self-refreshing and not being deleted" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns true @resource.expects(:deleting?).returns false @manager.expects(:queue_events_for_resource).with(@resource, @resource, :refresh, [@event]) @manager.queue_events(@resource, [@event]) end it "should not queue events for the changed resource if the resource is not self-refreshing" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns false @resource.stubs(:deleting?).returns false @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should not queue events for the changed resource if the resource is being deleted" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns true @resource.expects(:deleting?).returns true @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should ignore edges that don't have a callback" do edge1 = stub("edge1", :callback => :nil, :source => stub("s1"), :target => stub("t1", :c1 => nil)) @graph.expects(:matching_edges).returns [edge1] @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should ignore targets that don't respond to the callback" do edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1")) @graph.expects(:matching_edges).returns [edge1] @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end end describe "when queueing events for a resource" do before do @transaction = stub 'transaction' @manager = Puppet::Transaction::EventManager.new(@transaction) end it "should do nothing if no events are queued" do @manager.queued_events(stub("target")) { |callback, events| raise "should never reach this" } end it "should yield the callback and events for each callback" do target = stub("target") 2.times do |i| @manager.queue_events_for_resource(stub("source", :info => nil), target, "callback#{i}", ["event#{i}"]) end @manager.queued_events(target) { |callback, events| } end it "should use the source to log that it's scheduling a refresh of the target" do target = stub("target") source = stub 'source' source.expects(:info) @manager.queue_events_for_resource(source, target, "callback", ["event"]) @manager.queued_events(target) { |callback, events| } end end describe "when processing events for a given resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @manager = Puppet::Transaction::EventManager.new(@transaction) @manager.stubs(:queue_events) @resource = Puppet::Type.type(:file).new :path => make_absolute("/my/file") @event = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) end it "should call the required callback once for each set of associated events" do @manager.expects(:queued_events).with(@resource).multiple_yields([:callback1, [@event]], [:callback2, [@event]]) @resource.expects(:callback1) @resource.expects(:callback2) @manager.process_events(@resource) end it "should set the 'restarted' state on the resource status" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @manager.process_events(@resource) @transaction.resource_status(@resource).should be_restarted end it "should queue a 'restarted' event generated by the resource" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @resource.expects(:event).with(:name => :restarted, :status => "success").returns "myevent" @manager.expects(:queue_events).with(@resource, ["myevent"]) @manager.process_events(@resource) end it "should log that it restarted" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @resource.expects(:notice).with { |msg| msg.include?("Triggered 'callback1'") } @manager.process_events(@resource) end describe "and the events include a noop event and at least one non-noop event" do before do @event.stubs(:status).returns "noop" @event2 = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) @event2.status = "success" @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event, @event2]) end it "should call the callback" do @resource.expects(:callback1) @manager.process_events(@resource) end end describe "and the events are all noop events" do before do @event.stubs(:status).returns "noop" @resource.stubs(:event).returns(Puppet::Transaction::Event.new) @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) end it "should log" do @resource.expects(:notice).with { |msg| msg.include?("Would have triggered 'callback1'") } @manager.process_events(@resource) end it "should not call the callback" do @resource.expects(:callback1).never @manager.process_events(@resource) end it "should queue a new noop event generated from the resource" do event = Puppet::Transaction::Event.new @resource.expects(:event).with(:status => "noop", :name => :noop_restart).returns event @manager.expects(:queue_events).with(@resource, [event]) @manager.process_events(@resource) end end describe "and the callback fails" do before do @resource.expects(:callback1).raises "a failure" @resource.stubs(:err) @manager.expects(:queued_events).yields(:callback1, [@event]) end it "should log but not fail" do @resource.expects(:err) lambda { @manager.process_events(@resource) }.should_not raise_error end it "should set the 'failed_restarts' state on the resource status" do @manager.process_events(@resource) @transaction.resource_status(@resource).should be_failed_to_restart end it "should not queue a 'restarted' event" do @manager.expects(:queue_events).never @manager.process_events(@resource) end it "should set the 'restarted' state on the resource status" do @manager.process_events(@resource) @transaction.resource_status(@resource).should_not be_restarted end end end end diff --git a/spec/unit/transaction/event_spec.rb b/spec/unit/transaction/event_spec.rb index ad5c00d76..eaabeab02 100755 --- a/spec/unit/transaction/event_spec.rb +++ b/spec/unit/transaction/event_spec.rb @@ -1,128 +1,128 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction/event' describe Puppet::Transaction::Event do include PuppetSpec::Files [:previous_value, :desired_value, :property, :resource, :name, :message, :file, :line, :tags, :audited].each do |attr| it "should support #{attr}" do event = Puppet::Transaction::Event.new event.send(attr.to_s + "=", "foo") event.send(attr).should == "foo" end end it "should always convert the property to a string" do Puppet::Transaction::Event.new(:property => :foo).property.should == "foo" end it "should always convert the resource to a string" do Puppet::Transaction::Event.new(:resource => :foo).resource.should == "foo" end it "should produce the message when converted to a string" do event = Puppet::Transaction::Event.new event.expects(:message).returns "my message" event.to_s.should == "my message" end it "should support 'status'" do event = Puppet::Transaction::Event.new event.status = "success" event.status.should == "success" end it "should fail if the status is not to 'audit', 'noop', 'success', or 'failure" do event = Puppet::Transaction::Event.new lambda { event.status = "foo" }.should raise_error(ArgumentError) end it "should support tags" do Puppet::Transaction::Event.ancestors.should include(Puppet::Util::Tagging) end it "should create a timestamp at its creation time" do Puppet::Transaction::Event.new.time.should be_instance_of(Time) end describe "audit property" do it "should default to false" do Puppet::Transaction::Event.new.audited.should == false end end describe "when sending logs" do before do Puppet::Util::Log.stubs(:new) end it "should set the level to the resources's log level if the event status is 'success' and a resource is available" do resource = stub 'resource' resource.expects(:[]).with(:loglevel).returns :myloglevel Puppet::Util::Log.expects(:create).with { |args| args[:level] == :myloglevel } Puppet::Transaction::Event.new(:status => "success", :resource => resource).send_log end it "should set the level to 'notice' if the event status is 'success' and no resource is available" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } Puppet::Transaction::Event.new(:status => "success").send_log end it "should set the level to 'notice' if the event status is 'noop'" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } Puppet::Transaction::Event.new(:status => "noop").send_log end it "should set the level to 'err' if the event status is 'failure'" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :err } Puppet::Transaction::Event.new(:status => "failure").send_log end it "should set the 'message' to the event log" do Puppet::Util::Log.expects(:new).with { |args| args[:message] == "my message" } Puppet::Transaction::Event.new(:message => "my message").send_log end it "should set the tags to the event tags" do Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } Puppet::Transaction::Event.new(:tags => %w{one two}).send_log end [:file, :line].each do |attr| it "should pass the #{attr}" do Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } Puppet::Transaction::Event.new(attr => "my val").send_log end end it "should use the source description as the source if one is set" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "/my/param" } Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => "Foo[bar]", :property => "foo").send_log end it "should use the property as the source if one is available and no source description is set" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "foo" } Puppet::Transaction::Event.new(:resource => "Foo[bar]", :property => "foo").send_log end it "should use the property as the source if one is available and no property or source description is set" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "Foo[bar]" } Puppet::Transaction::Event.new(:resource => "Foo[bar]").send_log end end describe "When converting to YAML" do it "should include only documented attributes" do resource = Puppet::Type.type(:file).new(:title => make_absolute("/tmp/foo")) event = Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => resource, :file => "/foo.rb", :line => 27, :tags => %w{one two}, :desired_value => 7, :historical_value => 'Brazil', :message => "Help I'm trapped in a spec test", :name => :mode_changed, :previous_value => 6, :property => :mode, :status => 'success') event.to_yaml_properties.should == Puppet::Transaction::Event::YAML_ATTRIBUTES.sort end end end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 2e69b3554..574be08a8 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -1,346 +1,346 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction/report' describe Puppet::Transaction::Report do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end it "should set its host name to the node_name_value" do Puppet[:node_name_value] = 'mynode' Puppet::Transaction::Report.new("apply").host.should == "mynode" end it "should return its host name as its name" do r = Puppet::Transaction::Report.new("apply") r.name.should == r.host end it "should create an initialization timestamp" do Time.expects(:now).returns "mytime" Puppet::Transaction::Report.new("apply").time.should == "mytime" end it "should take a 'kind' as an argument" do Puppet::Transaction::Report.new("inspect").kind.should == "inspect" end it "should take a 'configuration_version' as an argument" do Puppet::Transaction::Report.new("inspect", "some configuration version", "some environment").configuration_version.should == "some configuration version" end it "should be able to set configuration_version" do report = Puppet::Transaction::Report.new("inspect") report.configuration_version = "some version" report.configuration_version.should == "some version" end it "should take 'environment' as an argument" do Puppet::Transaction::Report.new("inspect", "some configuration version", "some environment").environment.should == "some environment" end it "should be able to set environment" do report = Puppet::Transaction::Report.new("inspect") report.environment = "some environment" report.environment.should == "some environment" end it "should not include whits" do Puppet::FileBucket::File.indirection.stubs(:save) filename = tmpfile('whit_test') file = Puppet::Type.type(:file).new(:path => filename) catalog = Puppet::Resource::Catalog.new catalog.add_resource(file) report = Puppet::Transaction::Report.new("apply") catalog.apply(:report => report) report.finalize_report report.resource_statuses.values.any? {|res| res.resource_type =~ /whit/i}.should be_false report.metrics['time'].values.any? {|metric| metric.first =~ /whit/i}.should be_false end describe "when accepting logs" do before do @report = Puppet::Transaction::Report.new("apply") end it "should add new logs to the log list" do @report << "log" @report.logs[-1].should == "log" end it "should return self" do r = @report << "log" r.should equal(@report) end end describe "when accepting resource statuses" do before do @report = Puppet::Transaction::Report.new("apply") end it "should add each status to its status list" do status = stub 'status', :resource => "foo" @report.add_resource_status status @report.resource_statuses["foo"].should equal(status) end end describe "when using the indirector" do it "should redirect :save to the indirection" do Facter.stubs(:value).returns("eh") @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) report = Puppet::Transaction::Report.new("apply") @indirection.expects(:save) Puppet::Transaction::Report.indirection.save(report) end it "should default to the 'processor' terminus" do Puppet::Transaction::Report.indirection.terminus_class.should == :processor end it "should delegate its name attribute to its host method" do report = Puppet::Transaction::Report.new("apply") report.expects(:host).returns "me" report.name.should == "me" end end describe "when computing exit status" do it "should produce 2 if changes are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 1}) report.add_metric("resources", {"failed" => 0}) report.exit_status.should == 2 end it "should produce 4 if failures are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 0}) report.add_metric("resources", {"failed" => 1}) report.exit_status.should == 4 end it "should produce 6 if both changes and failures are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 1}) report.add_metric("resources", {"failed" => 1}) report.exit_status.should == 6 end end describe "before finalizing the report" do it "should have a status of 'failed'" do report = Puppet::Transaction::Report.new("apply") report.status.should == 'failed' end end describe "when finalizing the report" do before do @report = Puppet::Transaction::Report.new("apply") end def metric(name, value) if metric = @report.metrics[name.to_s] metric[value] else nil end end def add_statuses(count, type = :file) count.times do |i| status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => make_absolute("/my/path#{i}"))) yield status if block_given? @report.add_resource_status status end end [:time, :resources, :changes, :events].each do |type| it "should add #{type} metrics" do @report.finalize_report @report.metrics[type.to_s].should be_instance_of(Puppet::Transaction::Metric) end end describe "for resources" do it "should provide the total number of resources" do add_statuses(3) @report.finalize_report metric(:resources, "total").should == 3 end Puppet::Resource::Status::STATES.each do |state| it "should provide the number of #{state} resources as determined by the status objects" do add_statuses(3) { |status| status.send(state.to_s + "=", true) } @report.finalize_report metric(:resources, state.to_s).should == 3 end it "should provide 0 for states not in status" do @report.finalize_report metric(:resources, state.to_s).should == 0 end end it "should mark the report as 'failed' if there are failing resources" do add_statuses(1) { |status| status.failed = true } @report.finalize_report @report.status.should == 'failed' end end describe "for changes" do it "should provide the number of changes from the resource statuses and mark the report as 'changed'" do add_statuses(3) { |status| 3.times { status << Puppet::Transaction::Event.new(:status => 'success') } } @report.finalize_report metric(:changes, "total").should == 9 @report.status.should == 'changed' end it "should provide a total even if there are no changes, and mark the report as 'unchanged'" do @report.finalize_report metric(:changes, "total").should == 0 @report.status.should == 'unchanged' end end describe "for times" do it "should provide the total amount of time for each resource type" do add_statuses(3, :file) do |status| status.evaluation_time = 1 end add_statuses(3, :exec) do |status| status.evaluation_time = 2 end add_statuses(3, :tidy) do |status| status.evaluation_time = 3 end @report.finalize_report metric(:time, "file").should == 3 metric(:time, "exec").should == 6 metric(:time, "tidy").should == 9 end it "should add any provided times from external sources" do @report.add_times :foobar, 50 @report.finalize_report metric(:time, "foobar").should == 50 end it "should have a total time" do add_statuses(3, :file) do |status| status.evaluation_time = 1.25 end @report.add_times :config_retrieval, 0.5 @report.finalize_report metric(:time, "total").should == 4.25 end end describe "for events" do it "should provide the total number of events" do add_statuses(3) do |status| 3.times { |i| status.add_event(Puppet::Transaction::Event.new :status => 'success') } end @report.finalize_report metric(:events, "total").should == 9 end it "should provide the total even if there are no events" do @report.finalize_report metric(:events, "total").should == 0 end Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name| it "should provide the number of #{status_name} events" do add_statuses(3) do |status| 3.times do |i| event = Puppet::Transaction::Event.new event.status = status_name status.add_event(event) end end @report.finalize_report metric(:events, status_name).should == 9 end end end end describe "when producing a summary" do before do resource = Puppet::Type.type(:notify).new(:name => "testing") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource catalog.version = 1234567 trans = catalog.apply @report = trans.report @report.finalize_report end %w{changes time resources events version}.each do |main| it "should include the key #{main} in the raw summary hash" do @report.raw_summary.should be_key main end end it "should include the last run time in the raw summary hash" do Time.stubs(:now).returns(Time.utc(2010,11,10,12,0,24)) @report.raw_summary["time"]["last_run"].should == 1289390424 end it "should include all resource statuses" do resources_report = @report.raw_summary["resources"] Puppet::Resource::Status::STATES.each do |state| resources_report.should be_include(state.to_s) end end %w{total failure success}.each do |r| it "should include event #{r}" do events_report = @report.raw_summary["events"] events_report.should be_include(r) end end it "should include config version" do @report.raw_summary["version"]["config"].should == 1234567 end it "should include puppet version" do @report.raw_summary["version"]["puppet"].should == Puppet.version end %w{Changes Total Resources Time Events}.each do |main| it "should include information on #{main} in the textual summary" do @report.summary.should be_include(main) end end end describe "when outputting yaml" do it "should not include @external_times" do report = Puppet::Transaction::Report.new('apply') report.add_times('config_retrieval', 1.0) report.to_yaml_properties.should_not include('@external_times') end end end diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb index 20aac576e..54bb80334 100755 --- a/spec/unit/transaction/resource_harness_spec.rb +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -1,484 +1,484 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction/resource_harness' describe Puppet::Transaction::ResourceHarness do include PuppetSpec::Files before do @mode_750 = Puppet.features.microsoft_windows? ? '644' : '750' @mode_755 = Puppet.features.microsoft_windows? ? '644' : '755' path = make_absolute("/my/file") @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @resource = Puppet::Type.type(:file).new :path => path @harness = Puppet::Transaction::ResourceHarness.new(@transaction) @current_state = Puppet::Resource.new(:file, path) @resource.stubs(:retrieve).returns @current_state @status = Puppet::Resource::Status.new(@resource) Puppet::Resource::Status.stubs(:new).returns @status end it "should accept a transaction at initialization" do harness = Puppet::Transaction::ResourceHarness.new(@transaction) harness.transaction.should equal(@transaction) end it "should delegate to the transaction for its relationship graph" do @transaction.expects(:relationship_graph).returns "relgraph" Puppet::Transaction::ResourceHarness.new(@transaction).relationship_graph.should == "relgraph" end describe "when evaluating a resource" do it "should create and return a resource status instance for the resource" do @harness.evaluate(@resource).should be_instance_of(Puppet::Resource::Status) end it "should fail if no status can be created" do Puppet::Resource::Status.expects(:new).raises ArgumentError lambda { @harness.evaluate(@resource) }.should raise_error end it "should retrieve the current state of the resource" do @resource.expects(:retrieve).returns @current_state @harness.evaluate(@resource) end it "should mark the resource as failed and return if the current state cannot be retrieved" do @resource.expects(:retrieve).raises ArgumentError @harness.evaluate(@resource).should be_failed end it "should store the resource's evaluation time in the resource status" do @harness.evaluate(@resource).evaluation_time.should be_instance_of(Float) end end def events_to_hash(events) events.map do |event| hash = {} event.instance_variables.each do |varname| hash[varname] = event.instance_variable_get(varname) end hash end end def make_stub_provider stubProvider = Class.new(Puppet::Type) stubProvider.instance_eval do initvars newparam(:name) do desc "The name var" isnamevar end newproperty(:foo) do desc "A property that can be changed successfully" def sync end def retrieve :absent end def insync?(reference_value) false end end newproperty(:bar) do desc "A property that raises an exception when you try to change it" def sync raise ZeroDivisionError.new('bar') end def retrieve :absent end def insync?(reference_value) false end end end stubProvider end describe "when an error occurs" do before :each do stub_provider = make_stub_provider resource = stub_provider.new :name => 'name', :foo => 1, :bar => 2 resource.expects(:err).never @status = @harness.evaluate(resource) end it "should record previous successful events" do @status.events[0].property.should == 'foo' @status.events[0].status.should == 'success' end it "should record a failure event" do @status.events[1].property.should == 'bar' @status.events[1].status.should == 'failure' end end describe "when auditing" do it "should not call insync? on parameters that are merely audited" do stub_provider = make_stub_provider resource = stub_provider.new :name => 'name', :audit => ['foo'] resource.property(:foo).expects(:insync?).never status = @harness.evaluate(resource) status.events.each do |event| event.status.should != 'failure' end end it "should be able to audit a file's group" do # see bug #5710 test_file = tmpfile('foo') File.open(test_file, 'w').close resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['group'], :backup => false resource.expects(:err).never # make sure no exceptions get swallowed status = @harness.evaluate(resource) status.events.each do |event| event.status.should != 'failure' end end end describe "when applying changes" do [false, true].each do |noop_mode|; describe (noop_mode ? "in noop mode" : "in normal mode") do [nil, @mode_750].each do |machine_state|; describe (machine_state ? "with a file initially present" : "with no file initially present") do [nil, @mode_750, @mode_755].each do |yaml_mode| [nil, :file, :absent].each do |yaml_ensure|; describe "with mode=#{yaml_mode.inspect} and ensure=#{yaml_ensure.inspect} stored in state.yml" do [false, true].each do |auditing_ensure| [false, true].each do |auditing_mode| auditing = [] auditing.push(:mode) if auditing_mode auditing.push(:ensure) if auditing_ensure [nil, :file, :absent].each do |ensure_property| # what we set "ensure" to in the manifest [nil, @mode_750, @mode_755].each do |mode_property| # what we set "mode" to in the manifest manifest_settings = {} manifest_settings[:audit] = auditing if !auditing.empty? manifest_settings[:ensure] = ensure_property if ensure_property manifest_settings[:mode] = mode_property if mode_property describe "with manifest settings #{manifest_settings.inspect}" do; it "should behave properly" do # Set up preconditions test_file = tmpfile('foo') if machine_state File.open(test_file, 'w', machine_state.to_i(8)).close end Puppet[:noop] = noop_mode params = { :path => test_file, :backup => false } params.merge!(manifest_settings) resource = Puppet::Type.type(:file).new params @harness.cache(resource, :mode, yaml_mode) if yaml_mode @harness.cache(resource, :ensure, yaml_ensure) if yaml_ensure fake_time = Time.utc(2011, 'jan', 3, 12, 24, 0) Time.stubs(:now).returns(fake_time) # So that Puppet::Resource::Status objects will compare properly resource.expects(:err).never # make sure no exceptions get swallowed status = @harness.evaluate(resource) # do the thing # check that the state of the machine has been properly updated expected_logs = [] expected_status_events = [] if auditing_mode @harness.cached(resource, :mode).should == (machine_state || :absent) else @harness.cached(resource, :mode).should == yaml_mode end if auditing_ensure @harness.cached(resource, :ensure).should == (machine_state ? :file : :absent) else @harness.cached(resource, :ensure).should == yaml_ensure end if ensure_property == :file file_would_be_there_if_not_noop = true elsif ensure_property == nil file_would_be_there_if_not_noop = machine_state != nil else # ensure_property == :absent file_would_be_there_if_not_noop = false end file_should_be_there = noop_mode ? machine_state != nil : file_would_be_there_if_not_noop File.exists?(test_file).should == file_should_be_there if file_should_be_there if noop_mode expected_file_mode = machine_state else expected_file_mode = mode_property || machine_state end if !expected_file_mode # we didn't specify a mode and the file was created, so mode comes from umode else file_mode = File.stat(test_file).mode & 0777 file_mode.should == expected_file_mode.to_i(8) end end # Test log output for the "mode" parameter previously_recorded_mode_already_logged = false mode_status_msg = nil if machine_state && file_would_be_there_if_not_noop && mode_property && machine_state != mode_property if noop_mode what_happened = "current_value #{machine_state}, should be #{mode_property} (noop)" expected_status = 'noop' else what_happened = "mode changed '#{machine_state}' to '#{mode_property}'" expected_status = 'success' end if auditing_mode && yaml_mode && yaml_mode != machine_state previously_recorded_mode_already_logged = true mode_status_msg = "#{what_happened} (previously recorded value was #{yaml_mode})" else mode_status_msg = what_happened end expected_logs << "notice: /#{resource}/mode: #{mode_status_msg}" end if @harness.cached(resource, :mode) && @harness.cached(resource, :mode) != yaml_mode if yaml_mode unless previously_recorded_mode_already_logged mode_status_msg = "audit change: previously recorded value #{yaml_mode} has been changed to #{@harness.cached(resource, :mode)}" expected_logs << "notice: /#{resource}/mode: #{mode_status_msg}" expected_status = 'audit' end else expected_logs << "notice: /#{resource}/mode: audit change: newly-recorded value #{@harness.cached(resource, :mode)}" end end if mode_status_msg expected_status_events << Puppet::Transaction::Event.new( :source_description => "/#{resource}/mode", :resource => resource, :file => nil, :line => nil, :tags => %w{file}, :desired_value => mode_property, :historical_value => yaml_mode, :message => mode_status_msg, :name => :mode_changed, :previous_value => machine_state || :absent, :property => :mode, :status => expected_status, :audited => auditing_mode) end # Test log output for the "ensure" parameter previously_recorded_ensure_already_logged = false ensure_status_msg = nil if file_would_be_there_if_not_noop != (machine_state != nil) if noop_mode what_happened = "current_value #{machine_state ? 'file' : 'absent'}, should be #{file_would_be_there_if_not_noop ? 'file' : 'absent'} (noop)" expected_status = 'noop' else what_happened = file_would_be_there_if_not_noop ? 'created' : 'removed' expected_status = 'success' end if auditing_ensure && yaml_ensure && yaml_ensure != (machine_state ? :file : :absent) previously_recorded_ensure_already_logged = true ensure_status_msg = "#{what_happened} (previously recorded value was #{yaml_ensure})" else ensure_status_msg = "#{what_happened}" end expected_logs << "notice: /#{resource}/ensure: #{ensure_status_msg}" end if @harness.cached(resource, :ensure) && @harness.cached(resource, :ensure) != yaml_ensure if yaml_ensure unless previously_recorded_ensure_already_logged ensure_status_msg = "audit change: previously recorded value #{yaml_ensure} has been changed to #{@harness.cached(resource, :ensure)}" expected_logs << "notice: /#{resource}/ensure: #{ensure_status_msg}" expected_status = 'audit' end else expected_logs << "notice: /#{resource}/ensure: audit change: newly-recorded value #{@harness.cached(resource, :ensure)}" end end if ensure_status_msg if ensure_property == :file ensure_event_name = :file_created elsif ensure_property == nil ensure_event_name = :file_changed else # ensure_property == :absent ensure_event_name = :file_removed end expected_status_events << Puppet::Transaction::Event.new( :source_description => "/#{resource}/ensure", :resource => resource, :file => nil, :line => nil, :tags => %w{file}, :desired_value => ensure_property, :historical_value => yaml_ensure, :message => ensure_status_msg, :name => ensure_event_name, :previous_value => machine_state ? :file : :absent, :property => :ensure, :status => expected_status, :audited => auditing_ensure) end # Actually check the logs. @logs.map {|l| "#{l.level}: #{l.source}: #{l.message}"}.should =~ expected_logs # All the log messages should show up as events except the "newly-recorded" ones. expected_event_logs = @logs.reject {|l| l.message =~ /newly-recorded/ } status.events.map {|e| e.message}.should =~ expected_event_logs.map {|l| l.message } events_to_hash(status.events).should =~ events_to_hash(expected_status_events) # Check change count - this is the number of changes that actually occurred. expected_change_count = 0 if (machine_state != nil) != file_should_be_there expected_change_count = 1 elsif machine_state != nil if expected_file_mode != machine_state expected_change_count = 1 end end status.change_count.should == expected_change_count # Check out of sync count - this is the number # of changes that would have occurred in # non-noop mode. expected_out_of_sync_count = 0 if (machine_state != nil) != file_would_be_there_if_not_noop expected_out_of_sync_count = 1 elsif machine_state != nil if mode_property != nil && mode_property != machine_state expected_out_of_sync_count = 1 end end if !noop_mode expected_out_of_sync_count.should == expected_change_count end status.out_of_sync_count.should == expected_out_of_sync_count # Check legacy summary fields status.changed.should == (expected_change_count != 0) status.out_of_sync.should == (expected_out_of_sync_count != 0) # Check the :synced field on state.yml synced_should_be_set = !noop_mode && status.changed (@harness.cached(resource, :synced) != nil).should == synced_should_be_set end; end end end end end end; end end end; end end; end it "should not apply changes if allow_changes?() returns false" do test_file = tmpfile('foo') resource = Puppet::Type.type(:file).new :path => test_file, :backup => false, :ensure => :file resource.expects(:err).never # make sure no exceptions get swallowed @harness.expects(:allow_changes?).with(resource).returns false status = @harness.evaluate(resource) File.exists?(test_file).should == false end end describe "when determining whether the resource can be changed" do before do @resource.stubs(:purging?).returns true @resource.stubs(:deleting?).returns true end it "should be true if the resource is not being purged" do @resource.expects(:purging?).returns false @harness.should be_allow_changes(@resource) end it "should be true if the resource is not being deleted" do @resource.expects(:deleting?).returns false @harness.should be_allow_changes(@resource) end it "should be true if the resource has no dependents" do @harness.relationship_graph.expects(:dependents).with(@resource).returns [] @harness.should be_allow_changes(@resource) end it "should be true if all dependents are being deleted" do dep = stub 'dependent', :deleting? => true @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] @resource.expects(:purging?).returns true @harness.should be_allow_changes(@resource) end it "should be false if the resource's dependents are not being deleted" do dep = stub 'dependent', :deleting? => false, :ref => "myres" @resource.expects(:warning) @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] @harness.should_not be_allow_changes(@resource) end end describe "when finding the schedule" do before do @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog end it "should warn and return nil if the resource has no catalog" do @resource.catalog = nil @resource.expects(:warning) @harness.schedule(@resource).should be_nil end it "should return nil if the resource specifies no schedule" do @harness.schedule(@resource).should be_nil end it "should fail if the named schedule cannot be found" do @resource[:schedule] = "whatever" @resource.expects(:fail) @harness.schedule(@resource) end it "should return the named schedule if it exists" do sched = Puppet::Type.type(:schedule).new(:name => "sched") @catalog.add_resource(sched) @resource[:schedule] = "sched" @harness.schedule(@resource).to_s.should == sched.to_s end end describe "when determining if a resource is scheduled" do before do @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @status = Puppet::Resource::Status.new(@resource) end it "should return true if 'ignoreschedules' is set" do Puppet[:ignoreschedules] = true @resource[:schedule] = "meh" @harness.should be_scheduled(@status, @resource) end it "should return true if the resource has no schedule set" do @harness.should be_scheduled(@status, @resource) end it "should return the result of matching the schedule with the cached 'checked' time if a schedule is set" do t = Time.now @harness.expects(:cached).with(@resource, :checked).returns(t) sched = Puppet::Type.type(:schedule).new(:name => "sched") @catalog.add_resource(sched) @resource[:schedule] = "sched" sched.expects(:match?).with(t.to_i).returns "feh" @harness.scheduled?(@status, @resource).should == "feh" end end it "should be able to cache data in the Storage module" do data = {} Puppet::Util::Storage.expects(:cache).with(@resource).returns data @harness.cache(@resource, :foo, "something") data[:foo].should == "something" end it "should be able to retrieve data from the cache" do data = {:foo => "other"} Puppet::Util::Storage.expects(:cache).with(@resource).returns data @harness.cached(@resource, :foo).should == "other" end end diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index f5720e75a..c5e51031d 100755 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -1,833 +1,833 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/transaction' require 'fileutils' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Transaction do include PuppetSpec::Files before do @basepath = make_absolute("/what/ever") @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) end it "should delegate its event list to the event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.expects(:events).returns %w{my events} @transaction.events.should == %w{my events} end it "should delegate adding times to its report" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.report.expects(:add_times).with(:foo, 10) @transaction.report.expects(:add_times).with(:bar, 20) @transaction.add_times :foo => 10, :bar => 20 end it "should be able to accept resource status instances" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource).should equal(status) end it "should be able to look resource status up by resource reference" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource.to_s).should equal(status) end # This will basically only ever be used during testing. it "should automatically create resource statuses if asked for a non-existent status" do resource = Puppet::Type.type(:notify).new :title => "foobar" @transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status) end it "should add provided resource statuses to its report" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.report.resource_statuses[resource.to_s].should equal(status) end it "should consider a resource to be failed if a status instance exists for that resource and indicates it is failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_failed(resource) end it "should not consider a resource to be failed if a status instance exists for that resource but indicates it is not failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_failed(resource) end it "should consider there to be failed resources if any statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_any_failed end it "should not consider there to be failed resources if no statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_any_failed end it "should use the provided report object" do report = Puppet::Transaction::Report.new("apply") @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, report) @transaction.report.should == report end it "should create a report if none is provided" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.report.should be_kind_of Puppet::Transaction::Report end describe "when initializing" do it "should create an event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager) @transaction.event_manager.transaction.should equal(@transaction) end it "should create a resource harness" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness) @transaction.resource_harness.transaction.should equal(@transaction) end end describe "when evaluating a resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.stubs(:skip?).returns false @resource = Puppet::Type.type(:file).new :path => @basepath end it "should check whether the resource should be skipped" do @transaction.expects(:skip?).with(@resource).returns false @transaction.eval_resource(@resource) end it "should process events" do @transaction.event_manager.expects(:process_events).with(@resource) @transaction.eval_resource(@resource) end describe "and the resource should be skipped" do before do @transaction.expects(:skip?).with(@resource).returns true end it "should mark the resource's status as skipped" do @transaction.eval_resource(@resource) @transaction.resource_status(@resource).should be_skipped end end end describe "when applying a resource" do before do @resource = Puppet::Type.type(:file).new :path => @basepath @status = Puppet::Resource::Status.new(@resource) @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.stubs(:queue_events) @transaction.resource_harness.stubs(:evaluate).returns(@status) end it "should use its resource harness to apply the resource" do @transaction.resource_harness.expects(:evaluate).with(@resource) @transaction.apply(@resource) end it "should add the resulting resource status to its status list" do @transaction.apply(@resource) @transaction.resource_status(@resource).should be_instance_of(Puppet::Resource::Status) end it "should queue any events added to the resource status" do @status.expects(:events).returns %w{a b} @transaction.event_manager.expects(:queue_events).with(@resource, ["a", "b"]) @transaction.apply(@resource) end it "should log and skip any resources that cannot be applied" do @transaction.resource_harness.expects(:evaluate).raises ArgumentError @resource.expects(:err) @transaction.apply(@resource) @transaction.report.resource_statuses[@resource.to_s].should be_nil end end describe "#eval_generate" do let(:path) { tmpdir('eval_generate') } let(:resource) { Puppet::Type.type(:file).new(:path => path, :recurse => true) } let(:graph) { @transaction.relationship_graph } def find_vertex(type, title) graph.vertices.find {|v| v.type == type and v.title == title} end before :each do @filenames = [] 'a'.upto('c') do |x| @filenames << File.join(path,x) 'a'.upto('c') do |y| @filenames << File.join(path,x,y) FileUtils.mkdir_p(File.join(path,x,y)) 'a'.upto('c') do |z| @filenames << File.join(path,x,y,z) FileUtils.touch(File.join(path,x,y,z)) end end end @transaction.catalog.add_resource(resource) end it "should add the generated resources to the catalog" do @transaction.eval_generate(resource) @filenames.each do |file| @transaction.catalog.resource(:file, file).should be_a(Puppet::Type.type(:file)) end end it "should add a sentinel whit for the resource" do @transaction.eval_generate(resource) find_vertex(:whit, "completed_#{path}").should be_a(Puppet::Type.type(:whit)) end it "should replace dependencies on the resource with dependencies on the sentinel" do dependent = Puppet::Type.type(:notify).new(:name => "hello", :require => resource) @transaction.catalog.add_resource(dependent) res = find_vertex(resource.type, resource.title) generated = find_vertex(dependent.type, dependent.title) graph.should be_edge(res, generated) @transaction.eval_generate(resource) sentinel = find_vertex(:whit, "completed_#{path}") graph.should be_edge(sentinel, generated) graph.should_not be_edge(res, generated) end it "should add an edge from the nearest ancestor to the generated resource" do @transaction.eval_generate(resource) @filenames.each do |file| v = find_vertex(:file, file) p = find_vertex(:file, File.dirname(file)) graph.should be_edge(p, v) end end it "should add an edge from each generated resource to the sentinel" do @transaction.eval_generate(resource) sentinel = find_vertex(:whit, "completed_#{path}") @filenames.each do |file| v = find_vertex(:file, file) graph.should be_edge(v, sentinel) end end it "should add an edge from the resource to the sentinel" do @transaction.eval_generate(resource) res = find_vertex(:file, path) sentinel = find_vertex(:whit, "completed_#{path}") graph.should be_edge(res, sentinel) end it "should return false if an error occured when generating resources" do resource.stubs(:eval_generate).raises(Puppet::Error) @transaction.eval_generate(resource).should == false end it "should return true if resources were generated" do @transaction.eval_generate(resource).should == true end it "should not add a sentinel if no resources are generated" do path2 = tmpfile('empty') other_file = Puppet::Type.type(:file).new(:path => path2) @transaction.catalog.add_resource(other_file) @transaction.eval_generate(other_file).should == false find_vertex(:whit, "completed_#{path2}").should be_nil end end describe "#unblock" do let(:graph) { @transaction.relationship_graph } let(:resource) { Puppet::Type.type(:notify).new(:name => 'foo') } it "should calculate the number of blockers if it's not known" do graph.add_vertex(resource) 3.times do |i| other = Puppet::Type.type(:notify).new(:name => i.to_s) graph.add_vertex(other) graph.add_edge(other, resource) end graph.unblock(resource) graph.blockers[resource].should == 2 end it "should decrement the number of blockers if there are any" do graph.blockers[resource] = 40 graph.unblock(resource) graph.blockers[resource].should == 39 end it "should warn if there are no blockers" do vertex = stub('vertex') vertex.expects(:warning).with "appears to have a negative number of dependencies" graph.blockers[vertex] = 0 graph.unblock(vertex) end it "should return true if the resource is now unblocked" do graph.blockers[resource] = 1 graph.unblock(resource).should == true end it "should return false if the resource is still blocked" do graph.blockers[resource] = 2 graph.unblock(resource).should == false end end describe "#finish" do let(:graph) { @transaction.relationship_graph } let(:path) { tmpdir('eval_generate') } let(:resource) { Puppet::Type.type(:file).new(:path => path, :recurse => true) } before :each do @transaction.catalog.add_resource(resource) end it "should unblock the resource's dependents and queue them if ready" do dependent = Puppet::Type.type(:file).new(:path => tmpfile('dependent'), :require => resource) more_dependent = Puppet::Type.type(:file).new(:path => tmpfile('more_dependent'), :require => [resource, dependent]) @transaction.catalog.add_resource(dependent, more_dependent) graph.finish(resource) graph.blockers[dependent].should == 0 graph.blockers[more_dependent].should == 1 key = graph.unguessable_deterministic_key[dependent] graph.ready[key].must == dependent graph.ready.should_not be_has_key(graph.unguessable_deterministic_key[more_dependent]) end it "should mark the resource as done" do graph.finish(resource) graph.done[resource].should == true end end describe "when traversing" do let(:graph) { @transaction.relationship_graph } let(:path) { tmpdir('eval_generate') } let(:resource) { Puppet::Type.type(:file).new(:path => path, :recurse => true) } before :each do @transaction.catalog.add_resource(resource) end it "should clear blockers if resources are added" do graph.blockers['foo'] = 3 graph.blockers['bar'] = 4 graph.ready[graph.unguessable_deterministic_key[resource]] = resource @transaction.expects(:eval_generate).with(resource).returns true graph.traverse {} graph.blockers.should be_empty end it "should yield the resource even if eval_generate is called" do graph.ready[graph.unguessable_deterministic_key[resource]] = resource @transaction.expects(:eval_generate).with(resource).returns true yielded = false graph.traverse do |res| yielded = true if res == resource end yielded.should == true end it "should prefetch the provider if necessary" do @transaction.expects(:prefetch_if_necessary).with(resource) graph.traverse {} end it "should not clear blockers if resources aren't added" do graph.blockers['foo'] = 3 graph.blockers['bar'] = 4 graph.ready[graph.unguessable_deterministic_key[resource]] = resource @transaction.expects(:eval_generate).with(resource).returns false graph.traverse {} graph.blockers.should == {'foo' => 3, 'bar' => 4, resource => 0} end it "should unblock all dependents of the resource" do dependent = Puppet::Type.type(:notify).new(:name => "hello", :require => resource) dependent2 = Puppet::Type.type(:notify).new(:name => "goodbye", :require => resource) @transaction.catalog.add_resource(dependent, dependent2) # We enqueue them here just so we can check their blockers. This is done # again in traverse. graph.enqueue_roots graph.blockers[dependent].should == 1 graph.blockers[dependent2].should == 1 graph.ready[graph.unguessable_deterministic_key[resource]] = resource graph.traverse {} graph.blockers[dependent].should == 0 graph.blockers[dependent2].should == 0 end it "should enqueue any unblocked dependents" do dependent = Puppet::Type.type(:notify).new(:name => "hello", :require => resource) dependent2 = Puppet::Type.type(:notify).new(:name => "goodbye", :require => resource) @transaction.catalog.add_resource(dependent, dependent2) graph.enqueue_roots graph.blockers[dependent].should == 1 graph.blockers[dependent2].should == 1 graph.ready[graph.unguessable_deterministic_key[resource]] = resource seen = [] graph.traverse do |res| seen << res end seen.should =~ [resource, dependent, dependent2] end it "should mark the resource done" do graph.ready[graph.unguessable_deterministic_key[resource]] = resource graph.traverse {} graph.done[resource].should == true end it "should not evaluate the resource if it's not suitable" do resource.stubs(:suitable?).returns false graph.traverse do |resource| raise "evaluated a resource" end end it "should defer an unsuitable resource unless it can't go on" do other = Puppet::Type.type(:notify).new(:name => "hello") @transaction.catalog.add_resource(other) # Show that we check once, then get the resource back and check again resource.expects(:suitable?).twice.returns(false).then.returns(true) graph.traverse {} end it "should fail unsuitable resources and go on if it gets blocked" do dependent = Puppet::Type.type(:notify).new(:name => "hello", :require => resource) @transaction.catalog.add_resource(dependent) resource.stubs(:suitable?).returns false evaluated = [] graph.traverse do |res| evaluated << res end # We should have gone on to evaluate the children evaluated.should == [dependent] @transaction.resource_status(resource).should be_failed end end describe "when generating resources before traversal" do let(:catalog) { Puppet::Resource::Catalog.new } let(:transaction) { Puppet::Transaction.new(catalog) } let(:generator) { Puppet::Type.type(:notify).create :title => "generator" } let(:generated) do %w[a b c].map { |name| Puppet::Type.type(:notify).new(:name => name) } end before :each do catalog.add_resource generator generator.stubs(:generate).returns generated end it "should call 'generate' on all created resources" do generated.each { |res| res.expects(:generate) } transaction.add_dynamically_generated_resources end it "should finish all resources" do generated.each { |res| res.expects(:finish) } transaction.add_dynamically_generated_resources end it "should skip generated resources that conflict with existing resources" do duplicate = generated.first catalog.add_resource(duplicate) duplicate.expects(:finish).never duplicate.expects(:info).with { |msg| msg =~ /Duplicate generated resource/ } transaction.add_dynamically_generated_resources end it "should copy all tags to the newly generated resources" do generator.tag('one', 'two') transaction.add_dynamically_generated_resources generated.each do |res| res.should be_tagged(generator.tags) end end end describe "when skipping a resource" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should skip resource with missing tags" do @transaction.stubs(:missing_tags?).returns(true) @transaction.should be_skip(@resource) end it "should skip unscheduled resources" do @transaction.stubs(:scheduled?).returns(false) @transaction.should be_skip(@resource) end it "should skip resources with failed dependencies" do @transaction.stubs(:failed_dependencies?).returns(true) @transaction.should be_skip(@resource) end it "should skip virtual resource" do @resource.stubs(:virtual?).returns true @transaction.should be_skip(@resource) end it "should skip device only resouce on normal host" do @resource.stubs(:appliable_to_device?).returns true @transaction.for_network_device = false @transaction.should be_skip(@resource) end it "should not skip device only resouce on remote device" do @resource.stubs(:appliable_to_device?).returns true @transaction.for_network_device = true @transaction.should_not be_skip(@resource) end it "should skip host resouce on device" do @resource.stubs(:appliable_to_device?).returns false @transaction.for_network_device = true @transaction.should be_skip(@resource) end end describe "when determining if tags are missing" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) @transaction.stubs(:ignore_tags?).returns false end it "should not be missing tags if tags are being ignored" do @transaction.expects(:ignore_tags?).returns true @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should not be missing tags if the transaction tags are empty" do @transaction.tags = [] @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should otherwise let the resource determine if it is missing tags" do tags = ['one', 'two'] @transaction.tags = tags @resource.expects(:tagged?).with(*tags).returns(false) @transaction.should be_missing_tags(@resource) end end describe "when determining if a resource should be scheduled" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should always schedule resources if 'ignoreschedules' is set" do @transaction.ignoreschedules = true @transaction.resource_harness.expects(:scheduled?).never @transaction.should be_scheduled(@resource) end it "should let the resource harness determine whether the resource should be scheduled" do @transaction.resource_harness.expects(:scheduled?).with(@transaction.resource_status(@resource), @resource).returns "feh" @transaction.scheduled?(@resource).should == "feh" end end describe "when prefetching" do let(:catalog) { Puppet::Resource::Catalog.new } let(:transaction) { Puppet::Transaction.new(catalog) } let(:resource) { Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh", :provider => :parsed } let(:resource2) { Puppet::Type.type(:package).create :title => "blah", :provider => "apt" } before :each do catalog.add_resource resource catalog.add_resource resource2 end describe "#resources_by_provider" do it "should fetch resources by their type and provider" do transaction.resources_by_provider(:sshkey, :parsed).should == { resource.name => resource, } transaction.resources_by_provider(:package, :apt).should == { resource2.name => resource2, } end it "should omit resources whose types don't use providers" do # faking the sshkey type not to have a provider resource.class.stubs(:attrclass).returns nil transaction.resources_by_provider(:sshkey, :parsed).should == {} end it "should return empty hash for providers with no resources" do transaction.resources_by_provider(:package, :yum).should == {} end end it "should match resources by name, not title" do resource.provider.class.expects(:prefetch).with("bar" => resource) transaction.prefetch_if_necessary(resource) end it "should not prefetch a provider which has already been prefetched" do transaction.prefetched_providers[:sshkey][:parsed] = true resource.provider.class.expects(:prefetch).never transaction.prefetch_if_necessary(resource) end it "should mark the provider prefetched" do resource.provider.class.stubs(:prefetch) transaction.prefetch_if_necessary(resource) transaction.prefetched_providers[:sshkey][:parsed].should be_true end it "should prefetch resources without a provider if prefetching the default provider" do other = Puppet::Type.type(:sshkey).create :name => "other" other.instance_variable_set(:@provider, nil) catalog.add_resource other resource.provider.class.expects(:prefetch).with('bar' => resource, 'other' => other) transaction.prefetch_if_necessary(resource) end end it "should return all resources for which the resource status indicates the resource has changed when determinig changed resources" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) names = [] 2.times do |i| name = File.join(@basepath, "file#{i}") resource = Puppet::Type.type(:file).new :path => name names << resource.to_s @catalog.add_resource resource @transaction.add_resource_status Puppet::Resource::Status.new(resource) end @transaction.resource_status(names[0]).changed = true @transaction.changed?.should == [@catalog.resource(names[0])] end describe 'when checking application run state' do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should return true for :stop_processing? if Puppet::Application.stop_requested? is true' do Puppet::Application.stubs(:stop_requested?).returns(true) @transaction.stop_processing?.should be_true end it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do Puppet::Application.stubs(:stop_requested?).returns(false) @transaction.stop_processing?.should be_false end describe 'within an evaluate call' do before do @resource = Puppet::Type.type(:notify).new :title => "foobar" @catalog.add_resource @resource @transaction.stubs(:add_dynamically_generated_resources) end it 'should stop processing if :stop_processing? is true' do @transaction.stubs(:stop_processing?).returns(true) @transaction.expects(:eval_resource).never @transaction.evaluate end it 'should continue processing if :stop_processing? is false' do @transaction.stubs(:stop_processing?).returns(false) @transaction.expects(:eval_resource).returns(nil) @transaction.evaluate end end end end describe Puppet::Transaction, " when determining tags" do before do @config = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@config) end it "should default to the tags specified in the :tags setting" do Puppet.expects(:[]).with(:tags).returns("one") @transaction.tags.should == %w{one} end it "should split tags based on ','" do Puppet.expects(:[]).with(:tags).returns("one,two") @transaction.tags.should == %w{one two} end it "should use any tags set after creation" do Puppet.expects(:[]).with(:tags).never @transaction.tags = %w{one two} @transaction.tags.should == %w{one two} end it "should always convert assigned tags to an array" do @transaction.tags = "one::two" @transaction.tags.should == %w{one::two} end it "should accept a comma-delimited string" do @transaction.tags = "one, two" @transaction.tags.should == %w{one two} end it "should accept an empty string" do @transaction.tags = "" @transaction.tags.should == [] end end diff --git a/spec/unit/type/augeas_spec.rb b/spec/unit/type/augeas_spec.rb index c8dc207f9..3e29f1cbf 100755 --- a/spec/unit/type/augeas_spec.rb +++ b/spec/unit/type/augeas_spec.rb @@ -1,119 +1,119 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' augeas = Puppet::Type.type(:augeas) describe augeas do describe "when augeas is present", :if => Puppet.features.augeas? do it "should have a default provider inheriting from Puppet::Provider" do augeas.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should have a valid provider" do augeas.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) end end describe "basic structure" do it "should be able to create a instance" do provider_class = Puppet::Type::Augeas.provider(Puppet::Type::Augeas.providers[0]) Puppet::Type::Augeas.expects(:defaultprovider).returns provider_class augeas.new(:name => "bar").should_not be_nil end it "should have an parse_commands feature" do augeas.provider_feature(:parse_commands).should_not be_nil end it "should have an need_to_run? feature" do augeas.provider_feature(:need_to_run?).should_not be_nil end it "should have an execute_changes feature" do augeas.provider_feature(:execute_changes).should_not be_nil end properties = [:returns] params = [:name, :context, :onlyif, :changes, :root, :load_path, :type_check] properties.each do |property| it "should have a #{property} property" do augeas.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do augeas.attrclass(property).doc.should be_instance_of(String) end end params.each do |param| it "should have a #{param} parameter" do augeas.attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} parameter" do augeas.attrclass(param).doc.should be_instance_of(String) end end end describe "default values" do before do provider_class = augeas.provider(augeas.providers[0]) augeas.expects(:defaultprovider).returns provider_class end it "should be blank for context" do augeas.new(:name => :context)[:context].should == "" end it "should be blank for onlyif" do augeas.new(:name => :onlyif)[:onlyif].should == "" end it "should be blank for load_path" do augeas.new(:name => :load_path)[:load_path].should == "" end it "should be / for root" do augeas.new(:name => :root)[:root].should == "/" end it "should be false for type_check" do augeas.new(:name => :type_check)[:type_check].should == :false end end describe "provider interaction" do it "should return 0 if it does not need to run" do provider = stub("provider", :need_to_run? => false) resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) changes = augeas.attrclass(:returns).new(:resource => resource) changes.retrieve.should == 0 end it "should return :need_to_run if it needs to run" do provider = stub("provider", :need_to_run? => true) resource = stub('resource', :resource => nil, :provider => provider, :line => nil, :file => nil) changes = augeas.attrclass(:returns).new(:resource => resource) changes.retrieve.should == :need_to_run end end describe "loading specific files" do it "should require lens when incl is used" do lambda { augeas.new(:name => :no_lens, :incl => "/etc/hosts")}.should raise_error(Puppet::Error) end it "should require incl when lens is used" do lambda { augeas.new(:name => :no_incl, :lens => "Hosts.lns") }.should raise_error(Puppet::Error) end it "should set the context when a specific file is used" do fake_provider = stub_everything "fake_provider" augeas.stubs(:defaultprovider).returns fake_provider augeas.new(:name => :no_incl, :lens => "Hosts.lns", :incl => "/etc/hosts")[:context].should == "/files/etc/hosts" end end end diff --git a/spec/unit/type/component_spec.rb b/spec/unit/type/component_spec.rb index 9d6d71926..682d18b44 100755 --- a/spec/unit/type/component_spec.rb +++ b/spec/unit/type/component_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' component = Puppet::Type.type(:component) describe component do it "should have a :name attribute" do component.attrclass(:name).should_not be_nil end it "should use Class as its type when a normal string is provided as the title" do component.new(:name => "bar").ref.should == "Class[Bar]" end it "should always produce a resource reference string as its title" do component.new(:name => "bar").title.should == "Class[Bar]" end it "should have a reference string equivalent to its title" do comp = component.new(:name => "Foo[bar]") comp.title.should == comp.ref end it "should alias itself to its reference if it has a catalog and the catalog does not already have a resource with the same reference" do catalog = mock 'catalog' catalog.expects(:resource).with("Foo[bar]").returns nil catalog.expects(:alias).with { |resource, name| resource.is_a?(component) and name == "Foo[bar]" } component.new(:name => "Foo[bar]", :catalog => catalog) end it "should not fail when provided an invalid value" do comp = component.new(:name => "Foo[bar]") lambda { comp[:yayness] = "ey" }.should_not raise_error end it "should return previously provided invalid values" do comp = component.new(:name => "Foo[bar]") comp[:yayness] = "eh" comp[:yayness].should == "eh" end it "should correctly support metaparameters" do comp = component.new(:name => "Foo[bar]", :require => "Foo[bar]") comp.parameter(:require).should be_instance_of(component.attrclass(:require)) end describe "when building up the path" do it "should produce the class name if the component models a class" do component.new(:name => "Class[foo]").pathbuilder.must == ["Foo"] end it "should produce an empty string if the component models the 'main' class" do component.new(:name => "Class[main]").pathbuilder.must == [""] end it "should produce a resource reference if the component does not model a class" do component.new(:name => "Foo[bar]").pathbuilder.must == ["Foo[bar]"] end end end diff --git a/spec/unit/type/computer_spec.rb b/spec/unit/type/computer_spec.rb index 1e15d7a4f..0afff705d 100755 --- a/spec/unit/type/computer_spec.rb +++ b/spec/unit/type/computer_spec.rb @@ -1,80 +1,80 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' computer = Puppet::Type.type(:computer) describe Puppet::Type.type(:computer), " when checking computer objects" do before do provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) Puppet::Type::Computer.expects(:defaultprovider).returns provider_class @resource = Puppet::Type::Computer.new( :name => "puppetcomputertest", :en_address => "aa:bb:cc:dd:ee:ff", :ip_address => "1.2.3.4") @properties = {} @ensure = Puppet::Type::Computer.attrclass(:ensure).new(:resource => @resource) end it "should be able to create a instance" do provider_class = Puppet::Type::Computer.provider(Puppet::Type::Computer.providers[0]) Puppet::Type::Computer.expects(:defaultprovider).returns provider_class computer.new(:name => "bar").should_not be_nil end properties = [:en_address, :ip_address] params = [:name] properties.each do |property| it "should have a #{property} property" do computer.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do computer.attrclass(property).doc.should be_instance_of(String) end it "should accept :absent as a value" do prop = computer.attrclass(property).new(:resource => @resource) prop.should = :absent prop.should.must == :absent end end params.each do |param| it "should have a #{param} parameter" do computer.attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} parameter" do computer.attrclass(param).doc.should be_instance_of(String) end end describe "default values" do before do provider_class = computer.provider(computer.providers[0]) computer.expects(:defaultprovider).returns provider_class end it "should be nil for en_address" do computer.new(:name => :en_address)[:en_address].should == nil end it "should be nil for ip_address" do computer.new(:name => :ip_address)[:ip_address].should == nil end end describe "when managing the ensure property" do it "should support a :present value" do lambda { @ensure.should = :present }.should_not raise_error end it "should support an :absent value" do lambda { @ensure.should = :absent }.should_not raise_error end end end diff --git a/spec/unit/type/cron_spec.rb b/spec/unit/type/cron_spec.rb index 7adafff48..249526213 100755 --- a/spec/unit/type/cron_spec.rb +++ b/spec/unit/type/cron_spec.rb @@ -1,497 +1,497 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:cron), :unless => Puppet.features.microsoft_windows? do before do @provider_class = described_class.provide(:simple) { mk_resource_methods } @provider_class.stubs(:suitable?).returns true described_class.stubs(:defaultprovider).returns @provider_class end it "should have :name be its namevar" do described_class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider].each do |param| it "should have a #{param} parameter" do described_class.attrtype(param).should == :param end end [:command, :special, :minute, :hour, :weekday, :month, :monthday, :environment, :user, :target].each do |property| it "should have a #{property} property" do described_class.attrtype(property).should == :property end end [:command, :minute, :hour, :weekday, :month, :monthday].each do |cronparam| it "should have #{cronparam} of type CronParam" do described_class.attrclass(cronparam).ancestors.should include CronParam end end end describe "when validating values" do describe "ensure" do it "should support present as a value for ensure" do proc { described_class.new(:name => 'foo', :ensure => :present) }.should_not raise_error end it "should support absent as a value for ensure" do proc { described_class.new(:name => 'foo', :ensure => :present) }.should_not raise_error end it "should not support other values" do proc { described_class.new(:name => 'foo', :ensure => :foo) }.should raise_error(Puppet::Error, /Invalid value/) end end describe "minute" do it "should support absent" do proc { described_class.new(:name => 'foo', :minute => 'absent') }.should_not raise_error end it "should support *" do proc { described_class.new(:name => 'foo', :minute => '*') }.should_not raise_error end it "should translate absent to :absent" do described_class.new(:name => 'foo', :minute => 'absent')[:minute].should == :absent end it "should translate * to :absent" do described_class.new(:name => 'foo', :minute => '*')[:minute].should == :absent end it "should support valid single values" do proc { described_class.new(:name => 'foo', :minute => '0') }.should_not raise_error proc { described_class.new(:name => 'foo', :minute => '1') }.should_not raise_error proc { described_class.new(:name => 'foo', :minute => '59') }.should_not raise_error end it "should not support non numeric characters" do proc { described_class.new(:name => 'foo', :minute => 'z59') }.should raise_error(Puppet::Error, /z59 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => '5z9') }.should raise_error(Puppet::Error, /5z9 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => '59z') }.should raise_error(Puppet::Error, /59z is not a valid minute/) end it "should not support single values out of range" do proc { described_class.new(:name => 'foo', :minute => '-1') }.should raise_error(Puppet::Error, /-1 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => '60') }.should raise_error(Puppet::Error, /60 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => '61') }.should raise_error(Puppet::Error, /61 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => '120') }.should raise_error(Puppet::Error, /120 is not a valid minute/) end it "should support valid multiple values" do proc { described_class.new(:name => 'foo', :minute => ['0','1','59'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :minute => ['40','30','20'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :minute => ['10','30','20'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { described_class.new(:name => 'foo', :minute => ['0','1','60'] ) }.should raise_error(Puppet::Error, /60 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => ['0','120','59'] ) }.should raise_error(Puppet::Error, /120 is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => ['-1','1','59'] ) }.should raise_error(Puppet::Error, /-1 is not a valid minute/) # two invalid proc { described_class.new(:name => 'foo', :minute => ['0','61','62'] ) }.should raise_error(Puppet::Error, /(61|62) is not a valid minute/) # all invalid proc { described_class.new(:name => 'foo', :minute => ['-1','61','62'] ) }.should raise_error(Puppet::Error, /(-1|61|62) is not a valid minute/) end it "should support valid step syntax" do proc { described_class.new(:name => 'foo', :minute => '*/2' ) }.should_not raise_error proc { described_class.new(:name => 'foo', :minute => '10-16/2' ) }.should_not raise_error end it "should not support invalid steps" do proc { described_class.new(:name => 'foo', :minute => '*/A' ) }.should raise_error(Puppet::Error, /\*\/A is not a valid minute/) proc { described_class.new(:name => 'foo', :minute => '*/2A' ) }.should raise_error(Puppet::Error, /\*\/2A is not a valid minute/) # As it turns out cron does not complaining about steps that exceed the valid range # proc { described_class.new(:name => 'foo', :minute => '*/120' ) }.should raise_error(Puppet::Error, /is not a valid minute/) end end describe "hour" do it "should support absent" do proc { described_class.new(:name => 'foo', :hour => 'absent') }.should_not raise_error end it "should support *" do proc { described_class.new(:name => 'foo', :hour => '*') }.should_not raise_error end it "should translate absent to :absent" do described_class.new(:name => 'foo', :hour => 'absent')[:hour].should == :absent end it "should translate * to :absent" do described_class.new(:name => 'foo', :hour => '*')[:hour].should == :absent end it "should support valid single values" do proc { described_class.new(:name => 'foo', :hour => '0') }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => '11') }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => '12') }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => '13') }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => '23') }.should_not raise_error end it "should not support non numeric characters" do proc { described_class.new(:name => 'foo', :hour => 'z15') }.should raise_error(Puppet::Error, /z15 is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => '1z5') }.should raise_error(Puppet::Error, /1z5 is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => '15z') }.should raise_error(Puppet::Error, /15z is not a valid hour/) end it "should not support single values out of range" do proc { described_class.new(:name => 'foo', :hour => '-1') }.should raise_error(Puppet::Error, /-1 is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => '24') }.should raise_error(Puppet::Error, /24 is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => '120') }.should raise_error(Puppet::Error, /120 is not a valid hour/) end it "should support valid multiple values" do proc { described_class.new(:name => 'foo', :hour => ['0','1','23'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => ['5','16','14'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => ['16','13','9'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { described_class.new(:name => 'foo', :hour => ['0','1','24'] ) }.should raise_error(Puppet::Error, /24 is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => ['0','-1','5'] ) }.should raise_error(Puppet::Error, /-1 is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => ['-1','1','23'] ) }.should raise_error(Puppet::Error, /-1 is not a valid hour/) # two invalid proc { described_class.new(:name => 'foo', :hour => ['0','25','26'] ) }.should raise_error(Puppet::Error, /(25|26) is not a valid hour/) # all invalid proc { described_class.new(:name => 'foo', :hour => ['-1','24','120'] ) }.should raise_error(Puppet::Error, /(-1|24|120) is not a valid hour/) end it "should support valid step syntax" do proc { described_class.new(:name => 'foo', :hour => '*/2' ) }.should_not raise_error proc { described_class.new(:name => 'foo', :hour => '10-18/4' ) }.should_not raise_error end it "should not support invalid steps" do proc { described_class.new(:name => 'foo', :hour => '*/A' ) }.should raise_error(Puppet::Error, /\*\/A is not a valid hour/) proc { described_class.new(:name => 'foo', :hour => '*/2A' ) }.should raise_error(Puppet::Error, /\*\/2A is not a valid hour/) # As it turns out cron does not complaining about steps that exceed the valid range # proc { described_class.new(:name => 'foo', :hour => '*/26' ) }.should raise_error(Puppet::Error, /is not a valid hour/) end end describe "weekday" do it "should support absent" do proc { described_class.new(:name => 'foo', :weekday => 'absent') }.should_not raise_error end it "should support *" do proc { described_class.new(:name => 'foo', :weekday => '*') }.should_not raise_error end it "should translate absent to :absent" do described_class.new(:name => 'foo', :weekday => 'absent')[:weekday].should == :absent end it "should translate * to :absent" do described_class.new(:name => 'foo', :weekday => '*')[:weekday].should == :absent end it "should support valid numeric weekdays" do proc { described_class.new(:name => 'foo', :weekday => '0') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => '1') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => '6') }.should_not raise_error # According to http://www.manpagez.com/man/5/crontab 7 is also valid (Sunday) proc { described_class.new(:name => 'foo', :weekday => '7') }.should_not raise_error end it "should support valid weekdays as words (long version)" do proc { described_class.new(:name => 'foo', :weekday => 'Monday') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Tuesday') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Wednesday') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Thursday') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Friday') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Saturday') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Sunday') }.should_not raise_error end it "should support valid weekdays as words (3 character version)" do proc { described_class.new(:name => 'foo', :weekday => 'Mon') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Tue') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Wed') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Thu') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Fri') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Sat') }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => 'Sun') }.should_not raise_error end it "should not support numeric values out of range" do proc { described_class.new(:name => 'foo', :weekday => '-1') }.should raise_error(Puppet::Error, /-1 is not a valid weekday/) proc { described_class.new(:name => 'foo', :weekday => '8') }.should raise_error(Puppet::Error, /8 is not a valid weekday/) end it "should not support invalid weekday names" do proc { described_class.new(:name => 'foo', :weekday => 'Sar') }.should raise_error(Puppet::Error, /Sar is not a valid weekday/) end it "should support valid multiple values" do proc { described_class.new(:name => 'foo', :weekday => ['0','1','6'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => ['Mon','Wed','Friday'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { described_class.new(:name => 'foo', :weekday => ['0','1','8'] ) }.should raise_error(Puppet::Error, /8 is not a valid weekday/) proc { described_class.new(:name => 'foo', :weekday => ['Mon','Fii','Sat'] ) }.should raise_error(Puppet::Error, /Fii is not a valid weekday/) # two invalid proc { described_class.new(:name => 'foo', :weekday => ['Mos','Fii','Sat'] ) }.should raise_error(Puppet::Error, /(Mos|Fii) is not a valid weekday/) # all invalid proc { described_class.new(:name => 'foo', :weekday => ['Mos','Fii','Saa'] ) }.should raise_error(Puppet::Error, /(Mos|Fii|Saa) is not a valid weekday/) proc { described_class.new(:name => 'foo', :weekday => ['-1','8','11'] ) }.should raise_error(Puppet::Error, /(-1|8|11) is not a valid weekday/) end it "should support valid step syntax" do proc { described_class.new(:name => 'foo', :weekday => '*/2' ) }.should_not raise_error proc { described_class.new(:name => 'foo', :weekday => '0-4/2' ) }.should_not raise_error end it "should not support invalid steps" do proc { described_class.new(:name => 'foo', :weekday => '*/A' ) }.should raise_error(Puppet::Error, /\*\/A is not a valid weekday/) proc { described_class.new(:name => 'foo', :weekday => '*/2A' ) }.should raise_error(Puppet::Error, /\*\/2A is not a valid weekday/) # As it turns out cron does not complaining about steps that exceed the valid range # proc { described_class.new(:name => 'foo', :weekday => '*/9' ) }.should raise_error(Puppet::Error, /is not a valid weekday/) end end describe "month" do it "should support absent" do proc { described_class.new(:name => 'foo', :month => 'absent') }.should_not raise_error end it "should support *" do proc { described_class.new(:name => 'foo', :month => '*') }.should_not raise_error end it "should translate absent to :absent" do described_class.new(:name => 'foo', :month => 'absent')[:month].should == :absent end it "should translate * to :absent" do described_class.new(:name => 'foo', :month => '*')[:month].should == :absent end it "should support valid numeric values" do proc { described_class.new(:name => 'foo', :month => '1') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => '12') }.should_not raise_error end it "should support valid months as words" do proc { described_class.new(:name => 'foo', :month => 'January') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'February') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'March') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'April') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'May') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'June') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'July') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'August') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'September') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'October') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'November') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'December') }.should_not raise_error end it "should support valid months as words (3 character short version)" do proc { described_class.new(:name => 'foo', :month => 'Jan') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Feb') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Mar') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Apr') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'May') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Jun') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Jul') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Aug') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Sep') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Oct') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Nov') }.should_not raise_error proc { described_class.new(:name => 'foo', :month => 'Dec') }.should_not raise_error end it "should not support numeric values out of range" do proc { described_class.new(:name => 'foo', :month => '-1') }.should raise_error(Puppet::Error, /-1 is not a valid month/) proc { described_class.new(:name => 'foo', :month => '0') }.should raise_error(Puppet::Error, /0 is not a valid month/) proc { described_class.new(:name => 'foo', :month => '13') }.should raise_error(Puppet::Error, /13 is not a valid month/) end it "should not support words that are not valid months" do proc { described_class.new(:name => 'foo', :month => 'Jal') }.should raise_error(Puppet::Error, /Jal is not a valid month/) end it "should not support single values out of range" do proc { described_class.new(:name => 'foo', :month => '-1') }.should raise_error(Puppet::Error, /-1 is not a valid month/) proc { described_class.new(:name => 'foo', :month => '60') }.should raise_error(Puppet::Error, /60 is not a valid month/) proc { described_class.new(:name => 'foo', :month => '61') }.should raise_error(Puppet::Error, /61 is not a valid month/) proc { described_class.new(:name => 'foo', :month => '120') }.should raise_error(Puppet::Error, /120 is not a valid month/) end it "should support valid multiple values" do proc { described_class.new(:name => 'foo', :month => ['1','9','12'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :month => ['Jan','March','Jul'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { described_class.new(:name => 'foo', :month => ['0','1','12'] ) }.should raise_error(Puppet::Error, /0 is not a valid month/) proc { described_class.new(:name => 'foo', :month => ['1','13','10'] ) }.should raise_error(Puppet::Error, /13 is not a valid month/) proc { described_class.new(:name => 'foo', :month => ['Jan','Feb','Jxx'] ) }.should raise_error(Puppet::Error, /Jxx is not a valid month/) # two invalid proc { described_class.new(:name => 'foo', :month => ['Jan','Fex','Jux'] ) }.should raise_error(Puppet::Error, /(Fex|Jux) is not a valid month/) # all invalid proc { described_class.new(:name => 'foo', :month => ['-1','0','13'] ) }.should raise_error(Puppet::Error, /(-1|0|13) is not a valid month/) proc { described_class.new(:name => 'foo', :month => ['Jax','Fex','Aux'] ) }.should raise_error(Puppet::Error, /(Jax|Fex|Aux) is not a valid month/) end it "should support valid step syntax" do proc { described_class.new(:name => 'foo', :month => '*/2' ) }.should_not raise_error proc { described_class.new(:name => 'foo', :month => '1-12/3' ) }.should_not raise_error end it "should not support invalid steps" do proc { described_class.new(:name => 'foo', :month => '*/A' ) }.should raise_error(Puppet::Error, /\*\/A is not a valid month/) proc { described_class.new(:name => 'foo', :month => '*/2A' ) }.should raise_error(Puppet::Error, /\*\/2A is not a valid month/) # As it turns out cron does not complaining about steps that exceed the valid range # proc { described_class.new(:name => 'foo', :month => '*/13' ) }.should raise_error(Puppet::Error, /is not a valid month/) end end describe "monthday" do it "should support absent" do proc { described_class.new(:name => 'foo', :monthday => 'absent') }.should_not raise_error end it "should support *" do proc { described_class.new(:name => 'foo', :monthday => '*') }.should_not raise_error end it "should translate absent to :absent" do described_class.new(:name => 'foo', :monthday => 'absent')[:monthday].should == :absent end it "should translate * to :absent" do described_class.new(:name => 'foo', :monthday => '*')[:monthday].should == :absent end it "should support valid single values" do proc { described_class.new(:name => 'foo', :monthday => '1') }.should_not raise_error proc { described_class.new(:name => 'foo', :monthday => '30') }.should_not raise_error proc { described_class.new(:name => 'foo', :monthday => '31') }.should_not raise_error end it "should not support non numeric characters" do proc { described_class.new(:name => 'foo', :monthday => 'z23') }.should raise_error(Puppet::Error, /z23 is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => '2z3') }.should raise_error(Puppet::Error, /2z3 is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => '23z') }.should raise_error(Puppet::Error, /23z is not a valid monthday/) end it "should not support single values out of range" do proc { described_class.new(:name => 'foo', :monthday => '-1') }.should raise_error(Puppet::Error, /-1 is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => '0') }.should raise_error(Puppet::Error, /0 is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => '32') }.should raise_error(Puppet::Error, /32 is not a valid monthday/) end it "should support valid multiple values" do proc { described_class.new(:name => 'foo', :monthday => ['1','23','31'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :monthday => ['31','23','1'] ) }.should_not raise_error proc { described_class.new(:name => 'foo', :monthday => ['1','31','23'] ) }.should_not raise_error end it "should not support multiple values if at least one is invalid" do # one invalid proc { described_class.new(:name => 'foo', :monthday => ['1','23','32'] ) }.should raise_error(Puppet::Error, /32 is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => ['-1','12','23'] ) }.should raise_error(Puppet::Error, /-1 is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => ['13','32','30'] ) }.should raise_error(Puppet::Error, /32 is not a valid monthday/) # two invalid proc { described_class.new(:name => 'foo', :monthday => ['-1','0','23'] ) }.should raise_error(Puppet::Error, /(-1|0) is not a valid monthday/) # all invalid proc { described_class.new(:name => 'foo', :monthday => ['-1','0','32'] ) }.should raise_error(Puppet::Error, /(-1|0|32) is not a valid monthday/) end it "should support valid step syntax" do proc { described_class.new(:name => 'foo', :monthday => '*/2' ) }.should_not raise_error proc { described_class.new(:name => 'foo', :monthday => '10-16/2' ) }.should_not raise_error end it "should not support invalid steps" do proc { described_class.new(:name => 'foo', :monthday => '*/A' ) }.should raise_error(Puppet::Error, /\*\/A is not a valid monthday/) proc { described_class.new(:name => 'foo', :monthday => '*/2A' ) }.should raise_error(Puppet::Error, /\*\/2A is not a valid monthday/) # As it turns out cron does not complaining about steps that exceed the valid range # proc { described_class.new(:name => 'foo', :monthday => '*/32' ) }.should raise_error(Puppet::Error, /is not a valid monthday/) end end describe "environment" do it "it should accept an :environment that looks like a path" do lambda do described_class.new(:name => 'foo',:environment => 'PATH=/bin:/usr/bin:/usr/sbin') end.should_not raise_error end it "should not accept environment variables that do not contain '='" do lambda do described_class.new(:name => 'foo',:environment => 'INVALID') end.should raise_error(Puppet::Error, /Invalid environment setting "INVALID"/) end it "should accept empty environment variables that do not contain '='" do lambda do described_class.new(:name => 'foo',:environment => 'MAILTO=') end.should_not raise_error(Puppet::Error) end it "should accept 'absent'" do lambda do described_class.new(:name => 'foo',:environment => 'absent') end.should_not raise_error(Puppet::Error) end end end describe "when autorequiring resources" do before :each do @user_bob = Puppet::Type.type(:user).new(:name => 'bob', :ensure => :present) @user_alice = Puppet::Type.type(:user).new(:name => 'alice', :ensure => :present) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @user_bob, @user_alice end it "should autorequire the user" do @resource = described_class.new(:name => 'dummy', :command => '/usr/bin/uptime', :user => 'alice') @catalog.add_resource @resource req = @resource.autorequire req.size.should == 1 req[0].target.must == @resource req[0].source.must == @user_alice end end it "should require a command when adding an entry" do entry = described_class.new(:name => "test_entry", :ensure => :present) expect { entry.value(:command) }.should raise_error(Puppet::Error, /No command/) end it "should not require a command when removing an entry" do entry = described_class.new(:name => "test_entry", :ensure => :absent) entry.value(:command).should == nil end it "should default to user => root if Etc.getpwuid(Process.uid) returns nil (#12357)" do Etc.expects(:getpwuid).returns(nil) entry = described_class.new(:name => "test_entry", :ensure => :present) entry.value(:user).should eql "root" end end diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb index 535d0d5a2..efe90ed49 100755 --- a/spec/unit/type/exec_spec.rb +++ b/spec/unit/type/exec_spec.rb @@ -1,740 +1,740 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:exec) do include PuppetSpec::Files def exec_tester(command, exitstatus = 0, rest = {}) Puppet.features.stubs(:root?).returns(true) output = rest.delete(:output) || '' tries = rest[:tries] || 1 args = { :name => command, :path => @example_path, :logoutput => false, :loglevel => :err, :returns => 0 }.merge(rest) exec = Puppet::Type.type(:exec).new(args) status = stub "process", :exitstatus => exitstatus Puppet::Util::SUIDManager.expects(:run_and_capture).times(tries). with() { |*args| args[0] == command && args[1] == nil && args[2] == nil && args[3][:override_locale] == false && args[3].has_key?(:custom_environment) } .returns([output, status]) return exec end before do @command = make_absolute('/bin/true whatever') @executable = make_absolute('/bin/true') @bogus_cmd = make_absolute('/bogus/cmd') end describe "when not stubbing the provider" do before do path = tmpdir('path') true_cmd = File.join(path, 'true') false_cmd = File.join(path, 'false') FileUtils.touch(true_cmd) FileUtils.touch(false_cmd) File.chmod(0755, true_cmd) File.chmod(0755, false_cmd) @example_path = [path] end it "should return :executed_command as its event" do resource = Puppet::Type.type(:exec).new :command => @command resource.parameter(:returns).event.name.should == :executed_command end describe "when execing" do it "should use the 'run_and_capture' method to exec" do exec_tester("true").refresh.should == :executed_command end it "should report a failure" do proc { exec_tester('false', 1).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should not report a failure if the exit status is specified in a returns array" do proc { exec_tester("false", 1, :returns => [0, 1]).refresh }.should_not raise_error end it "should report a failure if the exit status is not specified in a returns array" do proc { exec_tester('false', 1, :returns => [0, 100]).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should log the output on success" do output = "output1\noutput2\n" exec_tester('false', 0, :output => output, :logoutput => true).refresh output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :logoutput => true).refresh }. should raise_error(Puppet::Error) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end end describe "when logoutput=>on_failure is set" do it "should log the output on failure" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure when returns is specified as an array" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :returns => [0, 100], :logoutput => :on_failure).refresh }.should raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "shouldn't log the output on success" do exec_tester('true', 0, :output => "a\nb\nc\n", :logoutput => :on_failure).refresh @logs.should == [] end end it "shouldn't log the output on success when non-zero exit status is in a returns array" do exec_tester("true", 100, :output => "a\n", :logoutput => :on_failure, :returns => [1, 100]).refresh @logs.should == [] end describe " when multiple tries are set," do it "should repeat the command attempt 'tries' times on failure and produce an error" do tries = 5 resource = exec_tester("false", 1, :tries => tries, :try_sleep => 0) proc { resource.refresh }.should raise_error(Puppet::Error) end end end it "should be able to autorequire files mentioned in the command" do foo = make_absolute('/bin/foo') catalog = Puppet::Resource::Catalog.new tmp = Puppet::Type.type(:file).new(:name => foo) catalog.add_resource tmp execer = Puppet::Type.type(:exec).new(:name => foo) catalog.add_resource execer catalog.relationship_graph.dependencies(execer).should == [tmp] end describe "when handling the path parameter" do expect = %w{one two three four} { "an array" => expect, "a path-separator delimited list" => expect.join(File::PATH_SEPARATOR), "both array and path-separator delimited lists" => ["one", "two#{File::PATH_SEPARATOR}three", "four"], }.each do |test, input| it "should accept #{test}" do type = Puppet::Type.type(:exec).new(:name => @command, :path => input) type[:path].should == expect end end describe "on platforms where path separator is not :" do before :each do @old_verbosity = $VERBOSE $VERBOSE = nil @old_separator = File::PATH_SEPARATOR File::PATH_SEPARATOR = 'q' end after :each do File::PATH_SEPARATOR = @old_separator $VERBOSE = @old_verbosity end it "should use the path separator of the current platform" do type = Puppet::Type.type(:exec).new(:name => @command, :path => "fooqbarqbaz") type[:path].should == %w[foo bar baz] end end end describe "when setting user" do describe "on POSIX systems", :as_platform => :posix do it "should fail if we are not root" do Puppet.features.stubs(:root?).returns(false) expect { Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => 'input') }. should raise_error Puppet::Error, /Parameter user failed/ end ['one', 2, 'root', 4294967295, 4294967296].each do |value| it "should accept '#{value}' as user if we are root" do Puppet.features.stubs(:root?).returns(true) type = Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => value) type[:user].should == value end end end describe "on Windows systems", :as_platform => :windows do before :each do Puppet.features.stubs(:root?).returns(true) end it "should reject user parameter" do expect { Puppet::Type.type(:exec).new(:name => 'c:\windows\notepad.exe', :user => 'input') }. should raise_error Puppet::Error, /Unable to execute commands as other users on Windows/ end end end describe "when setting group" do shared_examples_for "exec[:group]" do ['one', 2, 'wheel', 4294967295, 4294967296].each do |value| it "should accept '#{value}' without error or judgement" do type = Puppet::Type.type(:exec).new(:name => @command, :group => value) type[:group].should == value end end end describe "when running as root" do before :each do Puppet.features.stubs(:root?).returns(true) end it_behaves_like "exec[:group]" end describe "when not running as root" do before :each do Puppet.features.stubs(:root?).returns(false) end it_behaves_like "exec[:group]" end end describe "when setting cwd" do it_should_behave_like "all path parameters", :cwd, :array => false do def instance(path) # Specify shell provider so we don't have to care about command validation Puppet::Type.type(:exec).new(:name => @executable, :cwd => path, :provider => :shell) end end end shared_examples_for "all exec command parameters" do |param| { "relative" => "example", "absolute" => "/bin/example" }.sort.each do |name, command| describe "if command is #{name}" do before :each do @param = param end def test(command, valid) if @param == :name then instance = Puppet::Type.type(:exec).new() else instance = Puppet::Type.type(:exec).new(:name => @executable) end if valid then instance.provider.expects(:validatecmd).returns(true) else instance.provider.expects(:validatecmd).raises(Puppet::Error, "from a stub") end instance[@param] = command end it "should work if the provider calls the command valid" do expect { test(command, true) }.should_not raise_error end it "should fail if the provider calls the command invalid" do expect { test(command, false) }. should raise_error Puppet::Error, /Parameter #{@param} failed: from a stub/ end end end end shared_examples_for "all exec command parameters that take arrays" do |param| describe "when given an array of inputs" do before :each do @test = Puppet::Type.type(:exec).new(:name => @executable) end it "should accept the array when all commands return valid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(true) @test[param] = input @test[param].should == input end it "should reject the array when any commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).with(input.first).returns(false) input[1..-1].each do |cmd| @test.provider.expects(:validatecmd).with(cmd).returns(true) end @test[param] = input @test[param].should == input end it "should reject the array when all commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(false) @test[param] = input @test[param].should == input end end end describe "when setting refresh" do it_should_behave_like "all exec command parameters", :refresh end describe "for simple parameters" do before :each do @exec = Puppet::Type.type(:exec).new(:name => @executable) end describe "when setting environment" do { "single values" => "foo=bar", "multiple values" => ["foo=bar", "baz=quux"], }.each do |name, data| it "should accept #{name}" do @exec[:environment] = data @exec[:environment].should == data end end { "single values" => "foo", "only values" => ["foo", "bar"], "any values" => ["foo=bar", "baz"] }.each do |name, data| it "should reject #{name} without assignment" do expect { @exec[:environment] = data }. should raise_error Puppet::Error, /Invalid environment setting/ end end end describe "when setting timeout" do [0, 0.1, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:timeout] = valid @exec[:timeout].should == valid end it "should accept '#{valid}' in an array as valid" do @exec[:timeout] = [valid] @exec[:timeout].should == valid end end ['1/2', '', 'foo', '5foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:timeout] = invalid }. should raise_error Puppet::Error, /The timeout must be a number/ end it "should reject '#{invalid}' in an array as invalid" do expect { @exec[:timeout] = [invalid] }. should raise_error Puppet::Error, /The timeout must be a number/ end end it "should fail if timeout is exceeded" do ruby_path = Puppet::Util::Execution.ruby_path() ## Leaving this commented version in here because it fails on windows, due to what appears to be ## an assumption about hash iteration order in lib/puppet/type.rb#hash2resource, where ## resource[]= will overwrite the namevar with ":name" if the iteration is in the wrong order #sleep_exec = Puppet::Type.type(:exec).new(:name => 'exec_spec sleep command', :command => "#{ruby_path} -e 'sleep 0.02'", :timeout => '0.01') sleep_exec = Puppet::Type.type(:exec).new(:name => "#{ruby_path} -e 'sleep 0.02'", :timeout => '0.01') lambda { sleep_exec.refresh }.should raise_error Puppet::Error, "Command exceeded timeout" end it "should convert timeout to a float" do command = make_absolute('/bin/false') resource = Puppet::Type.type(:exec).new :command => command, :timeout => "12" resource[:timeout].should be_a(Float) resource[:timeout].should == 12.0 end it "should munge negative timeouts to 0.0" do command = make_absolute('/bin/false') resource = Puppet::Type.type(:exec).new :command => command, :timeout => "-12.0" resource.parameter(:timeout).value.should be_a(Float) resource.parameter(:timeout).value.should == 0.0 end end describe "when setting tries" do [1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:tries] = valid @exec[:tries].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:tries] = [valid] @exec[:tries].should == valid end end end [-3.5, -1, 0, 0.2, '1/2', '1_000_000', '+12', '', 'foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:tries] = invalid }. should raise_error Puppet::Error, /Tries must be an integer/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:tries] = [invalid] }. should raise_error Puppet::Error, /Tries must be an integer/ end end end end describe "when setting try_sleep" do [0, 0.2, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:try_sleep] = valid @exec[:try_sleep].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:try_sleep] = [valid] @exec[:try_sleep].should == valid end end end { -3.5 => "cannot be a negative number", -1 => "cannot be a negative number", '1/2' => 'must be a number', '1_000_000' => 'must be a number', '+12' => 'must be a number', '' => 'must be a number', 'foo' => 'must be a number', }.each do |invalid, error| it "should reject '#{invalid}' as invalid" do expect { @exec[:try_sleep] = invalid }. should raise_error Puppet::Error, /try_sleep #{error}/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:try_sleep] = [invalid] }. should raise_error Puppet::Error, /try_sleep #{error}/ end end end end describe "when setting refreshonly" do [:true, :false].each do |value| it "should accept '#{value}'" do @exec[:refreshonly] = value @exec[:refreshonly].should == value end end [1, 0, "1", "0", "yes", "y", "no", "n"].each do |value| it "should reject '#{value}'" do expect { @exec[:refreshonly] = value }. should raise_error(Puppet::Error, /Invalid value #{value.inspect}\. Valid values are true, false/ ) end end end end describe "when setting creates" do it_should_behave_like "all path parameters", :creates, :array => true do def instance(path) # Specify shell provider so we don't have to care about command validation Puppet::Type.type(:exec).new(:name => @executable, :creates => path, :provider => :shell) end end end describe "when setting unless" do it_should_behave_like "all exec command parameters", :unless it_should_behave_like "all exec command parameters that take arrays", :unless end describe "when setting onlyif" do it_should_behave_like "all exec command parameters", :onlyif it_should_behave_like "all exec command parameters that take arrays", :onlyif end describe "#check" do before :each do @test = Puppet::Type.type(:exec).new(:name => @executable) end describe ":refreshonly" do { :true => false, :false => true }.each do |input, result| it "should return '#{result}' when given '#{input}'" do @test[:refreshonly] = input @test.check_all_attributes.should == result end end end describe ":creates" do before :each do @exist = tmpfile('exist') FileUtils.touch(@exist) @unexist = tmpfile('unexist') end context "with a single item" do it "should run when the item does not exist" do @test[:creates] = @unexist @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = @exist @test.check_all_attributes.should == false end end context "with an array with one item" do it "should run when the item does not exist" do @test[:creates] = [@unexist] @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = [@exist] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run when all items do not exist" do @test[:creates] = [@unexist] * 3 @test.check_all_attributes.should == true end it "should not run when one item exists" do @test[:creates] = [@unexist, @exist, @unexist] @test.check_all_attributes.should == false end it "should not run when all items exist" do @test[:creates] = [@exist] * 3 end end end { :onlyif => { :pass => false, :fail => true }, :unless => { :pass => true, :fail => false }, }.each do |param, sense| describe ":#{param}" do before :each do @pass = make_absolute("/magic/pass") @fail = make_absolute("/magic/fail") @pass_status = stub('status', :exitstatus => sense[:pass] ? 0 : 1) @fail_status = stub('status', :exitstatus => sense[:fail] ? 0 : 1) @test.provider.stubs(:checkexe).returns(true) [true, false].each do |check| @test.provider.stubs(:run).with(@pass, check). returns(['test output', @pass_status]) @test.provider.stubs(:run).with(@fail, check). returns(['test output', @fail_status]) end end context "with a single item" do it "should run if the command exits non-zero" do @test[param] = @fail @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = @pass @test.check_all_attributes.should == false end end context "with an array with a single item" do it "should run if the command exits non-zero" do @test[param] = [@fail] @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = [@pass] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run if all the commands exits non-zero" do @test[param] = [@fail] * 3 @test.check_all_attributes.should == true end it "should not run if one command exits zero" do @test[param] = [@pass, @fail, @pass] @test.check_all_attributes.should == false end it "should not run if all command exits zero" do @test[param] = [@pass] * 3 @test.check_all_attributes.should == false end end it "should emit output to debug" do Puppet::Util::Log.level = :debug @test[param] = @fail @test.check_all_attributes.should == true @logs.shift.message.should == "test output" end end end end describe "#retrieve" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should return :notrun when check_all_attributes returns true" do @exec_resource.stubs(:check_all_attributes).returns true @exec_resource.retrieve[:returns].should == :notrun end it "should return default exit code 0 when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource.retrieve[:returns].should == ['0'] end it "should return the specified exit code when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource[:returns] = 42 @exec_resource.retrieve[:returns].should == ["42"] end end describe "#output" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should return the provider's run output" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh @exec_resource.output.should == 'silly output' end end describe "#refresh" do before :each do @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should call provider run with the refresh parameter if it is set" do myother_bogus_cmd = make_absolute('/myother/bogus/cmd') provider = stub 'provider' @exec_resource.stubs(:provider).returns(provider) @exec_resource.stubs(:[]).with(:refresh).returns(myother_bogus_cmd) provider.expects(:run).with(myother_bogus_cmd) @exec_resource.refresh end it "should call provider run with the specified command if the refresh parameter is not set" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).with(@bogus_cmd).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end it "should not run the provider if check_all_attributes is false" do @exec_resource.stubs(:check_all_attributes).returns false provider = stub 'provider' provider.expects(:run).never @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end end describe "relative and absolute commands vs path" do let :type do Puppet::Type.type(:exec) end let :rel do 'echo' end let :abs do make_absolute('/bin/echo') end let :path do make_absolute('/bin') end it "should fail with relative command and no path" do expect { type.new(:command => rel) }. to raise_error Puppet::Error, /no path was specified/ end it "should accept a relative command with a path" do type.new(:command => rel, :path => path).should be end it "should accept an absolute command with no path" do type.new(:command => abs).should be end it "should accept an absolute command with a path" do type.new(:command => abs, :path => path).should be end end end diff --git a/spec/unit/type/file/checksum_spec.rb b/spec/unit/type/file/checksum_spec.rb index 30c4aba6d..bb060f232 100755 --- a/spec/unit/type/file/checksum_spec.rb +++ b/spec/unit/type/file/checksum_spec.rb @@ -1,73 +1,73 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' checksum = Puppet::Type.type(:file).attrclass(:checksum) describe checksum do before do @path = Puppet.features.microsoft_windows? ? "c:/foo/bar" : "/foo/bar" @resource = Puppet::Type.type(:file).new :path => @path @checksum = @resource.parameter(:checksum) end it "should be a parameter" do checksum.superclass.must == Puppet::Parameter end it "should use its current value when asked to sum content" do @checksum.value = :md5lite @checksum.expects(:md5lite).with("foobar").returns "yay" @checksum.sum("foobar") end it "should use :md5 to sum when no value is set" do @checksum.expects(:md5).with("foobar").returns "yay" @checksum.sum("foobar") end it "should return the summed contents with a checksum label" do sum = Digest::MD5.hexdigest("foobar") @resource[:checksum] = :md5 @checksum.sum("foobar").should == "{md5}#{sum}" end it "should use :md5 as its default type" do @checksum.default.should == :md5 end it "should use its current value when asked to sum a file's content" do @checksum.value = :md5lite @checksum.expects(:md5lite_file).with(@path).returns "yay" @checksum.sum_file(@path) end it "should use :md5 to sum a file when no value is set" do @checksum.expects(:md5_file).with(@path).returns "yay" @checksum.sum_file(@path) end it "should convert all sums to strings when summing files" do @checksum.value = :mtime @checksum.expects(:mtime_file).with(@path).returns Time.now lambda { @checksum.sum_file(@path) }.should_not raise_error end it "should return the summed contents of a file with a checksum label" do @resource[:checksum] = :md5 @checksum.expects(:md5_file).returns "mysum" @checksum.sum_file(@path).should == "{md5}mysum" end it "should return the summed contents of a stream with a checksum label" do @resource[:checksum] = :md5 @checksum.expects(:md5_stream).returns "mysum" @checksum.sum_stream.should == "{md5}mysum" end it "should yield the sum_stream block to the underlying checksum" do @resource[:checksum] = :md5 @checksum.expects(:md5_stream).yields("something").returns("mysum") @checksum.sum_stream do |sum| sum.should == "something" end end end diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index 74c055512..88c869e53 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -1,440 +1,440 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/network/http_pool' require 'puppet/network/resolver' content = Puppet::Type.type(:file).attrclass(:content) describe content do include PuppetSpec::Files before do @filename = tmpfile('testfile') @catalog = Puppet::Resource::Catalog.new @resource = Puppet::Type.type(:file).new :path => @filename, :catalog => @catalog File.open(@filename, 'w') {|f| f.write "initial file content"} content.stubs(:standalone?).returns(false) end describe "when determining the checksum type" do it "should use the type specified in the source checksum if a source is set" do @resource[:source] = File.expand_path("/foo") @resource.parameter(:source).expects(:checksum).returns "{md5lite}eh" @content = content.new(:resource => @resource) @content.checksum_type.should == :md5lite end it "should use the type specified by the checksum parameter if no source is set" do @resource[:checksum] = :md5lite @content = content.new(:resource => @resource) @content.checksum_type.should == :md5lite end end describe "when determining the actual content to write" do it "should use the set content if available" do @content = content.new(:resource => @resource) @content.should = "ehness" @content.actual_content.should == "ehness" end it "should not use the content from the source if the source is set" do source = mock 'source' @resource.expects(:parameter).never.with(:source).returns source @content = content.new(:resource => @resource) @content.actual_content.should be_nil end end describe "when setting the desired content" do it "should make the actual content available via an attribute" do @content = content.new(:resource => @resource) @content.stubs(:checksum_type).returns "md5" @content.should = "this is some content" @content.actual_content.should == "this is some content" end it "should store the checksum as the desired content" do @content = content.new(:resource => @resource) digest = Digest::MD5.hexdigest("this is some content") @content.stubs(:checksum_type).returns "md5" @content.should = "this is some content" @content.should.must == "{md5}#{digest}" end it "should not checksum 'absent'" do @content = content.new(:resource => @resource) @content.should = :absent @content.should.must == :absent end it "should accept a checksum as the desired content" do @content = content.new(:resource => @resource) digest = Digest::MD5.hexdigest("this is some content") string = "{md5}#{digest}" @content.should = string @content.should.must == string end end describe "when retrieving the current content" do it "should return :absent if the file does not exist" do @content = content.new(:resource => @resource) @resource.expects(:stat).returns nil @content.retrieve.should == :absent end it "should not manage content on directories" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "directory" @resource.expects(:stat).returns stat @content.retrieve.should be_nil end it "should not manage content on links" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "link" @resource.expects(:stat).returns stat @content.retrieve.should be_nil end it "should always return the checksum as a string" do @content = content.new(:resource => @resource) @resource[:checksum] = :mtime stat = mock 'stat', :ftype => "file" @resource.expects(:stat).returns stat time = Time.now @resource.parameter(:checksum).expects(:mtime_file).with(@resource[:path]).returns time @content.retrieve.should == "{mtime}#{time}" end it "should return the checksum of the file if it exists and is a normal file" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "file" @resource.expects(:stat).returns stat @resource.parameter(:checksum).expects(:md5_file).with(@resource[:path]).returns "mysum" @content.retrieve.should == "{md5}mysum" end end describe "when testing whether the content is in sync" do before do @resource[:ensure] = :file @content = content.new(:resource => @resource) end it "should return true if the resource shouldn't be a regular file" do @resource.expects(:should_be_file?).returns false @content.should = "foo" @content.must be_safe_insync("whatever") end it "should return false if the current content is :absent" do @content.should = "foo" @content.should_not be_safe_insync(:absent) end it "should return false if the file should be a file but is not present" do @resource.expects(:should_be_file?).returns true @content.should = "foo" @content.should_not be_safe_insync(:absent) end describe "and the file exists" do before do @resource.stubs(:stat).returns mock("stat") end it "should return false if the current contents are different from the desired content" do @content.should = "some content" @content.should_not be_safe_insync("other content") end it "should return true if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.must be_safe_insync("{md5}" + Digest::MD5.hexdigest("some content")) end describe "and Puppet[:show_diff] is set" do before do Puppet[:show_diff] = true end it "should display a diff if the current contents are different from the desired content" do @content.should = "some content" @content.expects(:diff).returns("my diff").once @content.expects(:notice).with("\nmy diff").once @content.safe_insync?("other content") end it "should not display a diff if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.expects(:diff).never @content.safe_insync?("{md5}" + Digest::MD5.hexdigest("some content")) end end end describe "and :replace is false" do before do @resource.stubs(:replace?).returns false end it "should be insync if the file exists and the content is different" do @resource.stubs(:stat).returns mock('stat') @content.must be_safe_insync("whatever") end it "should be insync if the file exists and the content is right" do @resource.stubs(:stat).returns mock('stat') @content.must be_safe_insync("something") end it "should not be insync if the file does not exist" do @content.should = "foo" @content.should_not be_safe_insync(:absent) end end end describe "when changing the content" do before do @content = content.new(:resource => @resource) @content.should = "some content" @resource.stubs(:[]).with(:path).returns "/boo" @resource.stubs(:stat).returns "eh" end it "should use the file's :write method to write the content" do @resource.expects(:write).with(:content) @content.sync end it "should return :file_changed if the file already existed" do @resource.expects(:stat).returns "something" @resource.stubs(:write) @content.sync.should == :file_changed end it "should return :file_created if the file did not exist" do @resource.expects(:stat).returns nil @resource.stubs(:write) @content.sync.should == :file_created end end describe "when writing" do before do @content = content.new(:resource => @resource) end it "should attempt to read from the filebucket if no actual content nor source exists" do @fh = File.open(@filename, 'wb') @content.should = "{md5}foo" @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" @content.write(@fh) @fh.close end describe "from actual content" do before(:each) do @content.stubs(:actual_content).returns("this is content") end it "should write to the given file handle" do @fh.expects(:print).with("this is content") @content.write(@fh) end it "should return the current checksum value" do @resource.parameter(:checksum).expects(:sum_stream).returns "checksum" @content.write(@fh).should == "checksum" end end describe "from a file bucket" do it "should fail if a file bucket cannot be retrieved" do @content.should = "{md5}foo" @content.resource.expects(:bucket).returns nil lambda { @content.write(@fh) }.should raise_error(Puppet::Error) end it "should fail if the file bucket cannot find any content" do @content.should = "{md5}foo" bucket = stub 'bucket' @content.resource.expects(:bucket).returns bucket bucket.expects(:getfile).with("foo").raises "foobar" lambda { @content.write(@fh) }.should raise_error(Puppet::Error) end it "should write the returned content to the file" do @content.should = "{md5}foo" bucket = stub 'bucket' @content.resource.expects(:bucket).returns bucket bucket.expects(:getfile).with("foo").returns "mycontent" @fh.expects(:print).with("mycontent") @content.write(@fh) end end describe "from local source" do before(:each) do @sourcename = tmpfile('source') @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false, :source => @sourcename, :catalog => @catalog @source_content = "source file content\r\n"*10000 @sourcefile = File.open(@sourcename, 'wb') {|f| f.write @source_content} @content = @resource.newattr(:content) @source = @resource.parameter :source #newattr(:source) end it "should copy content from the source to the file" do @resource.write(@source) IO.binread(@filename).should == @source_content end it "should return the checksum computed" do File.open(@filename, 'wb') do |file| @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" end end end describe "from remote source" do before(:each) do @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false, :catalog => @catalog @response = stub_everything 'response', :code => "200" @source_content = "source file content\n"*10000 @response.stubs(:read_body).multiple_yields(*(["source file content\n"]*10000)) @conn = stub_everything 'connection' @conn.stubs(:request_get).yields @response Puppet::Network::HttpPool.stubs(:http_instance).returns @conn @content = @resource.newattr(:content) @sourcename = "puppet:///test/foo" @source = @resource.newattr(:source) @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end it "should write the contents to the file" do @resource.write(@source) IO.binread(@filename).should == @source_content end it "should not write anything if source is not found" do @response.stubs(:code).returns("404") lambda {@resource.write(@source)}.should raise_error(Net::HTTPError) { |e| e.message =~ /404/ } File.read(@filename).should == "initial file content" end it "should raise an HTTP error in case of server error" do @response.stubs(:code).returns("500") lambda { @content.write(@fh) }.should raise_error { |e| e.message.include? @source_content } end it "should return the checksum computed" do File.open(@filename, 'w') do |file| @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" end end end # These are testing the implementation rather than the desired behaviour; while that bites, there are a whole # pile of other methods in the File type that depend on intimate details of this implementation and vice-versa. # If these blow up, you are gonna have to review the callers to make sure they don't explode! --daniel 2011-02-01 describe "each_chunk_from should work" do before do @content = content.new(:resource => @resource) end it "when content is a string" do @content.each_chunk_from('i_am_a_string') { |chunk| chunk.should == 'i_am_a_string' } end # The following manifest is a case where source and content.should are both set # file { "/tmp/mydir" : # source => '/tmp/sourcedir', # recurse => true, # } it "when content checksum comes from source" do source_param = Puppet::Type.type(:file).attrclass(:source) source = source_param.new(:resource => @resource) @content.should = "{md5}123abcd" @content.expects(:chunk_file_from_source).returns('from_source') @content.each_chunk_from(source) { |chunk| chunk.should == 'from_source' } end it "when no content, source, but ensure present" do @resource[:ensure] = :present @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end # you might do this if you were just auditing it "when no content, source, but ensure file" do @resource[:ensure] = :file @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end it "when source_or_content is nil and content not a checksum" do @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end # the content is munged so that if it's a checksum nil gets passed in it "when content is a checksum it should try to read from filebucket" do @content.should = "{md5}123abcd" @content.expects(:read_file_from_filebucket).once.returns('im_a_filebucket') @content.each_chunk_from(nil) { |chunk| chunk.should == 'im_a_filebucket' } end it "when running as puppet apply" do Puppet[:default_file_terminus] = "file_server" source_or_content = stubs('source_or_content') source_or_content.expects(:content).once.returns :whoo @content.each_chunk_from(source_or_content) { |chunk| chunk.should == :whoo } end it "when running from source with a local file" do source_or_content = stubs('source_or_content') source_or_content.expects(:local?).returns true @content.expects(:chunk_file_from_disk).with(source_or_content).once.yields 'woot' @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } end it "when running from source with a remote file" do source_or_content = stubs('source_or_content') source_or_content.expects(:local?).returns false @content.expects(:chunk_file_from_source).with(source_or_content).once.yields 'woot' @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } end end end end diff --git a/spec/unit/type/file/ctime.rb b/spec/unit/type/file/ctime.rb index 1700d4928..65e2eed0d 100755 --- a/spec/unit/type/file/ctime.rb +++ b/spec/unit/type/file/ctime.rb @@ -1,34 +1,34 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:ctime) do require 'puppet_spec/files' include PuppetSpec::Files before do @filename = tmpfile('ctime') @resource = Puppet::Type.type(:file).new({:name => @filename}) end it "should be able to audit the file's ctime" do File.open(@filename, "w"){ } @resource[:audit] = [:ctime] # this .to_resource audit behavior is magical :-( @resource.to_resource[:ctime].should == File.stat(@filename).ctime end it "should return absent if auditing an absent file" do @resource[:audit] = [:ctime] @resource.to_resource[:ctime].should == :absent end it "should prevent the user from trying to set the ctime" do lambda { @resource[:ctime] = Time.now.to_s }.should raise_error(Puppet::Error, /ctime is read-only/) end end diff --git a/spec/unit/type/file/ensure_spec.rb b/spec/unit/type/file/ensure_spec.rb index 8555ef035..e54980880 100755 --- a/spec/unit/type/file/ensure_spec.rb +++ b/spec/unit/type/file/ensure_spec.rb @@ -1,84 +1,84 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' property = Puppet::Type.type(:file).attrclass(:ensure) describe property do before do # Wow that's a messy interface to the resource. @resource = stub 'resource', :[] => nil, :[]= => nil, :property => nil, :newattr => nil, :parameter => nil, :replace? => true @resource.stubs(:[]).returns "foo" @resource.stubs(:[]).with(:path).returns "/my/file" @ensure = property.new :resource => @resource end it "should be a subclass of Ensure" do property.superclass.must == Puppet::Property::Ensure end describe "when retrieving the current state" do it "should return :absent if the file does not exist" do @ensure = property.new(:resource => @resource) @resource.expects(:stat).returns nil @ensure.retrieve.should == :absent end it "should return the current file type if the file exists" do @ensure = property.new(:resource => @resource) stat = mock 'stat', :ftype => "directory" @resource.expects(:stat).returns stat @ensure.retrieve.should == :directory end end describe "when testing whether :ensure is in sync" do before do @ensure = property.new(:resource => @resource) @stat = stub 'stat', :ftype => "file" end it "should always be in sync if replace is 'false' unless the file is missing" do @ensure.should = :file @resource.expects(:replace?).returns false @ensure.safe_insync?(:link).should be_true end it "should be in sync if :ensure is set to :absent and the file does not exist" do @ensure.should = :absent @ensure.must be_safe_insync(:absent) end it "should not be in sync if :ensure is set to :absent and the file exists" do @ensure.should = :absent @ensure.should_not be_safe_insync(:file) end it "should be in sync if a normal file exists and :ensure is set to :present" do @ensure.should = :present @ensure.must be_safe_insync(:file) end it "should be in sync if a directory exists and :ensure is set to :present" do @ensure.should = :present @ensure.must be_safe_insync(:directory) end it "should be in sync if a symlink exists and :ensure is set to :present" do @ensure.should = :present @ensure.must be_safe_insync(:link) end it "should not be in sync if :ensure is set to :file and a directory exists" do @ensure.should = :file @ensure.should_not be_safe_insync(:directory) end end end diff --git a/spec/unit/type/file/group_spec.rb b/spec/unit/type/file/group_spec.rb index 3817eb665..46c41919d 100755 --- a/spec/unit/type/file/group_spec.rb +++ b/spec/unit/type/file/group_spec.rb @@ -1,60 +1,60 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:group) do include PuppetSpec::Files let(:path) { tmpfile('mode_spec') } let(:resource) { Puppet::Type.type(:file).new :path => path, :group => 'users' } let(:group) { resource.property(:group) } before :each do # If the provider was already loaded without root, it won't have the # feature, so we have to add it here to test. Puppet::Type.type(:file).defaultprovider.has_feature :manages_ownership end describe "#insync?" do before :each do resource[:group] = ['foos', 'bars'] resource.provider.stubs(:name2gid).with('foos').returns 1001 resource.provider.stubs(:name2gid).with('bars').returns 1002 end it "should fail if an group's id can't be found by name" do resource.provider.stubs(:name2gid).returns nil expect { group.insync?(5) }.to raise_error(/Could not find group foos/) end it "should use the id for comparisons, not the name" do group.insync?('foos').should be_false end it "should return true if the current group is one of the desired group" do group.insync?(1001).should be_true end it "should return false if the current group is not one of the desired group" do group.insync?(1003).should be_false end end %w[is_to_s should_to_s].each do |prop_to_s| describe "##{prop_to_s}" do it "should use the name of the user if it can find it" do resource.provider.stubs(:gid2name).with(1001).returns 'foos' group.send(prop_to_s, 1001).should == 'foos' end it "should use the id of the user if it can't" do resource.provider.stubs(:gid2name).with(1001).returns nil group.send(prop_to_s, 1001).should == 1001 end end end end diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb index 88881043c..3d4739624 100755 --- a/spec/unit/type/file/mode_spec.rb +++ b/spec/unit/type/file/mode_spec.rb @@ -1,142 +1,142 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:mode) do include PuppetSpec::Files let(:path) { tmpfile('mode_spec') } let(:resource) { Puppet::Type.type(:file).new :path => path, :mode => 0644 } let(:mode) { resource.property(:mode) } describe "#validate" do it "should accept values specified as integers" do expect { mode.value = 0755 }.not_to raise_error end it "should accept values specified as octal numbers in strings" do expect { mode.value = '0755' }.not_to raise_error end it "should not accept strings other than octal numbers" do expect do mode.value = 'readable please!' end.to raise_error(Puppet::Error, /The file mode specification is invalid/) end end describe "#munge" do # This is sort of a redundant test, but its spec is important. it "should return the value as a string" do mode.munge('0644').should be_a(String) end it "should accept strings as arguments" do mode.munge('0644').should == '644' end it "should accept integers are arguments" do mode.munge(0644).should == '644' end end describe "#dirmask" do before :each do Dir.mkdir(path) end it "should add execute bits corresponding to read bits for directories" do mode.dirmask('0644').should == '755' end it "should not add an execute bit when there is no read bit" do mode.dirmask('0600').should == '700' end it "should not add execute bits for files that aren't directories" do resource[:path] = tmpfile('other_file') mode.dirmask('0644').should == '0644' end end describe "#insync?" do it "should return true if the mode is correct" do FileUtils.touch(path) mode.must be_insync('644') end it "should return false if the mode is incorrect" do FileUtils.touch(path) mode.must_not be_insync('755') end it "should return true if the file is a link and we are managing links", :unless => Puppet.features.microsoft_windows? do File.symlink('anything', path) mode.must be_insync('644') end end describe "#retrieve" do it "should return absent if the resource doesn't exist" do resource[:path] = File.expand_path("/does/not/exist") mode.retrieve.should == :absent end it "should retrieve the directory mode from the provider" do Dir.mkdir(path) mode.expects(:dirmask).with('644').returns '755' resource.provider.expects(:mode).returns '755' mode.retrieve.should == '755' end it "should retrieve the file mode from the provider" do FileUtils.touch(path) mode.expects(:dirmask).with('644').returns '644' resource.provider.expects(:mode).returns '644' mode.retrieve.should == '644' end end describe '#should_to_s' do describe 'with a 3-digit mode' do it 'returns a 4-digit mode with a leading zero' do mode.should_to_s('755').should == '0755' end end describe 'with a 4-digit mode' do it 'returns the 4-digit mode when the first digit is a zero' do mode.should_to_s('0755').should == '0755' end it 'returns the 4-digit mode when the first digit is not a zero' do mode.should_to_s('1755').should == '1755' end end end describe '#is_to_s' do describe 'with a 3-digit mode' do it 'returns a 4-digit mode with a leading zero' do mode.is_to_s('755').should == '0755' end end describe 'with a 4-digit mode' do it 'returns the 4-digit mode when the first digit is a zero' do mode.is_to_s('0755').should == '0755' end it 'returns the 4-digit mode when the first digit is not a zero' do mode.is_to_s('1755').should == '1755' end end end end diff --git a/spec/unit/type/file/mtime.rb b/spec/unit/type/file/mtime.rb index 3222df095..a309cd479 100755 --- a/spec/unit/type/file/mtime.rb +++ b/spec/unit/type/file/mtime.rb @@ -1,34 +1,34 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:mtime) do require 'puppet_spec/files' include PuppetSpec::Files before do @filename = tmpfile('mtime') @resource = Puppet::Type.type(:file).new({:name => @filename}) end it "should be able to audit the file's mtime" do File.open(@filename, "w"){ } @resource[:audit] = [:mtime] # this .to_resource audit behavior is magical :-( @resource.to_resource[:mtime].should == File.stat(@filename).mtime end it "should return absent if auditing an absent file" do @resource[:audit] = [:mtime] @resource.to_resource[:mtime].should == :absent end it "should prevent the user from trying to set the mtime" do lambda { @resource[:mtime] = Time.now.to_s }.should raise_error(Puppet::Error, /mtime is read-only/) end end diff --git a/spec/unit/type/file/owner_spec.rb b/spec/unit/type/file/owner_spec.rb index a57e91091..19e077874 100755 --- a/spec/unit/type/file/owner_spec.rb +++ b/spec/unit/type/file/owner_spec.rb @@ -1,58 +1,58 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:owner) do include PuppetSpec::Files let(:path) { tmpfile('mode_spec') } let(:resource) { Puppet::Type.type(:file).new :path => path, :owner => 'joeuser' } let(:owner) { resource.property(:owner) } before :each do Puppet.features.stubs(:root?).returns(true) end describe "#insync?" do before :each do resource[:owner] = ['foo', 'bar'] resource.provider.stubs(:name2uid).with('foo').returns 1001 resource.provider.stubs(:name2uid).with('bar').returns 1002 end it "should fail if an owner's id can't be found by name" do resource.provider.stubs(:name2uid).returns nil expect { owner.insync?(5) }.to raise_error(/Could not find user foo/) end it "should use the id for comparisons, not the name" do owner.insync?('foo').should be_false end it "should return true if the current owner is one of the desired owners" do owner.insync?(1001).should be_true end it "should return false if the current owner is not one of the desired owners" do owner.insync?(1003).should be_false end end %w[is_to_s should_to_s].each do |prop_to_s| describe "##{prop_to_s}" do it "should use the name of the user if it can find it" do resource.provider.stubs(:uid2name).with(1001).returns 'foo' owner.send(prop_to_s, 1001).should == 'foo' end it "should use the id of the user if it can't" do resource.provider.stubs(:uid2name).with(1001).returns nil owner.send(prop_to_s, 1001).should == 1001 end end end end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb index f6e7451c7..8a279c876 100755 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -1,89 +1,89 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' [:seluser, :selrole, :seltype, :selrange].each do |param| property = Puppet::Type.type(:file).attrclass(param) describe property do include PuppetSpec::Files before do @path = make_absolute("/my/file") @resource = Puppet::Type.type(:file).new :path => @path @sel = property.new :resource => @resource end it "retrieve on #{param} should return :absent if the file isn't statable" do @resource.expects(:stat).returns nil @sel.retrieve.should == :absent end it "should retrieve nil for #{param} if there is no SELinux support" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat @sel.expects(:get_selinux_current_context).with(@path).returns nil @sel.retrieve.should be_nil end it "should retrieve #{param} if a SELinux context is found with a range" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat @sel.expects(:get_selinux_current_context).with(@path).returns "user_u:role_r:type_t:s0" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end @sel.retrieve.should == expectedresult end it "should retrieve #{param} if a SELinux context is found without a range" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat @sel.expects(:get_selinux_current_context).with(@path).returns "user_u:role_r:type_t" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; nil end @sel.retrieve.should == expectedresult end it "should handle no default gracefully" do @sel.expects(:get_selinux_default_context).with(@path).returns nil @sel.default.must be_nil end it "should be able to detect matchpathcon defaults" do @sel.stubs(:debug) @sel.expects(:get_selinux_default_context).with(@path).returns "user_u:role_r:type_t:s0" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end @sel.default.must == expectedresult end it "should return nil for defaults if selinux_ignore_defaults is true" do @resource[:selinux_ignore_defaults] = :true @sel.default.must be_nil end it "should be able to set a new context" do stat = stub 'stat', :ftype => "foo" @sel.should = %w{newone} @sel.expects(:set_selinux_context).with(@path, ["newone"], param) @sel.sync end it "should do nothing for safe_insync? if no SELinux support" do @sel.should = %{newcontext} @sel.expects(:selinux_support?).returns false @sel.safe_insync?("oldcontext").should == true end end end diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb index 44f0bda29..a261cf067 100755 --- a/spec/unit/type/file/source_spec.rb +++ b/spec/unit/type/file/source_spec.rb @@ -1,360 +1,360 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'uri' source = Puppet::Type.type(:file).attrclass(:source) describe Puppet::Type.type(:file).attrclass(:source) do include PuppetSpec::Files before do # Wow that's a messy interface to the resource. @environment = "myenv" @resource = stub 'resource', :[]= => nil, :property => nil, :catalog => stub("catalog", :dependent_data_expired? => false, :environment => @environment), :line => 0, :file => '' @foobar = make_absolute("/foo/bar baz") @feebooz = make_absolute("/fee/booz baz") @foobar_uri = URI.unescape(Puppet::Util.path_to_uri(@foobar).to_s) @feebooz_uri = URI.unescape(Puppet::Util.path_to_uri(@feebooz).to_s) end it "should be a subclass of Parameter" do source.superclass.must == Puppet::Parameter end describe "#validate" do let(:path) { tmpfile('file_source_validate') } let(:resource) { Puppet::Type.type(:file).new(:path => path) } it "should fail if the set values are not URLs" do URI.expects(:parse).with('foo').raises RuntimeError lambda { resource[:source] = %w{foo} }.must raise_error(Puppet::Error) end it "should fail if the URI is not a local file, file URI, or puppet URI" do lambda { resource[:source] = %w{http://foo/bar} }.must raise_error(Puppet::Error, /Cannot use URLs of type 'http' as source for fileserving/) end it "should strip trailing forward slashes", :unless => Puppet.features.microsoft_windows? do resource[:source] = "/foo/bar\\//" resource[:source].should == %w{file:/foo/bar\\} end it "should strip trailing forward and backslashes", :if => Puppet.features.microsoft_windows? do resource[:source] = "X:/foo/bar\\//" resource[:source].should == %w{file:/X:/foo/bar} end it "should accept an array of sources" do resource[:source] = %w{file:///foo/bar puppet://host:8140/foo/bar} resource[:source].should == %w{file:///foo/bar puppet://host:8140/foo/bar} end it "should accept file path characters that are not valid in URI" do resource[:source] = 'file:///foo bar' end it "should reject relative URI sources" do lambda { resource[:source] = 'foo/bar' }.must raise_error(Puppet::Error) end it "should reject opaque sources" do lambda { resource[:source] = 'mailto:foo@com' }.must raise_error(Puppet::Error) end it "should accept URI authority component" do resource[:source] = 'file://host/foo' resource[:source].should == %w{file://host/foo} end it "should accept when URI authority is absent" do resource[:source] = 'file:///foo/bar' resource[:source].should == %w{file:///foo/bar} end end describe "#munge" do let(:path) { tmpfile('file_source_munge') } let(:resource) { Puppet::Type.type(:file).new(:path => path) } it "should prefix file scheme to absolute paths" do resource[:source] = path resource[:source].should == [URI.unescape(Puppet::Util.path_to_uri(path).to_s)] end %w[file puppet].each do |scheme| it "should not prefix valid #{scheme} URIs" do resource[:source] = "#{scheme}:///foo bar" resource[:source].should == ["#{scheme}:///foo bar"] end end end describe "when returning the metadata" do before do @metadata = stub 'metadata', :source= => nil end it "should return already-available metadata" do @source = source.new(:resource => @resource) @source.metadata = "foo" @source.metadata.should == "foo" end it "should return nil if no @should value is set and no metadata is available" do @source = source.new(:resource => @resource) @source.metadata.should be_nil end it "should collect its metadata using the Metadata class if it is not already set" do @source = source.new(:resource => @resource, :value => @foobar) Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment).returns @metadata @source.metadata end it "should use the metadata from the first found source" do metadata = stub 'metadata', :source= => nil @source = source.new(:resource => @resource, :value => [@foobar, @feebooz]) Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment).returns nil Puppet::FileServing::Metadata.indirection.expects(:find).with(@feebooz_uri, :environment => @environment).returns metadata @source.metadata.should equal(metadata) end it "should store the found source as the metadata's source" do metadata = mock 'metadata' @source = source.new(:resource => @resource, :value => @foobar) Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment).returns metadata metadata.expects(:source=).with(@foobar_uri) @source.metadata end it "should fail intelligently if an exception is encountered while querying for metadata" do @source = source.new(:resource => @resource, :value => @foobar) Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment).raises RuntimeError @source.expects(:fail).raises ArgumentError lambda { @source.metadata }.should raise_error(ArgumentError) end it "should fail if no specified sources can be found" do @source = source.new(:resource => @resource, :value => @foobar) Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar_uri, :environment => @environment).returns nil @source.expects(:fail).raises RuntimeError lambda { @source.metadata }.should raise_error(RuntimeError) end end it "should have a method for setting the desired values on the resource" do source.new(:resource => @resource).must respond_to(:copy_source_values) end describe "when copying the source values" do before do @resource = Puppet::Type.type(:file).new :path => @foobar @source = source.new(:resource => @resource) @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file", :source => @foobar @source.stubs(:metadata).returns @metadata Puppet.features.stubs(:root?).returns true end it "should fail if there is no metadata" do @source.stubs(:metadata).returns nil @source.expects(:devfail).raises ArgumentError lambda { @source.copy_source_values }.should raise_error(ArgumentError) end it "should set :ensure to the file type" do @metadata.stubs(:ftype).returns "file" @source.copy_source_values @resource[:ensure].must == :file end it "should not set 'ensure' if it is already set to 'absent'" do @metadata.stubs(:ftype).returns "file" @resource[:ensure] = :absent @source.copy_source_values @resource[:ensure].must == :absent end describe "and the source is a file" do before do @metadata.stubs(:ftype).returns "file" Puppet.features.stubs(:microsoft_windows?).returns false end it "should copy the metadata's owner, group, checksum, and mode to the resource if they are not set on the resource" do @source.copy_source_values @resource[:owner].must == 100 @resource[:group].must == 200 @resource[:mode].must == "173" # Metadata calls it checksum, we call it content. @resource[:content].must == @metadata.checksum end it "should not copy the metadata's owner to the resource if it is already set" do @resource[:owner] = 1 @resource[:group] = 2 @resource[:mode] = 3 @resource[:content] = "foobar" @source.copy_source_values @resource[:owner].must == 1 @resource[:group].must == 2 @resource[:mode].must == "3" @resource[:content].should_not == @metadata.checksum end describe "and puppet is not running as root" do it "should not try to set the owner" do Puppet.features.expects(:root?).returns false @source.copy_source_values @resource[:owner].should be_nil end end describe "on Windows" do before :each do Puppet.features.stubs(:microsoft_windows?).returns true end it "should not copy owner and group from remote sources" do @source.stubs(:local?).returns false @source.copy_source_values @resource[:owner].must be_nil @resource[:group].must be_nil end it "should copy owner and group from local sources" do @source.stubs(:local?).returns true @source.copy_source_values @resource[:owner].must == 100 @resource[:group].must == 200 end end end describe "and the source is a link" do it "should set the target to the link destination" do @metadata.stubs(:ftype).returns "link" @metadata.stubs(:links).returns "manage" @resource.stubs(:[]) @resource.stubs(:[]=) @metadata.expects(:destination).returns "/path/to/symlink" @resource.expects(:[]=).with(:target, "/path/to/symlink") @source.copy_source_values end end end it "should have a local? method" do source.new(:resource => @resource).must be_respond_to(:local?) end context "when accessing source properties" do let(:catalog) { Puppet::Resource::Catalog.new } let(:path) { tmpfile('file_resource') } let(:resource) { Puppet::Type.type(:file).new(:path => path, :catalog => catalog) } let(:sourcepath) { tmpfile('file_source') } describe "for local sources" do before :each do FileUtils.touch(sourcepath) end describe "on POSIX systems", :if => Puppet.features.posix? do ['', "file:", "file://"].each do |prefix| it "with prefix '#{prefix}' should be local" do resource[:source] = "#{prefix}#{sourcepath}" resource.parameter(:source).must be_local end it "should be able to return the metadata source full path" do resource[:source] = "#{prefix}#{sourcepath}" resource.parameter(:source).full_path.should == sourcepath end end end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do ['', "file:/", "file:///"].each do |prefix| it "should be local with prefix '#{prefix}'" do resource[:source] = "#{prefix}#{sourcepath}" resource.parameter(:source).must be_local end it "should be able to return the metadata source full path" do resource[:source] = "#{prefix}#{sourcepath}" resource.parameter(:source).full_path.should == sourcepath end it "should convert backslashes to forward slashes" do resource[:source] = "#{prefix}#{sourcepath.gsub(/\\/, '/')}" end end it "should be UNC with two slashes" end end describe "for remote sources" do let(:sourcepath) { "/path/to/source" } let(:uri) { URI::Generic.build(:scheme => 'puppet', :host => 'server', :port => 8192, :path => sourcepath).to_s } before(:each) do metadata = Puppet::FileServing::Metadata.new(path, :source => uri, 'type' => 'file') #metadata = stub('remote', :ftype => "file", :source => uri) Puppet::FileServing::Metadata.indirection.stubs(:find).with(uri, has_key(:environment)).returns metadata resource[:source] = uri end it "should not be local" do resource.parameter(:source).should_not be_local end it "should be able to return the metadata source full path" do resource.parameter(:source).full_path.should == "/path/to/source" end it "should be able to return the source server" do resource.parameter(:source).server.should == "server" end it "should be able to return the source port" do resource.parameter(:source).port.should == 8192 end describe "which don't specify server or port" do let(:uri) { "puppet:///path/to/source" } it "should return the default source server" do Puppet.settings.expects(:[]).with(:server).returns("myserver") resource.parameter(:source).server.should == "myserver" end it "should return the default source port" do Puppet.settings.expects(:[]).with(:masterport).returns(1234) resource.parameter(:source).port.should == 1234 end end end end end diff --git a/spec/unit/type/file/type.rb b/spec/unit/type/file/type.rb index 7d4af0e16..3f4694671 100755 --- a/spec/unit/type/file/type.rb +++ b/spec/unit/type/file/type.rb @@ -1,19 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file).attrclass(:type) do require 'puppet_spec/files' include PuppetSpec::Files before do @filename = tmpfile('type') @resource = Puppet::Type.type(:file).new({:name => @filename}) end it "should prevent the user from trying to set the type" do lambda { @resource[:type] = "fifo" }.should raise_error(Puppet::Error, /type is read-only/) end end diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index 97b49c37b..8002aef8e 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -1,1522 +1,1522 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:file) do include PuppetSpec::Files let(:path) { tmpfile('file_testing') } let(:file) { described_class.new(:path => path, :catalog => catalog) } let(:provider) { file.provider } let(:catalog) { Puppet::Resource::Catalog.new } before do @real_posix = Puppet.features.posix? Puppet.features.stubs("posix?").returns(true) end describe "the path parameter" do describe "on POSIX systems", :if => Puppet.features.posix? do it "should remove trailing slashes" do file[:path] = "/foo/bar/baz/" file[:path].should == "/foo/bar/baz" end it "should remove double slashes" do file[:path] = "/foo/bar//baz" file[:path].should == "/foo/bar/baz" end it "should remove trailing double slashes" do file[:path] = "/foo/bar/baz//" file[:path].should == "/foo/bar/baz" end it "should leave a single slash alone" do file[:path] = "/" file[:path].should == "/" end it "should accept a double-slash at the start of the path" do expect { file[:path] = "//tmp/xxx" # REVISIT: This should be wrong, later. See the next test. # --daniel 2011-01-31 file[:path].should == '/tmp/xxx' }.should_not raise_error end # REVISIT: This is pending, because I don't want to try and audit the # entire codebase to make sure we get this right. POSIX treats two (and # exactly two) '/' characters at the start of the path specially. # # See sections 3.2 and 4.11, which allow DomainOS to be all special like # and still have the POSIX branding and all. --daniel 2011-01-31 it "should preserve the double-slash at the start of the path" end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do it "should remove trailing slashes" do file[:path] = "X:/foo/bar/baz/" file[:path].should == "X:/foo/bar/baz" end it "should remove double slashes" do file[:path] = "X:/foo/bar//baz" file[:path].should == "X:/foo/bar/baz" end it "should remove trailing double slashes" do file[:path] = "X:/foo/bar/baz//" file[:path].should == "X:/foo/bar/baz" end it "should leave a drive letter with a slash alone" do file[:path] = "X:/" file[:path].should == "X:/" end it "should not accept a drive letter without a slash" do lambda { file[:path] = "X:" }.should raise_error(/File paths must be fully qualified/) end describe "when using UNC filenames", :if => Puppet.features.microsoft_windows? do before :each do pending("UNC file paths not yet supported") end it "should remove trailing slashes" do file[:path] = "//server/foo/bar/baz/" file[:path].should == "//server/foo/bar/baz" end it "should remove double slashes" do file[:path] = "//server/foo/bar//baz" file[:path].should == "//server/foo/bar/baz" end it "should remove trailing double slashes" do file[:path] = "//server/foo/bar/baz//" file[:path].should == "//server/foo/bar/baz" end it "should remove a trailing slash from a sharename" do file[:path] = "//server/foo/" file[:path].should == "//server/foo" end it "should not modify a sharename" do file[:path] = "//server/foo" file[:path].should == "//server/foo" end end end end describe "the backup parameter" do [false, 'false', :false].each do |value| it "should disable backup if the value is #{value.inspect}" do file[:backup] = value file[:backup].should == false end end [true, 'true', '.puppet-bak'].each do |value| it "should use .puppet-bak if the value is #{value.inspect}" do file[:backup] = value file[:backup].should == '.puppet-bak' end end it "should use the provided value if it's any other string" do file[:backup] = "over there" file[:backup].should == "over there" end it "should fail if backup is set to anything else" do expect do file[:backup] = 97 end.to raise_error(Puppet::Error, /Invalid backup type 97/) end end describe "the recurse parameter" do it "should default to recursion being disabled" do file[:recurse].should be_false end [true, "true", 10, "inf", "remote"].each do |value| it "should consider #{value} to enable recursion" do file[:recurse] = value file[:recurse].should be_true end end [false, "false", 0].each do |value| it "should consider #{value} to disable recursion" do file[:recurse] = value file[:recurse].should be_false end end it "should warn if recurse is specified as a number" do Puppet.expects(:deprecation_warning).with("Setting recursion depth with the recurse parameter is now deprecated, please use recurselimit") file[:recurse] = 3 end end describe "the recurselimit parameter" do it "should accept integers" do file[:recurselimit] = 12 file[:recurselimit].should == 12 end it "should munge string numbers to number numbers" do file[:recurselimit] = '12' file[:recurselimit].should == 12 end it "should fail if given a non-number" do expect do file[:recurselimit] = 'twelve' end.to raise_error(Puppet::Error, /Invalid value "twelve"/) end end describe "the replace parameter" do [true, :true, :yes].each do |value| it "should consider #{value} to be true" do file[:replace] = value file[:replace].should == :true end end [false, :false, :no].each do |value| it "should consider #{value} to be false" do file[:replace] = value file[:replace].should == :false end end end describe "#[]" do it "should raise an exception" do expect do described_class['anything'] end.to raise_error("Global resource access is deprecated") end end describe ".instances" do it "should return an empty array" do described_class.instances.should == [] end end describe "#asuser" do before :each do # Mocha won't let me just stub SUIDManager.asuser to yield and return, # but it will do exactly that if we're not root. Puppet.features.stubs(:root?).returns false end it "should return the desired owner if they can write to the parent directory" do file[:owner] = 1001 FileTest.stubs(:writable?).with(File.dirname file[:path]).returns true file.asuser.should == 1001 end it "should return nil if the desired owner can't write to the parent directory" do file[:owner] = 1001 FileTest.stubs(:writable?).with(File.dirname file[:path]).returns false file.asuser.should == nil end it "should return nil if not managing owner" do file.asuser.should == nil end end describe "#bucket" do it "should return nil if backup is off" do file[:backup] = false file.bucket.should == nil end it "should not return a bucket if using a file extension for backup" do file[:backup] = '.backup' file.bucket.should == nil end it "should return the default filebucket if using the 'puppet' filebucket" do file[:backup] = 'puppet' bucket = stub('bucket') file.stubs(:default_bucket).returns bucket file.bucket.should == bucket end it "should fail if using a remote filebucket and no catalog exists" do file.catalog = nil file[:backup] = 'my_bucket' expect { file.bucket }.to raise_error(Puppet::Error, "Can not find filebucket for backups without a catalog") end it "should fail if the specified filebucket isn't in the catalog" do file[:backup] = 'my_bucket' expect { file.bucket }.to raise_error(Puppet::Error, "Could not find filebucket my_bucket specified in backup") end it "should use the specified filebucket if it is in the catalog" do file[:backup] = 'my_bucket' filebucket = Puppet::Type.type(:filebucket).new(:name => 'my_bucket') catalog.add_resource(filebucket) file.bucket.should == filebucket.bucket end end describe "#asuser" do before :each do # Mocha won't let me just stub SUIDManager.asuser to yield and return, # but it will do exactly that if we're not root. Puppet.features.stubs(:root?).returns false end it "should return the desired owner if they can write to the parent directory" do file[:owner] = 1001 FileTest.stubs(:writable?).with(File.dirname file[:path]).returns true file.asuser.should == 1001 end it "should return nil if the desired owner can't write to the parent directory" do file[:owner] = 1001 FileTest.stubs(:writable?).with(File.dirname file[:path]).returns false file.asuser.should == nil end it "should return nil if not managing owner" do file.asuser.should == nil end end describe "#bucket" do it "should return nil if backup is off" do file[:backup] = false file.bucket.should == nil end it "should return nil if using a file extension for backup" do file[:backup] = '.backup' file.bucket.should == nil end it "should return the default filebucket if using the 'puppet' filebucket" do file[:backup] = 'puppet' bucket = stub('bucket') file.stubs(:default_bucket).returns bucket file.bucket.should == bucket end it "should fail if using a remote filebucket and no catalog exists" do file.catalog = nil file[:backup] = 'my_bucket' expect { file.bucket }.to raise_error(Puppet::Error, "Can not find filebucket for backups without a catalog") end it "should fail if the specified filebucket isn't in the catalog" do file[:backup] = 'my_bucket' expect { file.bucket }.to raise_error(Puppet::Error, "Could not find filebucket my_bucket specified in backup") end it "should use the specified filebucket if it is in the catalog" do file[:backup] = 'my_bucket' filebucket = Puppet::Type.type(:filebucket).new(:name => 'my_bucket') catalog.add_resource(filebucket) file.bucket.should == filebucket.bucket end end describe "#exist?" do it "should be considered existent if it can be stat'ed" do file.expects(:stat).returns mock('stat') file.must be_exist end it "should be considered nonexistent if it can not be stat'ed" do file.expects(:stat).returns nil file.must_not be_exist end end describe "#eval_generate" do before do @graph = stub 'graph', :add_edge => nil catalog.stubs(:relationship_graph).returns @graph end it "should recurse if recursion is enabled" do resource = stub('resource', :[] => 'resource') file.expects(:recurse).returns [resource] file[:recurse] = true file.eval_generate.should == [resource] end it "should not recurse if recursion is disabled" do file.expects(:recurse).never file[:recurse] = false file.eval_generate.should == [] end end describe "#ancestors" do it "should return the ancestors of the file, in ascending order" do file = described_class.new(:path => make_absolute("/tmp/foo/bar/baz/qux")) pieces = %W[#{make_absolute('/')} tmp foo bar baz] ancestors = file.ancestors ancestors.should_not be_empty ancestors.reverse.each_with_index do |path,i| path.should == File.join(*pieces[0..i]) end end end describe "#flush" do it "should flush all properties that respond to :flush" do file[:source] = File.expand_path(__FILE__) file.parameter(:source).expects(:flush) file.flush end it "should reset its stat reference" do FileUtils.touch(path) stat1 = file.stat file.stat.should equal(stat1) file.flush file.stat.should_not equal(stat1) end end describe "#initialize" do it "should remove a trailing slash from the title to create the path" do title = File.expand_path("/abc/\n\tdef/") file = described_class.new(:title => title) file[:path].should == title end it "should set a desired 'ensure' value if none is set and 'content' is set" do file = described_class.new(:path => path, :content => "/foo/bar") file[:ensure].should == :file end it "should set a desired 'ensure' value if none is set and 'target' is set" do file = described_class.new(:path => path, :target => File.expand_path(__FILE__)) file[:ensure].should == :symlink end end describe "#mark_children_for_purging" do it "should set each child's ensure to absent" do paths = %w[foo bar baz] children = paths.inject({}) do |children,child| children.merge child => described_class.new(:path => File.join(path, child), :ensure => :present) end file.mark_children_for_purging(children) children.length.should == 3 children.values.each do |child| child[:ensure].should == :absent end end it "should skip children which have a source" do child = described_class.new(:path => path, :ensure => :present, :source => File.expand_path(__FILE__)) file.mark_children_for_purging('foo' => child) child[:ensure].should == :present end end describe "#newchild" do it "should create a new resource relative to the parent" do child = file.newchild('bar') child.should be_a(described_class) child[:path].should == File.join(file[:path], 'bar') end { :ensure => :present, :recurse => true, :recurselimit => 5, :target => "some_target", :source => File.expand_path("some_source"), }.each do |param, value| it "should omit the #{param} parameter" do # Make a new file, because we have to set the param at initialization # or it wouldn't be copied regardless. file = described_class.new(:path => path, param => value) child = file.newchild('bar') child[param].should_not == value end end it "should copy all of the parent resource's 'should' values that were set at initialization" do parent = described_class.new(:path => path, :owner => 'root', :group => 'wheel') child = parent.newchild("my/path") child[:owner].should == 'root' child[:group].should == 'wheel' end it "should not copy default values to the new child" do child = file.newchild("my/path") child.original_parameters.should_not include(:backup) end it "should not copy values to the child which were set by the source" do source = File.expand_path(__FILE__) file[:source] = source metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever", :source => source file.parameter(:source).stubs(:metadata).returns metadata file.parameter(:source).copy_source_values file.class.expects(:new).with { |params| params[:group].nil? } file.newchild("my/path") end end describe "#purge?" do it "should return false if purge is not set" do file.must_not be_purge end it "should return true if purge is set to true" do file[:purge] = true file.must be_purge end it "should return false if purge is set to false" do file[:purge] = false file.must_not be_purge end end describe "#recurse" do before do file[:recurse] = true @metadata = Puppet::FileServing::Metadata end describe "and a source is set" do it "should pass the already-discovered resources to recurse_remote" do file[:source] = File.expand_path(__FILE__) file.stubs(:recurse_local).returns(:foo => "bar") file.expects(:recurse_remote).with(:foo => "bar").returns [] file.recurse end end describe "and a target is set" do it "should use recurse_link" do file[:target] = File.expand_path(__FILE__) file.stubs(:recurse_local).returns(:foo => "bar") file.expects(:recurse_link).with(:foo => "bar").returns [] file.recurse end end it "should use recurse_local if recurse is not remote" do file.expects(:recurse_local).returns({}) file.recurse end it "should not use recurse_local if recurse is remote" do file[:recurse] = :remote file.expects(:recurse_local).never file.recurse end it "should return the generated resources as an array sorted by file path" do one = stub 'one', :[] => "/one" two = stub 'two', :[] => "/one/two" three = stub 'three', :[] => "/three" file.expects(:recurse_local).returns(:one => one, :two => two, :three => three) file.recurse.should == [one, two, three] end describe "and purging is enabled" do before do file[:purge] = true end it "should mark each file for removal" do local = described_class.new(:path => path, :ensure => :present) file.expects(:recurse_local).returns("local" => local) file.recurse local[:ensure].should == :absent end it "should not remove files that exist in the remote repository" do file[:source] = File.expand_path(__FILE__) file.expects(:recurse_local).returns({}) remote = described_class.new(:path => path, :source => File.expand_path(__FILE__), :ensure => :present) file.expects(:recurse_remote).with { |hash| hash["remote"] = remote } file.recurse remote[:ensure].should_not == :absent end end end describe "#remove_less_specific_files" do it "should remove any nested files that are already in the catalog" do foo = described_class.new :path => File.join(file[:path], 'foo') bar = described_class.new :path => File.join(file[:path], 'bar') baz = described_class.new :path => File.join(file[:path], 'baz') catalog.add_resource(foo) catalog.add_resource(bar) file.remove_less_specific_files([foo, bar, baz]).should == [baz] end end describe "#remove_less_specific_files" do it "should remove any nested files that are already in the catalog" do foo = described_class.new :path => File.join(file[:path], 'foo') bar = described_class.new :path => File.join(file[:path], 'bar') baz = described_class.new :path => File.join(file[:path], 'baz') catalog.add_resource(foo) catalog.add_resource(bar) file.remove_less_specific_files([foo, bar, baz]).should == [baz] end end describe "#recurse?" do it "should be true if recurse is true" do file[:recurse] = true file.must be_recurse end it "should be true if recurse is remote" do file[:recurse] = :remote file.must be_recurse end it "should be false if recurse is false" do file[:recurse] = false file.must_not be_recurse end end describe "#recurse_link" do before do @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory" @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file" @resource = stub 'file', :[]= => nil end it "should pass its target to the :perform_recursion method" do file[:target] = "mylinks" file.expects(:perform_recursion).with("mylinks").returns [@first] file.stubs(:newchild).returns @resource file.recurse_link({}) end it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do @first.stubs(:relative_path).returns "." file[:target] = "mylinks" file.expects(:perform_recursion).with("mylinks").returns [@first] file.stubs(:newchild).never file.expects(:[]=).with(:ensure, :directory) file.recurse_link({}) end it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do file.expects(:perform_recursion).returns [@first, @second] file.expects(:newchild).with(@first.relative_path).returns @resource file.recurse_link("second" => @resource) end it "should not create a new child resource for paths that already exist in the children hash" do file.expects(:perform_recursion).returns [@first] file.expects(:newchild).never file.recurse_link("first" => @resource) end it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory" do file.stubs(:perform_recursion).returns [@first, @second] file.recurse_link("first" => @resource, "second" => file) file[:ensure].should == :link file[:target].should == "/my/second" end it "should :ensure to :directory if the file is a directory" do file.stubs(:perform_recursion).returns [@first, @second] file.recurse_link("first" => file, "second" => @resource) file[:ensure].should == :directory end it "should return a hash with both created and existing resources with the relative paths as the hash keys" do file.expects(:perform_recursion).returns [@first, @second] file.stubs(:newchild).returns file file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file} end end describe "#recurse_local" do before do @metadata = stub 'metadata', :relative_path => "my/file" end it "should pass its path to the :perform_recursion method" do file.expects(:perform_recursion).with(file[:path]).returns [@metadata] file.stubs(:newchild) file.recurse_local end it "should return an empty hash if the recursion returns nothing" do file.expects(:perform_recursion).returns nil file.recurse_local.should == {} end it "should create a new child resource with each generated metadata instance's relative path" do file.expects(:perform_recursion).returns [@metadata] file.expects(:newchild).with(@metadata.relative_path).returns "fiebar" file.recurse_local end it "should not create a new child resource for the '.' directory" do @metadata.stubs(:relative_path).returns "." file.expects(:perform_recursion).returns [@metadata] file.expects(:newchild).never file.recurse_local end it "should return a hash of the created resources with the relative paths as the hash keys" do file.expects(:perform_recursion).returns [@metadata] file.expects(:newchild).with("my/file").returns "fiebar" file.recurse_local.should == {"my/file" => "fiebar"} end it "should set checksum_type to none if this file checksum is none" do file[:checksum] = :none Puppet::FileServing::Metadata.indirection.expects(:search).with { |path,params| params[:checksum_type] == :none }.returns [@metadata] file.expects(:newchild).with("my/file").returns "fiebar" file.recurse_local end end describe "#recurse_remote" do let(:my) { File.expand_path('/my') } before do file[:source] = "puppet://foo/bar" @first = Puppet::FileServing::Metadata.new(my, :relative_path => "first") @second = Puppet::FileServing::Metadata.new(my, :relative_path => "second") @first.stubs(:ftype).returns "directory" @second.stubs(:ftype).returns "directory" @parameter = stub 'property', :metadata= => nil @resource = stub 'file', :[]= => nil, :parameter => @parameter end it "should pass its source to the :perform_recursion method" do data = Puppet::FileServing::Metadata.new(File.expand_path("/whatever"), :relative_path => "foobar") file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] file.stubs(:newchild).returns @resource file.recurse_remote({}) end it "should not recurse when the remote file is not a directory" do data = Puppet::FileServing::Metadata.new(File.expand_path("/whatever"), :relative_path => ".") data.stubs(:ftype).returns "file" file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] file.expects(:newchild).never file.recurse_remote({}) end it "should set the source of each returned file to the searched-for URI plus the found relative path" do @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path) file.expects(:perform_recursion).returns [@first] file.stubs(:newchild).returns @resource file.recurse_remote({}) end it "should create a new resource for any relative file paths that do not already have a resource" do file.stubs(:perform_recursion).returns [@first] file.expects(:newchild).with("first").returns @resource file.recurse_remote({}).should == {"first" => @resource} end it "should not create a new resource for any relative file paths that do already have a resource" do file.stubs(:perform_recursion).returns [@first] file.expects(:newchild).never file.recurse_remote("first" => @resource) end it "should set the source of each resource to the source of the metadata" do file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path)) file.recurse_remote("first" => @resource) end # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already # filed, and when it's fixed, we'll just fix the whole flow. it "should set the checksum type to :md5 if the remote file is a file" do @first.stubs(:ftype).returns "file" file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:checksum, :md5) file.recurse_remote("first" => @resource) end it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do file.stubs(:perform_recursion).returns [@first] @resource.expects(:parameter).with(:source).returns @parameter @parameter.expects(:metadata=).with(@first) file.recurse_remote("first" => @resource) end it "should not create a new resource for the '.' file" do @first.stubs(:relative_path).returns "." file.stubs(:perform_recursion).returns [@first] file.expects(:newchild).never file.recurse_remote({}) end it "should store the metadata in the main file's source property if the relative path is '.'" do @first.stubs(:relative_path).returns "." file.stubs(:perform_recursion).returns [@first] file.parameter(:source).expects(:metadata=).with @first file.recurse_remote("first" => @resource) end describe "and multiple sources are provided" do let(:sources) do h = {} %w{/a /b /c /d}.each do |key| h[key] = URI.unescape(Puppet::Util.path_to_uri(File.expand_path(key)).to_s) end h end describe "and :sourceselect is set to :first" do it "should create file instances for the results for the first source to return any values" do data = Puppet::FileServing::Metadata.new(File.expand_path("/whatever"), :relative_path => "foobar") file[:source] = sources.keys.sort.map { |key| File.expand_path(key) } file.expects(:perform_recursion).with(sources['/a']).returns nil file.expects(:perform_recursion).with(sources['/b']).returns [] file.expects(:perform_recursion).with(sources['/c']).returns [data] file.expects(:perform_recursion).with(sources['/d']).never file.expects(:newchild).with("foobar").returns @resource file.recurse_remote({}) end end describe "and :sourceselect is set to :all" do before do file[:sourceselect] = :all end it "should return every found file that is not in a previous source" do klass = Puppet::FileServing::Metadata file[:source] = abs_path = %w{/a /b /c /d}.map {|f| File.expand_path(f) } file.stubs(:newchild).returns @resource one = [klass.new(abs_path[0], :relative_path => "a")] file.expects(:perform_recursion).with(sources['/a']).returns one file.expects(:newchild).with("a").returns @resource two = [klass.new(abs_path[1], :relative_path => "a"), klass.new(abs_path[1], :relative_path => "b")] file.expects(:perform_recursion).with(sources['/b']).returns two file.expects(:newchild).with("b").returns @resource three = [klass.new(abs_path[2], :relative_path => "a"), klass.new(abs_path[2], :relative_path => "c")] file.expects(:perform_recursion).with(sources['/c']).returns three file.expects(:newchild).with("c").returns @resource file.expects(:perform_recursion).with(sources['/d']).returns [] file.recurse_remote({}) end end end end describe "#perform_recursion" do it "should use Metadata to do its recursion" do Puppet::FileServing::Metadata.indirection.expects(:search) file.perform_recursion(file[:path]) end it "should use the provided path as the key to the search" do Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| key == "/foo" } file.perform_recursion("/foo") end it "should return the results of the metadata search" do Puppet::FileServing::Metadata.indirection.expects(:search).returns "foobar" file.perform_recursion(file[:path]).should == "foobar" end it "should pass its recursion value to the search" do file[:recurse] = true Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } file.perform_recursion(file[:path]) end it "should pass true if recursion is remote" do file[:recurse] = :remote Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } file.perform_recursion(file[:path]) end it "should pass its recursion limit value to the search" do file[:recurselimit] = 10 Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurselimit] == 10 } file.perform_recursion(file[:path]) end it "should configure the search to ignore or manage links" do file[:links] = :manage Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:links] == :manage } file.perform_recursion(file[:path]) end it "should pass its 'ignore' setting to the search if it has one" do file[:ignore] = %w{.svn CVS} Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} } file.perform_recursion(file[:path]) end end describe "#remove_existing" do it "should do nothing if the file doesn't exist" do file.remove_existing(:file).should == nil end it "should fail if it can't backup the file" do file.stubs(:stat).returns stub('stat') file.stubs(:perform_backup).returns false expect { file.remove_existing(:file) }.to raise_error(Puppet::Error, /Could not back up; will not replace/) end it "should not do anything if the file is already the right type and not a link" do file.stubs(:stat).returns stub('stat', :ftype => 'file') file.remove_existing(:file).should == nil end it "should not remove directories and should not invalidate the stat unless force is set" do # Actually call stat to set @needs_stat to nil file.stat file.stubs(:stat).returns stub('stat', :ftype => 'directory') file.remove_existing(:file) file.instance_variable_get(:@stat).should == nil @logs.should be_any {|log| log.level == :notice and log.message =~ /Not removing directory; use 'force' to override/} end it "should remove a directory if force is set" do file[:force] = true file.stubs(:stat).returns stub('stat', :ftype => 'directory') FileUtils.expects(:rmtree).with(file[:path]) file.remove_existing(:file).should == true end it "should remove an existing file" do file.stubs(:perform_backup).returns true FileUtils.touch(path) file.remove_existing(:directory).should == true File.exists?(file[:path]).should == false end it "should remove an existing link", :unless => Puppet.features.microsoft_windows? do file.stubs(:perform_backup).returns true target = tmpfile('link_target') FileUtils.touch(target) FileUtils.symlink(target, path) file[:target] = target file.remove_existing(:directory).should == true File.exists?(file[:path]).should == false end it "should fail if the file is not a file, link, or directory" do file.stubs(:stat).returns stub('stat', :ftype => 'socket') expect { file.remove_existing(:file) }.to raise_error(Puppet::Error, /Could not back up files of type socket/) end it "should invalidate the existing stat of the file" do # Actually call stat to set @needs_stat to nil file.stat file.stubs(:stat).returns stub('stat', :ftype => 'file') File.stubs(:unlink) file.remove_existing(:directory).should == true file.instance_variable_get(:@stat).should == :needs_stat end end describe "#retrieve" do it "should copy the source values if the 'source' parameter is set" do file[:source] = File.expand_path('/foo/bar') file.parameter(:source).expects(:copy_source_values) file.retrieve end end describe "#should_be_file?" do it "should have a method for determining if the file should be a normal file" do file.must respond_to(:should_be_file?) end it "should be a file if :ensure is set to :file" do file[:ensure] = :file file.must be_should_be_file end it "should be a file if :ensure is set to :present and the file exists as a normal file" do file.stubs(:stat).returns(mock('stat', :ftype => "file")) file[:ensure] = :present file.must be_should_be_file end it "should not be a file if :ensure is set to something other than :file" do file[:ensure] = :directory file.must_not be_should_be_file end it "should not be a file if :ensure is set to :present and the file exists but is not a normal file" do file.stubs(:stat).returns(mock('stat', :ftype => "directory")) file[:ensure] = :present file.must_not be_should_be_file end it "should be a file if :ensure is not set and :content is" do file[:content] = "foo" file.must be_should_be_file end it "should be a file if neither :ensure nor :content is set but the file exists as a normal file" do file.stubs(:stat).returns(mock("stat", :ftype => "file")) file.must be_should_be_file end it "should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file" do file.stubs(:stat).returns(mock("stat", :ftype => "directory")) file.must_not be_should_be_file end end describe "#stat", :unless => Puppet.features.microsoft_windows? do before do target = tmpfile('link_target') FileUtils.touch(target) FileUtils.symlink(target, path) file[:target] = target file[:links] = :manage # so we always use :lstat end it "should stat the target if it is following links" do file[:links] = :follow file.stat.ftype.should == 'file' end it "should stat the link if is it not following links" do file[:links] = :manage file.stat.ftype.should == 'link' end it "should return nil if the file does not exist" do file[:path] = '/foo/bar/baz/non-existent' file.stat.should be_nil end it "should return nil if the file cannot be stat'ed" do dir = tmpfile('link_test_dir') child = File.join(dir, 'some_file') Dir.mkdir(dir) File.chmod(0, dir) file[:path] = child file.stat.should be_nil # chmod it back so we can clean it up File.chmod(0777, dir) end it "should return the stat instance" do file.stat.should be_a(File::Stat) end it "should cache the stat instance" do file.stat.should equal(file.stat) end end describe "#write" do it "should propagate failures encountered when renaming the temporary file" do File.stubs(:open) File.expects(:rename).raises ArgumentError file[:backup] = 'puppet' file.stubs(:validate_checksum?).returns(false) property = stub('content_property', :actual_content => "something", :length => "something".length) file.stubs(:property).with(:content).returns(property) lambda { file.write(:content) }.should raise_error(Puppet::Error) end it "should delegate writing to the content property" do filehandle = stub_everything 'fh' File.stubs(:open).yields(filehandle) File.stubs(:rename) property = stub('content_property', :actual_content => "something", :length => "something".length) file[:backup] = 'puppet' file.stubs(:validate_checksum?).returns(false) file.stubs(:property).with(:content).returns(property) property.expects(:write).with(filehandle) file.write(:content) end describe "when validating the checksum" do before { file.stubs(:validate_checksum?).returns(true) } it "should fail if the checksum parameter and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b', :sum_file => 'checksum_b') file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') file.stubs(:property).with(:content).returns(property) lambda { file.write :NOTUSED }.should raise_error(Puppet::Error) end end describe "when not validating the checksum" do before { file.stubs(:validate_checksum?).returns(false) } it "should not fail if the checksum property and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b') file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') file.stubs(:property).with(:content).returns(property) lambda { file.write :NOTUSED }.should_not raise_error(Puppet::Error) end end end describe "#fail_if_checksum_is_wrong" do it "should fail if the checksum of the file doesn't match the expected one" do expect do file.instance_eval do parameter(:checksum).stubs(:sum_file).returns('wrong!!') fail_if_checksum_is_wrong(self[:path], 'anything!') end end.to raise_error(Puppet::Error, /File written to disk did not match checksum/) end it "should not fail if the checksum is correct" do file.instance_eval do parameter(:checksum).stubs(:sum_file).returns('anything!') fail_if_checksum_is_wrong(self[:path], 'anything!').should == nil end end it "should not fail if the checksum is absent" do file.instance_eval do parameter(:checksum).stubs(:sum_file).returns(nil) fail_if_checksum_is_wrong(self[:path], 'anything!').should == nil end end end describe "#write_content" do it "should delegate writing the file to the content property" do io = stub('io') file[:content] = "some content here" file.property(:content).expects(:write).with(io) file.send(:write_content, io) end end describe "#write_temporary_file?" do it "should be true if the file has specified content" do file[:content] = 'some content' file.send(:write_temporary_file?).should be_true end it "should be true if the file has specified source" do file[:source] = File.expand_path('/tmp/foo') file.send(:write_temporary_file?).should be_true end it "should be false if the file has neither content nor source" do file.send(:write_temporary_file?).should be_false end end describe "#property_fix" do { :mode => 0777, :owner => 'joeuser', :group => 'joeusers', :seluser => 'seluser', :selrole => 'selrole', :seltype => 'seltype', :selrange => 'selrange' }.each do |name,value| it "should sync the #{name} property if it's not in sync" do file[name] = value prop = file.property(name) prop.expects(:retrieve) prop.expects(:safe_insync?).returns false prop.expects(:sync) file.send(:property_fix) end end end describe "when autorequiring" do describe "target" do it "should require file resource when specified with the target property" do file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory) link = described_class.new(:path => File.expand_path("/bar"), :ensure => :symlink, :target => File.expand_path("/foo")) catalog.add_resource file catalog.add_resource link reqs = link.autorequire reqs.size.must == 1 reqs[0].source.must == file reqs[0].target.must == link end it "should require file resource when specified with the ensure property" do file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory) link = described_class.new(:path => File.expand_path("/bar"), :ensure => File.expand_path("/foo")) catalog.add_resource file catalog.add_resource link reqs = link.autorequire reqs.size.must == 1 reqs[0].source.must == file reqs[0].target.must == link end it "should not require target if target is not managed" do link = described_class.new(:path => File.expand_path('/foo'), :ensure => :symlink, :target => '/bar') catalog.add_resource link link.autorequire.size.should == 0 end end describe "directories" do it "should autorequire its parent directory" do dir = described_class.new(:path => File.dirname(path)) catalog.add_resource file catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do dir = described_class.new(:path => File.dirname(path)) grandparent = described_class.new(:path => File.dirname(File.dirname(path))) catalog.add_resource file catalog.add_resource dir catalog.add_resource grandparent reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file[:path] = File.expand_path('/') catalog.add_resource file file.autorequire.should be_empty end describe "on Windows systems", :if => Puppet.features.microsoft_windows? do describe "when using UNC filenames" do it "should autorequire its parent directory" do file[:path] = '//server/foo/bar/baz' dir = described_class.new(:path => "//server/foo/bar") catalog.add_resource file catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do file = described_class.new(:path => "//server/foo/bar/baz/qux") dir = described_class.new(:path => "//server/foo/bar/baz") grandparent = described_class.new(:path => "//server/foo/bar") catalog.add_resource file catalog.add_resource dir catalog.add_resource grandparent reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do file = described_class.new(:path => "//server/foo/bar/baz/qux") catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file = described_class.new(:path => "//server/foo") catalog.add_resource file puts file.autorequire file.autorequire.should be_empty end end end end end describe "when managing links" do require 'tempfile' if @real_posix describe "on POSIX systems" do before do Dir.mkdir(path) @target = File.join(path, "target") @link = File.join(path, "link") File.open(@target, "w", 0644) { |f| f.puts "yayness" } File.symlink(@target, @link) file[:path] = @link file[:mode] = 0755 catalog.add_resource file end it "should default to managing the link" do catalog.apply # I convert them to strings so they display correctly if there's an error. (File.stat(@target).mode & 007777).to_s(8).should == '644' end it "should be able to follow links" do file[:links] = :follow catalog.apply (File.stat(@target).mode & 007777).to_s(8).should == '755' end end else # @real_posix # should recode tests using expectations instead of using the filesystem end describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should refuse to work with links" end end describe "when using source" do before do file[:source] = File.expand_path('/one') end Puppet::Type::File::ParameterChecksum.value_collection.values.reject {|v| v == :none}.each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do file[:checksum] = checksum_type end it 'should validate' do lambda { file.validate }.should_not raise_error end end end describe "with checksum 'none'" do before do file[:checksum] = :none end it 'should raise an exception when validating' do lambda { file.validate }.should raise_error(/You cannot specify source when using checksum 'none'/) end end end describe "when using content" do before do file[:content] = 'file contents' end (Puppet::Type::File::ParameterChecksum.value_collection.values - SOURCE_ONLY_CHECKSUMS).each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do file[:checksum] = checksum_type end it 'should validate' do lambda { file.validate }.should_not raise_error end end end SOURCE_ONLY_CHECKSUMS.each do |checksum_type| describe "with checksum '#{checksum_type}'" do it 'should raise an exception when validating' do file[:checksum] = checksum_type lambda { file.validate }.should raise_error(/You cannot specify content when using checksum '#{checksum_type}'/) end end end end describe "when auditing" do before :each do # to prevent the catalog from trying to write state.yaml Puppet::Util::Storage.stubs(:store) end it "should not fail if creating a new file if group is not set" do file = described_class.new(:path => path, :audit => 'all', :content => 'content') catalog.add_resource(file) report = catalog.apply.report report.resource_statuses["File[#{path}]"].should_not be_failed File.read(path).should == 'content' end it "should not log errors if creating a new file with ensure present and no content" do file[:audit] = 'content' file[:ensure] = 'present' catalog.add_resource(file) catalog.apply File.should be_exist(path) @logs.should_not be_any {|l| l.level != :notice } end end describe "when specifying both source and checksum" do it 'should use the specified checksum when source is first' do file[:source] = File.expand_path('/foo') file[:checksum] = :md5lite file[:checksum].should == :md5lite end it 'should use the specified checksum when source is last' do file[:checksum] = :md5lite file[:source] = File.expand_path('/foo') file[:checksum].should == :md5lite end end describe "when validating" do [[:source, :target], [:source, :content], [:target, :content]].each do |prop1,prop2| it "should fail if both #{prop1} and #{prop2} are specified" do file[prop1] = prop1 == :source ? File.expand_path("prop1 value") : "prop1 value" file[prop2] = "prop2 value" expect do file.validate end.to raise_error(Puppet::Error, /You cannot specify more than one of/) end end end end diff --git a/spec/unit/type/filebucket_spec.rb b/spec/unit/type/filebucket_spec.rb index 5bfb834c9..5c468ccd0 100755 --- a/spec/unit/type/filebucket_spec.rb +++ b/spec/unit/type/filebucket_spec.rb @@ -1,103 +1,103 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:filebucket) do include PuppetSpec::Files describe "when validating attributes" do %w{name server port path}.each do |attr| it "should have a '#{attr}' parameter" do Puppet::Type.type(:filebucket).attrtype(attr.intern).should == :param end end it "should have its 'name' attribute set as its namevar" do Puppet::Type.type(:filebucket).key_attributes.should == [:name] end end it "should use the clientbucketdir as the path by default path" do Puppet.settings[:clientbucketdir] = "/my/bucket" Puppet::Type.type(:filebucket).new(:name => "main")[:path].should == Puppet[:clientbucketdir] end it "should use the masterport as the path by default port" do Puppet.settings[:masterport] = 50 Puppet::Type.type(:filebucket).new(:name => "main")[:port].should == Puppet[:masterport] end it "should use the server as the path by default server" do Puppet.settings[:server] = "myserver" Puppet::Type.type(:filebucket).new(:name => "main")[:server].should == Puppet[:server] end it "be local by default" do bucket = Puppet::Type.type(:filebucket).new :name => "main" bucket.bucket.should be_local end describe "path" do def bucket(hash) Puppet::Type.type(:filebucket).new({:name => 'main'}.merge(hash)) end it "should accept false as a value" do expect { bucket(:path => false) }.not_to raise_error end it "should accept true as a value" do expect { bucket(:path => true) }.not_to raise_error end it "should fail when given an array of values" do expect { bucket(:path => ['one', 'two']) }. to raise_error Puppet::Error, /only have one filebucket path/ end %w{one ../one one/two}.each do |path| it "should fail if given a relative path of #{path.inspect}" do expect { bucket(:path => path) }. to raise_error Puppet::Error, /Filebucket paths must be absolute/ end end it "should succeed if given an absolute path" do expect { bucket(:path => make_absolute('/tmp/bucket')) }.not_to raise_error end it "not be local if path is false" do bucket(:path => false).bucket.should_not be_local end it "be local if both a path and a server are specified" do bucket(:server => "puppet", :path => make_absolute("/my/path")).bucket.should be_local end end describe "when creating the filebucket" do before do @bucket = stub 'bucket', :name= => nil end it "should use any provided path" do path = make_absolute("/foo/bar") bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => path Puppet::FileBucket::Dipper.expects(:new).with(:Path => path).returns @bucket bucket.bucket end it "should use any provided server and port" do bucket = Puppet::Type.type(:filebucket).new :name => "main", :server => "myserv", :port => "myport", :path => false Puppet::FileBucket::Dipper.expects(:new).with(:Server => "myserv", :Port => "myport").returns @bucket bucket.bucket end it "should use the default server if the path is unset and no server is provided" do Puppet.settings[:server] = "myserv" bucket = Puppet::Type.type(:filebucket).new :name => "main", :path => false Puppet::FileBucket::Dipper.expects(:new).with { |args| args[:Server] == "myserv" }.returns @bucket bucket.bucket end end end diff --git a/spec/unit/type/group_spec.rb b/spec/unit/type/group_spec.rb index c7fa1c724..034c2850f 100755 --- a/spec/unit/type/group_spec.rb +++ b/spec/unit/type/group_spec.rb @@ -1,54 +1,54 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:group) do before do @class = Puppet::Type.type(:group) end it "should have a system_groups feature" do @class.provider_feature(:system_groups).should_not be_nil end describe "when validating attributes" do [:name, :allowdupe].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:ensure, :gid].each do |param| it "should have a #{param} property" do @class.attrtype(param).should == :property end end it "should convert gids provided as strings into integers" do @class.new(:name => "foo", :gid => "15")[:gid].should == 15 end it "should accepts gids provided as integers" do @class.new(:name => "foo", :gid => 15)[:gid].should == 15 end end it "should have a boolean method for determining if duplicates are allowed" do @class.new(:name => "foo").should respond_to "allowdupe?" end it "should have a boolean method for determining if system groups are allowed" do @class.new(:name => "foo").should respond_to "system?" end it "should call 'create' to create the group" do group = @class.new(:name => "foo", :ensure => :present) group.provider.expects(:create) group.parameter(:ensure).sync end it "should call 'delete' to remove the group" do group = @class.new(:name => "foo", :ensure => :absent) group.provider.expects(:delete) group.parameter(:ensure).sync end end diff --git a/spec/unit/type/host_spec.rb b/spec/unit/type/host_spec.rb index 3cbe56943..a8a312e54 100755 --- a/spec/unit/type/host_spec.rb +++ b/spec/unit/type/host_spec.rb @@ -1,656 +1,656 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' host = Puppet::Type.type(:host) describe host do before do @class = host @catalog = Puppet::Resource::Catalog.new @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider ].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:ip, :target, :host_aliases, :comment, :ensure].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end it "should have a list host_aliases" do @class.attrclass(:host_aliases).ancestors.should be_include(Puppet::Property::OrderedList) end end describe "when validating values" do it "should support present as a value for ensure" do proc { @class.new(:name => "foo", :ensure => :present) }.should_not raise_error end it "should support absent as a value for ensure" do proc { @class.new(:name => "foo", :ensure => :absent) }.should_not raise_error end it "should accept IPv4 addresses" do proc { @class.new(:name => "foo", :ip => '10.96.0.1') }.should_not raise_error end it "should accept long IPv6 addresses" do # Taken from wikipedia article about ipv6 proc { @class.new(:name => "foo", :ip => '2001:0db8:85a3:08d3:1319:8a2e:0370:7344') }.should_not raise_error end it "should accept one host_alias" do proc { @class.new(:name => "foo", :host_aliases => 'alias1') }.should_not raise_error end it "should accept multiple host_aliases" do proc { @class.new(:name => "foo", :host_aliases => [ 'alias1', 'alias2' ]) }.should_not raise_error end it "should accept shortened IPv6 addresses" do proc { @class.new(:name => "foo", :ip => '2001:db8:0:8d3:0:8a2e:70:7344') }.should_not raise_error proc { @class.new(:name => "foo", :ip => '::ffff:192.0.2.128') }.should_not raise_error proc { @class.new(:name => "foo", :ip => '::1') }.should_not raise_error end it "should not accept malformed IPv4 addresses like 192.168.0.300" do proc { @class.new(:name => "foo", :ip => '192.168.0.300') }.should raise_error end it "should reject over-long IPv4 addresses" do expect { @class.new(:name => "foo", :ip => '10.10.10.10.10') }.to raise_error end it "should not accept malformed IP addresses like 2001:0dg8:85a3:08d3:1319:8a2e:0370:7344" do proc { @class.new(:name => "foo", :ip => '2001:0dg8:85a3:08d3:1319:8a2e:0370:7344') }.should raise_error end # Assorted, annotated IPv6 passes. ["::1", # loopback, compressed, non-routable "::", # unspecified, compressed, non-routable "0:0:0:0:0:0:0:1", # loopback, full "0:0:0:0:0:0:0:0", # unspecified, full "2001:DB8:0:0:8:800:200C:417A", # unicast, full "FF01:0:0:0:0:0:0:101", # multicast, full "2001:DB8::8:800:200C:417A", # unicast, compressed "FF01::101", # multicast, compressed # Some more test cases that should pass. "2001:0000:1234:0000:0000:C1C0:ABCD:0876", "3ffe:0b00:0000:0000:0001:0000:0000:000a", "FF02:0000:0000:0000:0000:0000:0000:0001", "0000:0000:0000:0000:0000:0000:0000:0001", "0000:0000:0000:0000:0000:0000:0000:0000", # Assorted valid, compressed IPv6 addresses. "2::10", "ff02::1", "fe80::", "2002::", "2001:db8::", "2001:0db8:1234::", "::ffff:0:0", "::1", "1:2:3:4:5:6:7:8", "1:2:3:4:5:6::8", "1:2:3:4:5::8", "1:2:3:4::8", "1:2:3::8", "1:2::8", "1::8", "1::2:3:4:5:6:7", "1::2:3:4:5:6", "1::2:3:4:5", "1::2:3:4", "1::2:3", "1::8", "::2:3:4:5:6:7:8", "::2:3:4:5:6:7", "::2:3:4:5:6", "::2:3:4:5", "::2:3:4", "::2:3", "::8", "1:2:3:4:5:6::", "1:2:3:4:5::", "1:2:3:4::", "1:2:3::", "1:2::", "1::", "1:2:3:4:5::7:8", "1:2:3:4::7:8", "1:2:3::7:8", "1:2::7:8", "1::7:8", # IPv4 addresses as dotted-quads "1:2:3:4:5:6:1.2.3.4", "1:2:3:4:5::1.2.3.4", "1:2:3:4::1.2.3.4", "1:2:3::1.2.3.4", "1:2::1.2.3.4", "1::1.2.3.4", "1:2:3:4::5:1.2.3.4", "1:2:3::5:1.2.3.4", "1:2::5:1.2.3.4", "1::5:1.2.3.4", "1::5:11.22.33.44", "fe80::217:f2ff:254.7.237.98", "::ffff:192.168.1.26", "::ffff:192.168.1.1", "0:0:0:0:0:0:13.1.68.3", # IPv4-compatible IPv6 address, full, deprecated "0:0:0:0:0:FFFF:129.144.52.38", # IPv4-mapped IPv6 address, full "::13.1.68.3", # IPv4-compatible IPv6 address, compressed, deprecated "::FFFF:129.144.52.38", # IPv4-mapped IPv6 address, compressed "fe80:0:0:0:204:61ff:254.157.241.86", "fe80::204:61ff:254.157.241.86", "::ffff:12.34.56.78", "::ffff:192.0.2.128", # this is OK, since there's a single zero digit in IPv4 "fe80:0000:0000:0000:0204:61ff:fe9d:f156", "fe80:0:0:0:204:61ff:fe9d:f156", "fe80::204:61ff:fe9d:f156", "::1", "fe80::", "fe80::1", "::ffff:c000:280", # Additional test cases from http://rt.cpan.org/Public/Bug/Display.html?id=50693 "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3:0:0:8a2e:370:7334", "2001:db8:85a3::8a2e:370:7334", "2001:0db8:0000:0000:0000:0000:1428:57ab", "2001:0db8:0000:0000:0000::1428:57ab", "2001:0db8:0:0:0:0:1428:57ab", "2001:0db8:0:0::1428:57ab", "2001:0db8::1428:57ab", "2001:db8::1428:57ab", "0000:0000:0000:0000:0000:0000:0000:0001", "::1", "::ffff:0c22:384e", "2001:0db8:1234:0000:0000:0000:0000:0000", "2001:0db8:1234:ffff:ffff:ffff:ffff:ffff", "2001:db8:a::123", "fe80::", "1111:2222:3333:4444:5555:6666:7777:8888", "1111:2222:3333:4444:5555:6666:7777::", "1111:2222:3333:4444:5555:6666::", "1111:2222:3333:4444:5555::", "1111:2222:3333:4444::", "1111:2222:3333::", "1111:2222::", "1111::", "1111:2222:3333:4444:5555:6666::8888", "1111:2222:3333:4444:5555::8888", "1111:2222:3333:4444::8888", "1111:2222:3333::8888", "1111:2222::8888", "1111::8888", "::8888", "1111:2222:3333:4444:5555::7777:8888", "1111:2222:3333:4444::7777:8888", "1111:2222:3333::7777:8888", "1111:2222::7777:8888", "1111::7777:8888", "::7777:8888", "1111:2222:3333:4444::6666:7777:8888", "1111:2222:3333::6666:7777:8888", "1111:2222::6666:7777:8888", "1111::6666:7777:8888", "::6666:7777:8888", "1111:2222:3333::5555:6666:7777:8888", "1111:2222::5555:6666:7777:8888", "1111::5555:6666:7777:8888", "::5555:6666:7777:8888", "1111:2222::4444:5555:6666:7777:8888", "1111::4444:5555:6666:7777:8888", "::4444:5555:6666:7777:8888", "1111::3333:4444:5555:6666:7777:8888", "::3333:4444:5555:6666:7777:8888", "::2222:3333:4444:5555:6666:7777:8888", "1111:2222:3333:4444:5555:6666:123.123.123.123", "1111:2222:3333:4444:5555::123.123.123.123", "1111:2222:3333:4444::123.123.123.123", "1111:2222:3333::123.123.123.123", "1111:2222::123.123.123.123", "1111::123.123.123.123", "::123.123.123.123", "1111:2222:3333:4444::6666:123.123.123.123", "1111:2222:3333::6666:123.123.123.123", "1111:2222::6666:123.123.123.123", "1111::6666:123.123.123.123", "::6666:123.123.123.123", "1111:2222:3333::5555:6666:123.123.123.123", "1111:2222::5555:6666:123.123.123.123", "1111::5555:6666:123.123.123.123", "::5555:6666:123.123.123.123", "1111:2222::4444:5555:6666:123.123.123.123", "1111::4444:5555:6666:123.123.123.123", "::4444:5555:6666:123.123.123.123", "1111::3333:4444:5555:6666:123.123.123.123", "::2222:3333:4444:5555:6666:123.123.123.123", # Playing with combinations of "0" and "::"; these are all sytactically # correct, but are bad form because "0" adjacent to "::" should be # combined into "::" "::0:0:0:0:0:0:0", "::0:0:0:0:0:0", "::0:0:0:0:0", "::0:0:0:0", "::0:0:0", "::0:0", "::0", "0:0:0:0:0:0:0::", "0:0:0:0:0:0::", "0:0:0:0:0::", "0:0:0:0::", "0:0:0::", "0:0::", "0::", # Additional cases: http://crisp.tweakblogs.net/blog/2031/ipv6-validation-%28and-caveats%29.html "0:a:b:c:d:e:f::", "::0:a:b:c:d:e:f", # syntactically correct, but bad form (::0:... could be combined) "a:b:c:d:e:f:0::", ].each do |ip| it "should accept #{ip.inspect} as an IPv6 address" do expect { @class.new(:name => "foo", :ip => ip) }.not_to raise_error end end # ...aaaand, some failure cases. [":", "02001:0000:1234:0000:0000:C1C0:ABCD:0876", # extra 0 not allowed! "2001:0000:1234:0000:00001:C1C0:ABCD:0876", # extra 0 not allowed! "2001:0000:1234:0000:0000:C1C0:ABCD:0876 0", # junk after valid address "2001:0000:1234: 0000:0000:C1C0:ABCD:0876", # internal space "3ffe:0b00:0000:0001:0000:0000:000a", # seven segments "FF02:0000:0000:0000:0000:0000:0000:0000:0001", # nine segments "3ffe:b00::1::a", # double "::" "::1111:2222:3333:4444:5555:6666::", # double "::" "1:2:3::4:5::7:8", # Double "::" "12345::6:7:8", # IPv4 embedded, but bad... "1::5:400.2.3.4", "1::5:260.2.3.4", "1::5:256.2.3.4", "1::5:1.256.3.4", "1::5:1.2.256.4", "1::5:1.2.3.256", "1::5:300.2.3.4", "1::5:1.300.3.4", "1::5:1.2.300.4", "1::5:1.2.3.300", "1::5:900.2.3.4", "1::5:1.900.3.4", "1::5:1.2.900.4", "1::5:1.2.3.900", "1::5:300.300.300.300", "1::5:3000.30.30.30", "1::400.2.3.4", "1::260.2.3.4", "1::256.2.3.4", "1::1.256.3.4", "1::1.2.256.4", "1::1.2.3.256", "1::300.2.3.4", "1::1.300.3.4", "1::1.2.300.4", "1::1.2.3.300", "1::900.2.3.4", "1::1.900.3.4", "1::1.2.900.4", "1::1.2.3.900", "1::300.300.300.300", "1::3000.30.30.30", "::400.2.3.4", "::260.2.3.4", "::256.2.3.4", "::1.256.3.4", "::1.2.256.4", "::1.2.3.256", "::300.2.3.4", "::1.300.3.4", "::1.2.300.4", "::1.2.3.300", "::900.2.3.4", "::1.900.3.4", "::1.2.900.4", "::1.2.3.900", "::300.300.300.300", "::3000.30.30.30", "2001:1:1:1:1:1:255Z255X255Y255", # garbage instead of "." in IPv4 "::ffff:192x168.1.26", # ditto "::ffff:2.3.4", "::ffff:257.1.2.3", "1.2.3.4:1111:2222:3333:4444::5555", "1.2.3.4:1111:2222:3333::5555", "1.2.3.4:1111:2222::5555", "1.2.3.4:1111::5555", "1.2.3.4::5555", "1.2.3.4::", # Testing IPv4 addresses represented as dotted-quads Leading zero's in # IPv4 addresses not allowed: some systems treat the leading "0" in # ".086" as the start of an octal number Update: The BNF in RFC-3986 # explicitly defines the dec-octet (for IPv4 addresses) not to have a # leading zero "fe80:0000:0000:0000:0204:61ff:254.157.241.086", "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4", "1111:2222:3333:4444:5555:6666:00.00.00.00", "1111:2222:3333:4444:5555:6666:000.000.000.000", "1111:2222:3333:4444:5555:6666:256.256.256.256", "1111:2222:3333:4444::5555:", "1111:2222:3333::5555:", "1111:2222::5555:", "1111::5555:", "::5555:", ":::", "1111:", ":", ":1111:2222:3333:4444::5555", ":1111:2222:3333::5555", ":1111:2222::5555", ":1111::5555", ":::5555", ":::", # Additional test cases from http://rt.cpan.org/Public/Bug/Display.html?id=50693 "123", "ldkfj", "2001::FFD3::57ab", "2001:db8:85a3::8a2e:37023:7334", "2001:db8:85a3::8a2e:370k:7334", "1:2:3:4:5:6:7:8:9", "1::2::3", "1:::3:4:5", "1:2:3::4:5:6:7:8:9", # Invalid data "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX", # Too many components "1111:2222:3333:4444:5555:6666:7777:8888:9999", "1111:2222:3333:4444:5555:6666:7777:8888::", "::2222:3333:4444:5555:6666:7777:8888:9999", # Too few components "1111:2222:3333:4444:5555:6666:7777", "1111:2222:3333:4444:5555:6666", "1111:2222:3333:4444:5555", "1111:2222:3333:4444", "1111:2222:3333", "1111:2222", "1111", # Missing : "11112222:3333:4444:5555:6666:7777:8888", "1111:22223333:4444:5555:6666:7777:8888", "1111:2222:33334444:5555:6666:7777:8888", "1111:2222:3333:44445555:6666:7777:8888", "1111:2222:3333:4444:55556666:7777:8888", "1111:2222:3333:4444:5555:66667777:8888", "1111:2222:3333:4444:5555:6666:77778888", # Missing : intended for :: "1111:2222:3333:4444:5555:6666:7777:8888:", "1111:2222:3333:4444:5555:6666:7777:", "1111:2222:3333:4444:5555:6666:", "1111:2222:3333:4444:5555:", "1111:2222:3333:4444:", "1111:2222:3333:", "1111:2222:", "1111:", ":", ":8888", ":7777:8888", ":6666:7777:8888", ":5555:6666:7777:8888", ":4444:5555:6666:7777:8888", ":3333:4444:5555:6666:7777:8888", ":2222:3333:4444:5555:6666:7777:8888", ":1111:2222:3333:4444:5555:6666:7777:8888", # ::: ":::2222:3333:4444:5555:6666:7777:8888", "1111:::3333:4444:5555:6666:7777:8888", "1111:2222:::4444:5555:6666:7777:8888", "1111:2222:3333:::5555:6666:7777:8888", "1111:2222:3333:4444:::6666:7777:8888", "1111:2222:3333:4444:5555:::7777:8888", "1111:2222:3333:4444:5555:6666:::8888", "1111:2222:3333:4444:5555:6666:7777:::", # Double ::", "::2222::4444:5555:6666:7777:8888", "::2222:3333::5555:6666:7777:8888", "::2222:3333:4444::6666:7777:8888", "::2222:3333:4444:5555::7777:8888", "::2222:3333:4444:5555:7777::8888", "::2222:3333:4444:5555:7777:8888::", "1111::3333::5555:6666:7777:8888", "1111::3333:4444::6666:7777:8888", "1111::3333:4444:5555::7777:8888", "1111::3333:4444:5555:6666::8888", "1111::3333:4444:5555:6666:7777::", "1111:2222::4444::6666:7777:8888", "1111:2222::4444:5555::7777:8888", "1111:2222::4444:5555:6666::8888", "1111:2222::4444:5555:6666:7777::", "1111:2222:3333::5555::7777:8888", "1111:2222:3333::5555:6666::8888", "1111:2222:3333::5555:6666:7777::", "1111:2222:3333:4444::6666::8888", "1111:2222:3333:4444::6666:7777::", "1111:2222:3333:4444:5555::7777::", # Too many components" "1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4", "1111:2222:3333:4444:5555:6666:7777:1.2.3.4", "1111:2222:3333:4444:5555:6666::1.2.3.4", "::2222:3333:4444:5555:6666:7777:1.2.3.4", "1111:2222:3333:4444:5555:6666:1.2.3.4.5", # Too few components "1111:2222:3333:4444:5555:1.2.3.4", "1111:2222:3333:4444:1.2.3.4", "1111:2222:3333:1.2.3.4", "1111:2222:1.2.3.4", "1111:1.2.3.4", # Missing : "11112222:3333:4444:5555:6666:1.2.3.4", "1111:22223333:4444:5555:6666:1.2.3.4", "1111:2222:33334444:5555:6666:1.2.3.4", "1111:2222:3333:44445555:6666:1.2.3.4", "1111:2222:3333:4444:55556666:1.2.3.4", "1111:2222:3333:4444:5555:66661.2.3.4", # Missing . "1111:2222:3333:4444:5555:6666:255255.255.255", "1111:2222:3333:4444:5555:6666:255.255255.255", "1111:2222:3333:4444:5555:6666:255.255.255255", # Missing : intended for :: ":1.2.3.4", ":6666:1.2.3.4", ":5555:6666:1.2.3.4", ":4444:5555:6666:1.2.3.4", ":3333:4444:5555:6666:1.2.3.4", ":2222:3333:4444:5555:6666:1.2.3.4", ":1111:2222:3333:4444:5555:6666:1.2.3.4", # ::: ":::2222:3333:4444:5555:6666:1.2.3.4", "1111:::3333:4444:5555:6666:1.2.3.4", "1111:2222:::4444:5555:6666:1.2.3.4", "1111:2222:3333:::5555:6666:1.2.3.4", "1111:2222:3333:4444:::6666:1.2.3.4", "1111:2222:3333:4444:5555:::1.2.3.4", # Double :: "::2222::4444:5555:6666:1.2.3.4", "::2222:3333::5555:6666:1.2.3.4", "::2222:3333:4444::6666:1.2.3.4", "::2222:3333:4444:5555::1.2.3.4", "1111::3333::5555:6666:1.2.3.4", "1111::3333:4444::6666:1.2.3.4", "1111::3333:4444:5555::1.2.3.4", "1111:2222::4444::6666:1.2.3.4", "1111:2222::4444:5555::1.2.3.4", "1111:2222:3333::5555::1.2.3.4", # Missing parts "::.", "::..", "::...", "::1...", "::1.2..", "::1.2.3.", "::.2..", "::.2.3.", "::.2.3.4", "::..3.", "::..3.4", "::...4", # Extra : in front ":1111:2222:3333:4444:5555:6666:7777::", ":1111:2222:3333:4444:5555:6666::", ":1111:2222:3333:4444:5555::", ":1111:2222:3333:4444::", ":1111:2222:3333::", ":1111:2222::", ":1111::", ":::", ":1111:2222:3333:4444:5555:6666::8888", ":1111:2222:3333:4444:5555::8888", ":1111:2222:3333:4444::8888", ":1111:2222:3333::8888", ":1111:2222::8888", ":1111::8888", ":::8888", ":1111:2222:3333:4444:5555::7777:8888", ":1111:2222:3333:4444::7777:8888", ":1111:2222:3333::7777:8888", ":1111:2222::7777:8888", ":1111::7777:8888", ":::7777:8888", ":1111:2222:3333:4444::6666:7777:8888", ":1111:2222:3333::6666:7777:8888", ":1111:2222::6666:7777:8888", ":1111::6666:7777:8888", ":::6666:7777:8888", ":1111:2222:3333::5555:6666:7777:8888", ":1111:2222::5555:6666:7777:8888", ":1111::5555:6666:7777:8888", ":::5555:6666:7777:8888", ":1111:2222::4444:5555:6666:7777:8888", ":1111::4444:5555:6666:7777:8888", ":::4444:5555:6666:7777:8888", ":1111::3333:4444:5555:6666:7777:8888", ":::3333:4444:5555:6666:7777:8888", ":::2222:3333:4444:5555:6666:7777:8888", ":1111:2222:3333:4444:5555:6666:1.2.3.4", ":1111:2222:3333:4444:5555::1.2.3.4", ":1111:2222:3333:4444::1.2.3.4", ":1111:2222:3333::1.2.3.4", ":1111:2222::1.2.3.4", ":1111::1.2.3.4", ":::1.2.3.4", ":1111:2222:3333:4444::6666:1.2.3.4", ":1111:2222:3333::6666:1.2.3.4", ":1111:2222::6666:1.2.3.4", ":1111::6666:1.2.3.4", ":::6666:1.2.3.4", ":1111:2222:3333::5555:6666:1.2.3.4", ":1111:2222::5555:6666:1.2.3.4", ":1111::5555:6666:1.2.3.4", ":::5555:6666:1.2.3.4", ":1111:2222::4444:5555:6666:1.2.3.4", ":1111::4444:5555:6666:1.2.3.4", ":::4444:5555:6666:1.2.3.4", ":1111::3333:4444:5555:6666:1.2.3.4", ":::2222:3333:4444:5555:6666:1.2.3.4", # Extra : at end "1111:2222:3333:4444:5555:6666:7777:::", "1111:2222:3333:4444:5555:6666:::", "1111:2222:3333:4444:5555:::", "1111:2222:3333:4444:::", "1111:2222:3333:::", "1111:2222:::", "1111:::", ":::", "1111:2222:3333:4444:5555:6666::8888:", "1111:2222:3333:4444:5555::8888:", "1111:2222:3333:4444::8888:", "1111:2222:3333::8888:", "1111:2222::8888:", "1111::8888:", "::8888:", "1111:2222:3333:4444:5555::7777:8888:", "1111:2222:3333:4444::7777:8888:", "1111:2222:3333::7777:8888:", "1111:2222::7777:8888:", "1111::7777:8888:", "::7777:8888:", "1111:2222:3333:4444::6666:7777:8888:", "1111:2222:3333::6666:7777:8888:", "1111:2222::6666:7777:8888:", "1111::6666:7777:8888:", "::6666:7777:8888:", "1111:2222:3333::5555:6666:7777:8888:", "1111:2222::5555:6666:7777:8888:", "1111::5555:6666:7777:8888:", "::5555:6666:7777:8888:", "1111:2222::4444:5555:6666:7777:8888:", "1111::4444:5555:6666:7777:8888:", "::4444:5555:6666:7777:8888:", "1111::3333:4444:5555:6666:7777:8888:", "::3333:4444:5555:6666:7777:8888:", "::2222:3333:4444:5555:6666:7777:8888:", ].each do |ip| it "should reject #{ip.inspect} as an IPv6 address" do expect { @class.new(:name => "foo", :ip => ip) }.to raise_error end end it "should not accept spaces in resourcename" do proc { @class.new(:name => "foo bar") }.should raise_error end it "should not accept host_aliases with spaces" do proc { @class.new(:name => "foo", :host_aliases => [ 'well_formed', 'not wellformed' ]) }.should raise_error end it "should not accept empty host_aliases" do proc { @class.new(:name => "foo", :host_aliases => ['alias1','']) }.should raise_error end end describe "when syncing" do it "should send the first value to the provider for ip property" do @ip = @class.attrclass(:ip).new(:resource => @resource, :should => %w{192.168.0.1 192.168.0.2}) @provider.expects(:ip=).with '192.168.0.1' @ip.sync end it "should send the first value to the provider for comment property" do @comment = @class.attrclass(:comment).new(:resource => @resource, :should => %w{Bazinga Notme}) @provider.expects(:comment=).with 'Bazinga' @comment.sync end it "should send the joined array to the provider for host_alias" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @provider.expects(:host_aliases=).with 'foo bar' @host_aliases.sync end it "should also use the specified delimiter for joining" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.stubs(:delimiter).returns "\t" @provider.expects(:host_aliases=).with "foo\tbar" @host_aliases.sync end it "should care about the order of host_aliases" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.insync?(%w{foo bar}).should == true @host_aliases.insync?(%w{bar foo}).should == false end it "should not consider aliases to be in sync if should is a subset of current" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.insync?(%w{foo bar anotherone}).should == false end end end diff --git a/spec/unit/type/interface_spec.rb b/spec/unit/type/interface_spec.rb index 74e3257f6..addfd3b48 100755 --- a/spec/unit/type/interface_spec.rb +++ b/spec/unit/type/interface_spec.rb @@ -1,97 +1,97 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:interface) do it "should have a 'name' parameter'" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1")[:name].should == "FastEthernet 0/1" end it "should have a 'device_url' parameter'" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :device_url => :device)[:device_url].should == :device end it "should have an ensure property" do Puppet::Type.type(:interface).attrtype(:ensure).should == :property end it "should be applied on device" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1").should be_appliable_to_device end [:description, :speed, :duplex, :native_vlan, :encapsulation, :mode, :allowed_trunk_vlans, :etherchannel, :ipaddress].each do |p| it "should have a #{p} property" do Puppet::Type.type(:interface).attrtype(p).should == :property end end describe "when validating attribute values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:interface).defaultprovider, :clear => nil Puppet::Type.type(:interface).defaultprovider.stubs(:new).returns(@provider) end it "should support :present as a value to :ensure" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :present) end it "should support :shutdown as a value to :ensure" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :shutdown) end it "should support :no_shutdown as a value to :ensure" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ensure => :no_shutdown) end describe "especially speed" do it "should allow a number" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :speed => "100") end it "should allow :auto" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :speed => :auto) end end describe "especially duplex" do it "should allow :half" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :half) end it "should allow :full" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :full) end it "should allow :auto" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :duplex => :auto) end end describe "especially ipaddress" do it "should allow ipv4 addresses" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "192.168.0.1/24") end it "should allow arrays of ipv4 addresses" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => ["192.168.0.1/24", "192.168.1.0/24"]) end it "should allow ipv6 addresses" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64") end it "should allow ipv6 options" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64 link-local") Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "f0e9::/64 eui-64") end it "should allow a mix of ipv4 and ipv6" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => ["192.168.0.1/24", "f0e9::/64 link-local"]) end it "should munge ip addresses to a computer format" do Puppet::Type.type(:interface).new(:name => "FastEthernet 0/1", :ipaddress => "192.168.0.1/24")[:ipaddress].should == [[24, IPAddr.new('192.168.0.1'), nil]] end end end end diff --git a/spec/unit/type/macauthorization_spec.rb b/spec/unit/type/macauthorization_spec.rb index 8ab30834b..25a1cfc40 100755 --- a/spec/unit/type/macauthorization_spec.rb +++ b/spec/unit/type/macauthorization_spec.rb @@ -1,110 +1,110 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' macauth_type = Puppet::Type.type(:macauthorization) describe Puppet::Type.type(:macauthorization), "when checking macauthorization objects" do before do authplist = {} authplist["rules"] = { "foorule" => "foo" } authplist["rights"] = { "fooright" => "foo" } provider_class = macauth_type.provider(macauth_type.providers[0]) Plist.stubs(:parse_xml).with("/etc/authorization").returns(authplist) macauth_type.stubs(:defaultprovider).returns provider_class @resource = macauth_type.new(:name => 'foo') end describe "when validating attributes" do parameters = [:name,] properties = [:auth_type, :allow_root, :authenticate_user, :auth_class, :comment, :group, :k_of_n, :mechanisms, :rule, :session_owner, :shared, :timeout, :tries] parameters.each do |parameter| it "should have a #{parameter} parameter" do macauth_type.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{parameter} parameter" do macauth_type.attrclass(parameter).doc.should be_instance_of(String) end end properties.each do |property| it "should have a #{property} property" do macauth_type.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do macauth_type.attrclass(property).doc.should be_instance_of(String) end end end describe "when validating properties" do it "should have a default provider inheriting from Puppet::Provider" do macauth_type.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should be able to create an instance" do lambda { macauth_type.new(:name => 'foo') }.should_not raise_error end it "should support :present as a value to :ensure" do lambda { macauth_type.new(:name => "foo", :ensure => :present) }.should_not raise_error end it "should support :absent as a value to :ensure" do lambda { macauth_type.new(:name => "foo", :ensure => :absent) }.should_not raise_error end end [:k_of_n, :timeout, :tries].each do |property| describe "when managing the #{property} property" do it "should convert number-looking strings into actual numbers" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = "300" prop.should.must == 300 end it "should support integers as a value" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = 300 prop.should.must == 300 end it "should raise an error for non-integer values" do prop = macauth_type.attrclass(property).new(:resource => @resource) lambda { prop.should = "foo" }.should raise_error(Puppet::Error) end end end [:allow_root, :authenticate_user, :session_owner, :shared].each do |property| describe "when managing the #{property} property" do it "should convert boolean-looking false strings into actual booleans" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = "false" prop.should.must == :false end it "should convert boolean-looking true strings into actual booleans" do prop = macauth_type.attrclass(property).new(:resource => @resource) prop.should = "true" prop.should.must == :true end it "should raise an error for non-boolean values" do prop = macauth_type.attrclass(property).new(:resource => @resource) lambda { prop.should = "foo" }.should raise_error(Puppet::Error) end end end end diff --git a/spec/unit/type/mailalias_spec.rb b/spec/unit/type/mailalias_spec.rb index d4ed81949..7f4057f5d 100755 --- a/spec/unit/type/mailalias_spec.rb +++ b/spec/unit/type/mailalias_spec.rb @@ -1,21 +1,21 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:mailalias) do include PuppetSpec::Files let :target do tmpfile('mailalias') end let :resource do described_class.new(:name => "luke", :recipient => "yay", :target => target) end it "should be initially absent" do resource.retrieve_resource[:recipient].should == :absent end it "should try and set the recipient when it does the sync" do resource.retrieve_resource[:recipient].should == :absent resource.property(:recipient).expects(:set).with(["yay"]) resource.property(:recipient).sync end end diff --git a/spec/unit/type/maillist_spec.rb b/spec/unit/type/maillist_spec.rb index ae5fcd5b3..cabc375ff 100755 --- a/spec/unit/type/maillist_spec.rb +++ b/spec/unit/type/maillist_spec.rb @@ -1,41 +1,41 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' maillist = Puppet::Type.type(:maillist) describe maillist do before do @provider_class = Puppet::Type.type(:maillist).provider(:mailman) @provider = stub 'provider', :class => @provider_class, :clear => nil @provider.stubs(:respond_to).with(:aliases).returns(true) @provider_class.stubs(:new).returns(@provider) Puppet::Type.type(:maillist).stubs(:defaultprovider).returns(@provider_class) @maillist = Puppet::Type.type(:maillist).new( :name => 'test' ) @catalog = Puppet::Resource::Catalog.new @maillist.catalog = @catalog end it "should generate aliases unless they already exist" do # Mail List aliases are careful not to stomp on managed Mail Alias aliases # test1 is an unmanaged alias from /etc/aliases Puppet::Type.type(:mailalias).provider(:aliases).stubs(:target_object).returns( StringIO.new("test1: root\n") ) # test2 is a managed alias from the manifest dupe = Puppet::Type.type(:mailalias).new( :name => 'test2' ) @catalog.add_resource dupe @provider.stubs(:aliases).returns({"test1" => 'this will get included', "test2" => 'this will dropped', "test3" => 'this will get included'}) generated = @maillist.generate generated.map{ |x| x.name }.sort.should == ['test1', 'test3'] generated.map{ |x| x.class }.should == [Puppet::Type::Mailalias, Puppet::Type::Mailalias] end end diff --git a/spec/unit/type/mcx_spec.rb b/spec/unit/type/mcx_spec.rb index 02f691a52..cccbe18b4 100755 --- a/spec/unit/type/mcx_spec.rb +++ b/spec/unit/type/mcx_spec.rb @@ -1,79 +1,79 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/type/mcx' mcx_type = Puppet::Type.type(:mcx) describe mcx_type, "when validating attributes" do properties = [:ensure, :content] parameters = [:name, :ds_type, :ds_name] parameters.each do |p| it "should have a #{p} parameter" do mcx_type.attrclass(p).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{p} parameter" do mcx_type.attrclass(p).doc.should be_instance_of(String) end end properties.each do |p| it "should have a #{p} property" do mcx_type.attrclass(p).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{p} property" do mcx_type.attrclass(p).doc.should be_instance_of(String) end end end describe mcx_type, "default values" do before :each do provider_class = mcx_type.provider(mcx_type.providers[0]) mcx_type.stubs(:defaultprovider).returns provider_class end it "should be nil for :ds_type" do mcx_type.new(:name => '/Foo/bar')[:ds_type].should be_nil end it "should be nil for :ds_name" do mcx_type.new(:name => '/Foo/bar')[:ds_name].should be_nil end it "should be nil for :content" do mcx_type.new(:name => '/Foo/bar')[:content].should be_nil end end describe mcx_type, "when validating properties" do before :each do provider_class = mcx_type.provider(mcx_type.providers[0]) mcx_type.stubs(:defaultprovider).returns provider_class end it "should be able to create an instance" do lambda { mcx_type.new(:name => '/Foo/bar') }.should_not raise_error end it "should support :present as a value to :ensure" do lambda { mcx_type.new(:name => "/Foo/bar", :ensure => :present) }.should_not raise_error end it "should support :absent as a value to :ensure" do lambda { mcx_type.new(:name => "/Foo/bar", :ensure => :absent) }.should_not raise_error end end diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb index 82eb563e9..3af5d1a67 100755 --- a/spec/unit/type/mount_spec.rb +++ b/spec/unit/type/mount_spec.rb @@ -1,329 +1,329 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:mount), :unless => Puppet.features.microsoft_windows? do it "should have a :refreshable feature that requires the :remount method" do Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount] end it "should have no default value for :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay") mount.should(:ensure).should be_nil end it "should have :name as the only keyattribut" do Puppet::Type.type(:mount).key_attributes.should == [:name] end end describe Puppet::Type.type(:mount), "when validating attributes" do [:name, :remounts, :provider].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:mount).attrtype(param).should == :param end end [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| it "should have a #{param} property" do Puppet::Type.type(:mount).attrtype(param).should == :property end end end describe Puppet::Type.type(:mount)::Ensure, "when validating values", :unless => Puppet.features.microsoft_windows? do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider) end it "should alias :present to :defined as a value to :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) mount.should(:ensure).should == :defined end it "should support :present as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) end it "should support :defined as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :defined) end it "should support :unmounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) end it "should support :absent as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :absent) end it "should support :mounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) end end describe Puppet::Type.type(:mount)::Ensure, :unless => Puppet.features.microsoft_windows? do before :each do provider_properties = {} @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :property_hash => provider_properties Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) @ensure = @mount.property(:ensure) end def mount_stub(params) Puppet::Type.type(:mount).validproperties.each do |prop| unless params[prop] params[prop] = :absent @mount[prop] = :absent end end params.each do |param, value| @provider.stubs(param).returns(value) end end describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do def test_ensure_change(options) @provider.stubs(:get).with(:ensure).returns options[:from] @provider.stubs(:ensure).returns options[:from] @provider.stubs(:mounted?).returns([:mounted,:ghost].include? options[:from]) @provider.expects(:create).times(options[:create] || 0) @provider.expects(:destroy).times(options[:destroy] || 0) @provider.expects(:mount).never @provider.expects(:unmount).times(options[:unmount] || 0) @ensure.stubs(:syncothers) @ensure.should = options[:to] @ensure.sync (!!@provider.property_hash[:needs_mount]).should == (!!options[:mount]) end it "should create itself when changing from :ghost to :present" do test_ensure_change(:from => :ghost, :to => :present, :create => 1) end it "should create itself when changing from :absent to :present" do test_ensure_change(:from => :absent, :to => :present, :create => 1) end it "should create itself and unmount when changing from :ghost to :unmounted" do test_ensure_change(:from => :ghost, :to => :unmounted, :create => 1, :unmount => 1) end it "should unmount resource when changing from :mounted to :unmounted" do test_ensure_change(:from => :mounted, :to => :unmounted, :unmount => 1) end it "should create itself when changing from :absent to :unmounted" do test_ensure_change(:from => :absent, :to => :unmounted, :create => 1) end it "should unmount resource when changing from :ghost to :absent" do test_ensure_change(:from => :ghost, :to => :absent, :unmount => 1) end it "should unmount and destroy itself when changing from :mounted to :absent" do test_ensure_change(:from => :mounted, :to => :absent, :destroy => 1, :unmount => 1) end it "should destroy itself when changing from :unmounted to :absent" do test_ensure_change(:from => :unmounted, :to => :absent, :destroy => 1) end it "should create itself when changing from :ghost to :mounted" do test_ensure_change(:from => :ghost, :to => :mounted, :create => 1) end it "should create itself and mount when changing from :absent to :mounted" do test_ensure_change(:from => :absent, :to => :mounted, :create => 1, :mount => 1) end it "should mount resource when changing from :unmounted to :mounted" do test_ensure_change(:from => :unmounted, :to => :mounted, :mount => 1) end it "should be in sync if it is :absent and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:absent).should == true end it "should be out of sync if it is :absent and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :absent and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :absent and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :mounted and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:mounted).should == false end it "should be in sync if it is :mounted and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:mounted).should == true end it "should be in sync if it is :mounted and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:mounted).should == true end it "should be out in sync if it is :mounted and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:mounted).should == false end it "should be out of sync if it is :unmounted and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:unmounted).should == false end it "should be in sync if it is :unmounted and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:unmounted).should == true end it "should be out of sync if it is :unmounted and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:unmounted).should == false end it "should be in sync if it is :unmounted and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:unmounted).should == true end it "should be out of sync if it is :ghost and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:ghost).should == false end end describe Puppet::Type.type(:mount), "when responding to refresh" do pending "2.6.x specifies slightly different behavior and the desired behavior needs to be clarified and revisited. See ticket #4904" do it "should remount if it is supposed to be mounted" do @mount[:ensure] = "mounted" @provider.expects(:remount) @mount.refresh end it "should not remount if it is supposed to be present" do @mount[:ensure] = "present" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be absent" do @mount[:ensure] = "absent" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be defined" do @mount[:ensure] = "defined" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be unmounted" do @mount[:ensure] = "unmounted" @provider.expects(:remount).never @mount.refresh end it "should not remount swap filesystems" do @mount[:ensure] = "mounted" @mount[:fstype] = "swap" @provider.expects(:remount).never @mount.refresh end end end end describe Puppet::Type.type(:mount), "when modifying an existing mount entry", :unless => Puppet.features.microsoft_windows? do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :remount => nil Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) {:device => "/foo/bar", :blockdevice => "/other/bar", :target => "/what/ever", :fstype => 'eh', :options => "", :pass => 0, :dump => 0, :atboot => 0, :ensure => :mounted}.each do |param, value| @mount.provider.stubs(param).returns value @mount[param] = value end @mount.provider.stubs(:mounted?).returns true # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @mount end it "should use the provider to change the dump value" do @mount.provider.expects(:dump).returns 0 @mount.provider.expects(:dump=).with(1) @mount[:dump] = 1 @catalog.apply end it "should umount before flushing changes to disk" do syncorder = sequence('syncorder') @mount.provider.expects(:options).returns 'soft' @mount.provider.expects(:ensure).returns :mounted @mount.provider.expects(:unmount).in_sequence(syncorder) @mount.provider.expects(:options=).in_sequence(syncorder).with 'hard' @mount.expects(:flush).in_sequence(syncorder) # Call inside syncothers @mount.expects(:flush).in_sequence(syncorder) # I guess transaction or anything calls flush again @mount[:ensure] = :unmounted @mount[:options] = 'hard' @catalog.apply end end diff --git a/spec/unit/type/nagios_spec.rb b/spec/unit/type/nagios_spec.rb index d650723c8..737fc6ae6 100755 --- a/spec/unit/type/nagios_spec.rb +++ b/spec/unit/type/nagios_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/external/nagios' describe "Nagios resource types" do Nagios::Base.eachtype do |name, nagios_type| puppet_type = Puppet::Type.type("nagios_#{name}") it "should have a valid type for #{name}" do puppet_type.should_not be_nil end next unless puppet_type describe puppet_type do it "should be defined as a Puppet resource type" do puppet_type.should_not be_nil end it "should have documentation" do puppet_type.instance_variable_get("@doc").should_not == "" end it "should have #{nagios_type.namevar} as its key attribute" do puppet_type.key_attributes.should == [nagios_type.namevar] end it "should have documentation for its #{nagios_type.namevar} parameter" do puppet_type.attrclass(nagios_type.namevar).instance_variable_get("@doc").should_not be_nil end it "should have an ensure property" do puppet_type.should be_validproperty(:ensure) end it "should have a target property" do puppet_type.should be_validproperty(:target) end it "should have documentation for its target property" do puppet_type.attrclass(:target).instance_variable_get("@doc").should_not be_nil end nagios_type.parameters.reject { |param| param == nagios_type.namevar or param.to_s =~ /^[0-9]/ }.each do |param| it "should have a #{param} property" do puppet_type.should be_validproperty(param) end it "should have documentation for its #{param} property" do puppet_type.attrclass(param).instance_variable_get("@doc").should_not be_nil end end nagios_type.parameters.find_all { |param| param.to_s =~ /^[0-9]/ }.each do |param| it "should have not have a #{param} property" do puppet_type.should_not be_validproperty(:param) end end end end end diff --git a/spec/unit/type/noop_metaparam_spec.rb b/spec/unit/type/noop_metaparam_spec.rb index 7083dd037..7277ef0ab 100755 --- a/spec/unit/type/noop_metaparam_spec.rb +++ b/spec/unit/type/noop_metaparam_spec.rb @@ -1,38 +1,38 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/type' describe Puppet::Type.type(:file).attrclass(:noop) do include PuppetSpec::Files before do Puppet.settings.stubs(:use) @file = Puppet::Type.newfile :path => make_absolute("/what/ever") end it "should accept true as a value" do lambda { @file[:noop] = true }.should_not raise_error end it "should accept false as a value" do lambda { @file[:noop] = false }.should_not raise_error end describe "when set on a resource" do it "should default to the :noop setting" do Puppet.settings.expects(:value).with(:noop).returns "myval" @file.noop.should == "myval" end it "should prefer true values from the attribute" do @file[:noop] = true @file.noop.should be_true end it "should prefer false values from the attribute" do @file[:noop] = false @file.noop.should be_false end end end diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb index 003d1f69a..44300d9ec 100755 --- a/spec/unit/type/package_spec.rb +++ b/spec/unit/type/package_spec.rb @@ -1,286 +1,286 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:package) do before do Puppet::Util::Storage.stubs(:store) end it "should have an :installable feature that requires the :install method" do Puppet::Type.type(:package).provider_feature(:installable).methods.should == [:install] end it "should have an :uninstallable feature that requires the :uninstall method" do Puppet::Type.type(:package).provider_feature(:uninstallable).methods.should == [:uninstall] end it "should have an :upgradeable feature that requires :update and :latest methods" do Puppet::Type.type(:package).provider_feature(:upgradeable).methods.should == [:update, :latest] end it "should have a :purgeable feature that requires the :purge latest method" do Puppet::Type.type(:package).provider_feature(:purgeable).methods.should == [:purge] end it "should have a :versionable feature" do Puppet::Type.type(:package).provider_feature(:versionable).should_not be_nil end it "should default to being installed" do pkg = Puppet::Type.type(:package).new(:name => "yay", :provider => :apt) pkg.should(:ensure).should == :present end describe "when validating attributes" do [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:package).attrtype(param).should == :param end end it "should have an ensure property" do Puppet::Type.type(:package).attrtype(:ensure).should == :property end end describe "when validating attribute values" do before do @provider = stub( 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :validate_source => nil ) Puppet::Type.type(:package).defaultprovider.expects(:new).returns(@provider) end it "should support :present as a value to :ensure" do Puppet::Type.type(:package).new(:name => "yay", :ensure => :present) end it "should alias :installed to :present as a value to :ensure" do pkg = Puppet::Type.type(:package).new(:name => "yay", :ensure => :installed) pkg.should(:ensure).should == :present end it "should support :absent as a value to :ensure" do Puppet::Type.type(:package).new(:name => "yay", :ensure => :absent) end it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do @provider.expects(:satisfies?).with([:purgeable]).returns(true) Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) end it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do @provider.expects(:satisfies?).with([:purgeable]).returns(false) proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) end it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do @provider.expects(:satisfies?).with([:upgradeable]).returns(true) Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) end it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do @provider.expects(:satisfies?).with([:upgradeable]).returns(false) proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) end it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do @provider.expects(:satisfies?).with([:versionable]).returns(true) Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") end it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do @provider.expects(:satisfies?).with([:versionable]).returns(false) proc { Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) end it "should accept any string as an argument to :source" do proc { Puppet::Type.type(:package).new(:name => "yay", :source => "stuff") }.should_not raise_error(Puppet::Error) end end module PackageEvaluationTesting def setprops(properties) @provider.stubs(:properties).returns(properties) end end describe Puppet::Type.type(:package) do before :each do @provider = stub( 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :validate_source => nil ) Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) Puppet::Type.type(:package).defaultprovider.stubs(:instances).returns([]) @package = Puppet::Type.type(:package).new(:name => "yay") @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@package) end describe Puppet::Type.type(:package), "when it should be purged" do include PackageEvaluationTesting before { @package[:ensure] = :purged } it "should do nothing if it is :purged" do @provider.expects(:properties).returns(:ensure => :purged).at_least_once @catalog.apply end [:absent, :installed, :present, :latest].each do |state| it "should purge if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:purge) @catalog.apply end end end describe Puppet::Type.type(:package), "when it should be absent" do include PackageEvaluationTesting before { @package[:ensure] = :absent } [:purged, :absent].each do |state| it "should do nothing if it is #{state.to_s}" do @provider.expects(:properties).returns(:ensure => state).at_least_once @catalog.apply end end [:installed, :present, :latest].each do |state| it "should uninstall if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:uninstall) @catalog.apply end end end describe Puppet::Type.type(:package), "when it should be present" do include PackageEvaluationTesting before { @package[:ensure] = :present } [:present, :latest, "1.0"].each do |state| it "should do nothing if it is #{state.to_s}" do @provider.expects(:properties).returns(:ensure => state).at_least_once @catalog.apply end end [:purged, :absent].each do |state| it "should install if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:install) @catalog.apply end end end describe Puppet::Type.type(:package), "when it should be latest" do include PackageEvaluationTesting before { @package[:ensure] = :latest } [:purged, :absent].each do |state| it "should upgrade if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @provider.expects(:update) @catalog.apply end end it "should upgrade if the current version is not equal to the latest version" do @provider.stubs(:properties).returns(:ensure => "1.0") @provider.stubs(:latest).returns("2.0") @provider.expects(:update) @catalog.apply end it "should do nothing if it is equal to the latest version" do @provider.stubs(:properties).returns(:ensure => "1.0") @provider.stubs(:latest).returns("1.0") @provider.expects(:update).never @catalog.apply end it "should do nothing if the provider returns :present as the latest version" do @provider.stubs(:properties).returns(:ensure => :present) @provider.stubs(:latest).returns("1.0") @provider.expects(:update).never @catalog.apply end end describe Puppet::Type.type(:package), "when it should be a specific version" do include PackageEvaluationTesting before { @package[:ensure] = "1.0" } [:purged, :absent].each do |state| it "should install if it is #{state.to_s}" do @provider.stubs(:properties).returns(:ensure => state) @package.property(:ensure).insync?(state).should be_false @provider.expects(:install) @catalog.apply end end it "should do nothing if the current version is equal to the desired version" do @provider.stubs(:properties).returns(:ensure => "1.0") @package.property(:ensure).insync?('1.0').should be_true @provider.expects(:install).never @catalog.apply end it "should install if the current version is not equal to the specified version" do @provider.stubs(:properties).returns(:ensure => "2.0") @package.property(:ensure).insync?('2.0').should be_false @provider.expects(:install) @catalog.apply end describe "when current value is an array" do let(:installed_versions) { ["1.0", "2.0", "3.0"] } before (:each) do @provider.stubs(:properties).returns(:ensure => installed_versions) end it "should install if value not in the array" do @package[:ensure] = "1.5" @package.property(:ensure).insync?(installed_versions).should be_false @provider.expects(:install) @catalog.apply end it "should not install if value is in the array" do @package[:ensure] = "2.0" @package.property(:ensure).insync?(installed_versions).should be_true @provider.expects(:install).never @catalog.apply end describe "when ensure is set to 'latest'" do it "should not install if the value is in the array" do @provider.expects(:latest).returns("3.0") @package[:ensure] = "latest" @package.property(:ensure).insync?(installed_versions).should be_true @provider.expects(:install).never @catalog.apply end end end end end end diff --git a/spec/unit/type/resources_spec.rb b/spec/unit/type/resources_spec.rb index 3c0850efb..76e0d1ec6 100755 --- a/spec/unit/type/resources_spec.rb +++ b/spec/unit/type/resources_spec.rb @@ -1,100 +1,100 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' resources = Puppet::Type.type(:resources) # There are still plenty of tests to port over from test/. describe resources do describe "when initializing" do it "should fail if the specified resource type does not exist" do Puppet::Type.stubs(:type).with { |x| x.to_s.downcase == "resources"}.returns resources Puppet::Type.expects(:type).with("nosuchtype").returns nil lambda { resources.new :name => "nosuchtype" }.should raise_error(Puppet::Error) end it "should not fail when the specified resource type exists" do lambda { resources.new :name => "file" }.should_not raise_error end it "should set its :resource_type attribute" do resources.new(:name => "file").resource_type.should == Puppet::Type.type(:file) end end describe "#generate" do before do @host1 = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') @catalog = Puppet::Resource::Catalog.new @context = Puppet::Transaction.new(@catalog) end describe "when dealing with non-purging resources" do before do @resources = Puppet::Type.type(:resources).new(:name => 'host') end it "should not generate any resource" do @resources.generate.should be_empty end end describe "when the catalog contains a purging resource" do before do @resources = Puppet::Type.type(:resources).new(:name => 'host', :purge => true) @purgeable_resource = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') @catalog.add_resource @resources end it "should not generate a duplicate of that resource" do Puppet::Type.type(:host).stubs(:instances).returns [@host1] @catalog.add_resource @host1 @resources.generate.collect { |r| r.ref }.should_not include(@host1.ref) end it "should not include the skipped system users" do res = Puppet::Type.type(:resources).new :name => :user, :purge => true res.catalog = Puppet::Resource::Catalog.new root = Puppet::Type.type(:user).new(:name => "root") Puppet::Type.type(:user).expects(:instances).returns [ root ] list = res.generate names = list.collect { |r| r[:name] } names.should_not be_include("root") end describe "when generating a purgeable resource" do it "should be included in the generated resources" do Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] @resources.generate.collect { |r| r.ref }.should include(@purgeable_resource.ref) end end describe "when the instance's do not have an ensure property" do it "should not be included in the generated resources" do @no_ensure_resource = Puppet::Type.type(:exec).new(:name => "#{File.expand_path('/usr/bin/env')} echo") Puppet::Type.type(:host).stubs(:instances).returns [@no_ensure_resource] @resources.generate.collect { |r| r.ref }.should_not include(@no_ensure_resource.ref) end end describe "when the instance's ensure property does not accept absent" do it "should not be included in the generated resources" do @no_absent_resource = Puppet::Type.type(:service).new(:name => 'foobar') Puppet::Type.type(:host).stubs(:instances).returns [@no_absent_resource] @resources.generate.collect { |r| r.ref }.should_not include(@no_absent_resource.ref) end end describe "when checking the instance fails" do it "should not be included in the generated resources" do @purgeable_resource = Puppet::Type.type(:host).new(:name => 'foobar') Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] @resources.expects(:check).with(@purgeable_resource).returns(false) @resources.generate.collect { |r| r.ref }.should_not include(@purgeable_resource.ref) end end end end end diff --git a/spec/unit/type/schedule_spec.rb b/spec/unit/type/schedule_spec.rb index a2a3cc12f..e5320e51a 100755 --- a/spec/unit/type/schedule_spec.rb +++ b/spec/unit/type/schedule_spec.rb @@ -1,601 +1,601 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' module ScheduleTesting def diff(unit, incr, method, count) diff = Time.now.to_i.send(method, incr * count) Time.at(diff) end def day(method, count) diff(:hour, 3600 * 24, method, count) end def hour(method, count) diff(:hour, 3600, method, count) end def min(method, count) diff(:min, 60, method, count) end end describe Puppet::Type.type(:schedule) do include ScheduleTesting before :each do Puppet[:ignoreschedules] = false @schedule = Puppet::Type.type(:schedule).new(:name => "testing") end describe Puppet::Type.type(:schedule) do it "should apply to device" do @schedule.must be_appliable_to_device end it "should apply to host" do @schedule.must be_appliable_to_host end it "should default to :distance for period-matching" do @schedule[:periodmatch].must == :distance end it "should default to a :repeat of 1" do @schedule[:repeat].must == 1 end it "should never match when the period is :never" do @schedule[:period] = :never @schedule.must_not be_match end end describe Puppet::Type.type(:schedule), "when producing default schedules" do %w{hourly daily weekly monthly never}.each do |period| period = period.to_sym it "should produce a #{period} schedule with the period set appropriately" do schedules = Puppet::Type.type(:schedule).mkdefaultschedules schedules.find { |s| s[:name] == period.to_s and s[:period] == period }.must be_instance_of(Puppet::Type.type(:schedule)) end end it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do schedules = Puppet::Type.type(:schedule).mkdefaultschedules schedules.find { |s| s[:name] == "puppet" and s[:period] == :hourly and s[:repeat] == 2 }.must be_instance_of(Puppet::Type.type(:schedule)) end end describe Puppet::Type.type(:schedule), "when matching ranges" do before do Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should match when the start time is before the current time and the end time is after the current time" do @schedule[:range] = "10:59:50 - 11:00:10" @schedule.must be_match end it "should not match when the start time is after the current time" do @schedule[:range] = "11:00:05 - 11:00:10" @schedule.must_not be_match end it "should not match when the end time is previous to the current time" do @schedule[:range] = "10:59:50 - 10:59:55" @schedule.must_not be_match end it "should not match the current time fails between an array of ranges" do @schedule[:range] = ["4-6", "20-23"] @schedule.must_not be_match end it "should match the lower array of ranges" do @schedule[:range] = ["9-11", "14-16"] @schedule.must be_match end it "should match the upper array of ranges" do @schedule[:range] = ["4-6", "11-12"] @schedule.must be_match end end describe Puppet::Type.type(:schedule), "when matching ranges with abbreviated time specifications" do before do Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 45, 59)) end it "should match when just an hour is specified" do @schedule[:range] = "11-12" @schedule.must be_match end it "should not match when the ending hour is the current hour" do @schedule[:range] = "10-11" @schedule.must_not be_match end it "should not match when the ending minute is the current minute" do @schedule[:range] = "10:00 - 11:45" @schedule.must_not be_match end end describe Puppet::Type.type(:schedule), "when matching ranges with abbreviated time specifications, edge cases part 1" do before do Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 00, 00)) end it "should match when the current time is the start of the range using hours" do @schedule[:range] = "11 - 12" @schedule.must be_match end it "should match when the current time is the end of the range using hours" do @schedule[:range] = "10 - 11" @schedule.must be_match end it "should match when the current time is the start of the range using hours and minutes" do @schedule[:range] = "11:00 - 12:00" @schedule.must be_match end it "should match when the current time is the end of the range using hours and minutes" do @schedule[:range] = "10:00 - 11:00" @schedule.must be_match end end describe Puppet::Type.type(:schedule), "when matching ranges with abbreviated time specifications, edge cases part 2" do before do Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 00, 01)) end it "should match when the current time is just past the start of the range using hours" do @schedule[:range] = "11 - 12" @schedule.must be_match end it "should not match when the current time is just past the end of the range using hours" do @schedule[:range] = "10 - 11" @schedule.must_not be_match end it "should match when the current time is just past the start of the range using hours and minutes" do @schedule[:range] = "11:00 - 12:00" @schedule.must be_match end it "should not match when the current time is just past the end of the range using hours and minutes" do @schedule[:range] = "10:00 - 11:00" @schedule.must_not be_match end end describe Puppet::Type.type(:schedule), "when matching ranges with abbreviated time specifications, edge cases part 3" do before do Time.stubs(:now).returns(Time.local(2011, "may", 23, 10, 59, 59)) end it "should not match when the current time is just before the start of the range using hours" do @schedule[:range] = "11 - 12" @schedule.must_not be_match end it "should match when the current time is just before the end of the range using hours" do @schedule[:range] = "10 - 11" @schedule.must be_match end it "should not match when the current time is just before the start of the range using hours and minutes" do @schedule[:range] = "11:00 - 12:00" @schedule.must_not be_match end it "should match when the current time is just before the end of the range using hours and minutes" do @schedule[:range] = "10:00 - 11:00" @schedule.must be_match end end describe Puppet::Type.type(:schedule), "when matching ranges spanning days, day 1" do before do # Test with the current time at a month's end boundary to ensure we are # advancing the day properly when we push the ending limit out a day. # For example, adding 1 to 31 would throw an error instead of advancing # the date. Time.stubs(:now).returns(Time.local(2011, "mar", 31, 22, 30, 0)) end it "should match when the start time is before current time and the end time is the following day" do @schedule[:range] = "22:00:00 - 02:00:00" @schedule.must be_match end it "should not match when the current time is outside the range" do @schedule[:range] = "23:30:00 - 21:00:00" @schedule.must_not be_match end end describe Puppet::Type.type(:schedule), "when matching ranges spanning days, day 2" do before do # Test with the current time at a month's end boundary to ensure we are # advancing the day properly when we push the ending limit out a day. # For example, adding 1 to 31 would throw an error instead of advancing # the date. Time.stubs(:now).returns(Time.local(2011, "mar", 31, 1, 30, 0)) end it "should match when the start time is the day before the current time and the end time is after the current time" do @schedule[:range] = "22:00:00 - 02:00:00" @schedule.must be_match end it "should not match when the start time is after the current time" do @schedule[:range] = "02:00:00 - 00:30:00" @schedule.must_not be_match end it "should not match when the end time is before the current time" do @schedule[:range] = "22:00:00 - 01:00:00" @schedule.must_not be_match end end describe Puppet::Type.type(:schedule), "when matching hourly by distance" do before do @schedule[:period] = :hourly @schedule[:periodmatch] = :distance Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should match when the previous time was an hour ago" do @schedule.must be_match(hour("-", 1)) end it "should not match when the previous time was now" do @schedule.must_not be_match(Time.now) end it "should not match when the previous time was 59 minutes ago" do @schedule.must_not be_match(min("-", 59)) end end describe Puppet::Type.type(:schedule), "when matching daily by distance" do before do @schedule[:period] = :daily @schedule[:periodmatch] = :distance Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should match when the previous time was one day ago" do @schedule.must be_match(day("-", 1)) end it "should not match when the previous time is now" do @schedule.must_not be_match(Time.now) end it "should not match when the previous time was 23 hours ago" do @schedule.must_not be_match(hour("-", 23)) end end describe Puppet::Type.type(:schedule), "when matching weekly by distance" do before do @schedule[:period] = :weekly @schedule[:periodmatch] = :distance Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should match when the previous time was seven days ago" do @schedule.must be_match(day("-", 7)) end it "should not match when the previous time was now" do @schedule.must_not be_match(Time.now) end it "should not match when the previous time was six days ago" do @schedule.must_not be_match(day("-", 6)) end end describe Puppet::Type.type(:schedule), "when matching monthly by distance" do before do @schedule[:period] = :monthly @schedule[:periodmatch] = :distance Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should match when the previous time was 32 days ago" do @schedule.must be_match(day("-", 32)) end it "should not match when the previous time was now" do @schedule.must_not be_match(Time.now) end it "should not match when the previous time was 27 days ago" do @schedule.must_not be_match(day("-", 27)) end end describe Puppet::Type.type(:schedule), "when matching hourly by number" do before do @schedule[:period] = :hourly @schedule[:periodmatch] = :number end it "should match if the times are one minute apart and the current minute is 0" do current = Time.utc(2008, 1, 1, 0, 0, 0) previous = Time.utc(2007, 12, 31, 23, 59, 0) Time.stubs(:now).returns(current) @schedule.must be_match(previous) end it "should not match if the times are 59 minutes apart and the current minute is 59" do current = Time.utc(2009, 2, 1, 12, 59, 0) previous = Time.utc(2009, 2, 1, 12, 0, 0) Time.stubs(:now).returns(current) @schedule.must_not be_match(previous) end end describe Puppet::Type.type(:schedule), "when matching daily by number" do before do @schedule[:period] = :daily @schedule[:periodmatch] = :number end it "should match if the times are one minute apart and the current minute and hour are 0" do current = Time.utc(2010, "nov", 7, 0, 0, 0) # Now set the previous time to one minute before that previous = current - 60 Time.stubs(:now).returns(current) @schedule.must be_match(previous) end it "should not match if the times are 23 hours and 58 minutes apart and the current hour is 23 and the current minute is 59" do # Reset the previous time to 00:00:00 previous = Time.utc(2010, "nov", 7, 0, 0, 0) # Set the current time to 23:59 now = previous + (23 * 3600) + (59 * 60) Time.stubs(:now).returns(now) @schedule.must_not be_match(previous) end end describe Puppet::Type.type(:schedule), "when matching weekly by number" do before do @schedule[:period] = :weekly @schedule[:periodmatch] = :number end it "should match if the previous time is prior to the most recent Sunday" do now = Time.utc(2010, "nov", 11, 0, 0, 0) # Thursday Time.stubs(:now).returns(now) previous = Time.utc(2010, "nov", 6, 23, 59, 59) # Sat @schedule.must be_match(previous) end it "should not match if the previous time is after the most recent Saturday" do now = Time.utc(2010, "nov", 11, 0, 0, 0) # Thursday Time.stubs(:now).returns(now) previous = Time.utc(2010, "nov", 7, 0, 0, 0) # Sunday @schedule.must_not be_match(previous) end end describe Puppet::Type.type(:schedule), "when matching monthly by number" do before do @schedule[:period] = :monthly @schedule[:periodmatch] = :number end it "should match when the previous time is prior to the first day of this month" do now = Time.utc(2010, "nov", 8, 00, 59, 59) Time.stubs(:now).returns(now) previous = Time.utc(2010, "oct", 31, 23, 59, 59) @schedule.must be_match(previous) end it "should not match when the previous time is after the last day of last month" do now = Time.utc(2010, "nov", 8, 00, 59, 59) Time.stubs(:now).returns(now) previous = Time.utc(2010, "nov", 1, 0, 0, 0) @schedule.must_not be_match(previous) end end describe Puppet::Type.type(:schedule), "when matching with a repeat greater than one" do before do @schedule[:period] = :daily @schedule[:repeat] = 2 Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should fail if the periodmatch is 'number'" do @schedule[:periodmatch] = :number proc { @schedule[:repeat] = 2 }.must raise_error(Puppet::Error) end it "should match if the previous run was further away than the distance divided by the repeat" do previous = Time.now - (3600 * 13) @schedule.must be_match(previous) end it "should not match if the previous run was closer than the distance divided by the repeat" do previous = Time.now - (3600 * 11) @schedule.must_not be_match(previous) end end describe Puppet::Type.type(:schedule), "when matching days of the week" do before do # 2011-05-23 is a Monday Time.stubs(:now).returns(Time.local(2011, "may", 23, 11, 0, 0)) end it "should raise an error if the weekday is 'Someday'" do proc { @schedule[:weekday] = "Someday" }.should raise_error(Puppet::Error) end it "should raise an error if the weekday is '7'" do proc { @schedule[:weekday] = "7" }.should raise_error(Puppet::Error) end it "should accept all full weekday names as valid values" do proc { @schedule[:weekday] = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'] }.should_not raise_error end it "should accept all short weekday names as valid values" do proc { @schedule[:weekday] = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] }.should_not raise_error end it "should match if the weekday is 'Monday'" do @schedule[:weekday] = "Monday" @schedule.match?.should be_true end it "should match if the weekday is 'Mon'" do @schedule[:weekday] = "Mon" @schedule.match?.should be_true end it "should match if the weekday is '1'" do @schedule[:weekday] = "1" @schedule.match?.should be_true end it "should not match if the weekday is Tuesday" do @schedule[:weekday] = "Tuesday" @schedule.should_not be_match end it "should match if weekday is ['Sun', 'Mon']" do @schedule[:weekday] = ["Sun", "Mon"] @schedule.match?.should be_true end it "should not match if weekday is ['Sun', 'Tue']" do @schedule[:weekday] = ["Sun", "Tue"] @schedule.should_not be_match end it "should match if the weekday is 'Monday'" do @schedule[:weekday] = "Monday" @schedule.match?.should be_true end it "should match if the weekday is 'Mon'" do @schedule[:weekday] = "Mon" @schedule.match?.should be_true end it "should match if the weekday is '1'" do @schedule[:weekday] = "1" @schedule.match?.should be_true end it "should not match if the weekday is Tuesday" do @schedule[:weekday] = "Tuesday" @schedule.should_not be_match end it "should match if weekday is ['Sun', 'Mon']" do @schedule[:weekday] = ["Sun", "Mon"] @schedule.match?.should be_true end end describe Puppet::Type.type(:schedule), "when matching days of week and ranges spanning days, day 1" do before do # Test with ranges and days-of-week both set. 2011-03-31 was a Thursday. Time.stubs(:now).returns(Time.local(2011, "mar", 31, 22, 30, 0)) end it "should match when the range and day of week matches" do @schedule[:range] = "22:00:00 - 02:00:00" @schedule[:weekday] = "Thursday" @schedule.must be_match end it "should not match when the range doesn't match even if the day-of-week matches" do @schedule[:range] = "23:30:00 - 21:00:00" @schedule[:weekday] = "Thursday" @schedule.must_not be_match end it "should not match when day-of-week doesn't match even if the range matches (1 day later)" do @schedule[:range] = "22:00:00 - 01:00:00" @schedule[:weekday] = "Friday" @schedule.must_not be_match end it "should not match when day-of-week doesn't match even if the range matches (1 day earlier)" do @schedule[:range] = "22:00:00 - 01:00:00" @schedule[:weekday] = "Wednesday" @schedule.must_not be_match end end describe Puppet::Type.type(:schedule), "when matching days of week and ranges spanning days, day 2" do before do # 2011-03-31 was a Thursday. As the end-time of a day spanning match, that means # we need to match on Wednesday. Time.stubs(:now).returns(Time.local(2011, "mar", 31, 1, 30, 0)) end it "should match when the range matches and the day of week should match" do @schedule[:range] = "22:00:00 - 02:00:00" @schedule[:weekday] = "Wednesday" @schedule.must be_match end it "should not match when the range does not match and the day of week should match" do @schedule[:range] = "22:00:00 - 01:00:00" @schedule[:weekday] = "Thursday" @schedule.must_not be_match end it "should not match when the range matches but the day-of-week does not (1 day later)" do @schedule[:range] = "22:00:00 - 02:00:00" @schedule[:weekday] = "Thursday" @schedule.must_not be_match end it "should not match when the range matches but the day-of-week does not (1 day later)" do @schedule[:range] = "22:00:00 - 02:00:00" @schedule[:weekday] = "Tuesday" @schedule.must_not be_match end end end diff --git a/spec/unit/type/scheduled_task_spec.rb b/spec/unit/type/scheduled_task_spec.rb index 17d84900e..48aba15c9 100644 --- a/spec/unit/type/scheduled_task_spec.rb +++ b/spec/unit/type/scheduled_task_spec.rb @@ -1,102 +1,102 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:scheduled_task), :if => Puppet.features.microsoft_windows? do it 'should use name as the namevar' do described_class.new( :title => 'Foo', :command => 'C:\Windows\System32\notepad.exe' ).name.must == 'Foo' end describe 'when setting the command' do it 'should accept an absolute path to the command' do described_class.new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe')[:command].should == 'C:\Windows\System32\notepad.exe' end it 'should fail if the path to the command is not absolute' do expect { described_class.new(:name => 'Test Task', :command => 'notepad.exe') }.to raise_error( Puppet::Error, /Parameter command failed: Must be specified using an absolute path\./ ) end end describe 'when setting the command arguments' do it 'should fail if provided an array' do expect { described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :arguments => ['/a', '/b', '/c'] ) }.to raise_error( Puppet::Error, /Parameter arguments failed: Must be specified as a single string/ ) end it 'should accept a string' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :arguments => '/a /b /c' )[:arguments].should == ['/a /b /c'] end it 'should allow not specifying any command arguments' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe' )[:arguments].should_not be end end describe 'when setting whether the task is enabled or not' do end describe 'when setting the working directory' do it 'should accept an absolute path to the working directory' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :working_dir => 'C:\Windows\System32' )[:working_dir].should == 'C:\Windows\System32' end it 'should fail if the path to the working directory is not absolute' do expect { described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :working_dir => 'Windows\System32' ) }.to raise_error( Puppet::Error, /Parameter working_dir failed: Must be specified using an absolute path/ ) end it 'should allow not specifying any working directory' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe' )[:working_dir].should_not be end end describe 'when setting the trigger' do it 'should delegate to the provider to validate the trigger' do described_class.defaultprovider.any_instance.expects(:validate_trigger).returns(true) described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :trigger => {'schedule' => 'once', 'start_date' => '2011-09-16', 'start_time' => '13:20'} ) end end end diff --git a/spec/unit/type/selboolean_spec.rb b/spec/unit/type/selboolean_spec.rb index aa495af4a..ccefb55ee 100755 --- a/spec/unit/type/selboolean_spec.rb +++ b/spec/unit/type/selboolean_spec.rb @@ -1,44 +1,44 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:selboolean), "when validating attributes" do [:name, :persistent].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:selboolean).attrtype(param).should == :param end end it "should have a value property" do Puppet::Type.type(:selboolean).attrtype(:value).should == :property end end describe Puppet::Type.type(:selboolean), "when validating values" do before do @class = Puppet::Type.type(:selboolean) @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true @class.stubs(:defaultprovider).returns(@provider_class) @class.stubs(:provider).returns(@provider_class) @provider = stub 'provider', :class => @provider_class, :clear => nil @provider_class.stubs(:new).returns(@provider) end it "should support :on as a value to :value" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on) end it "should support :off as a value to :value" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :off) end it "should support :true as a value to :persistent" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :true) end it "should support :false as a value to :persistent" do Puppet::Type.type(:selboolean).new(:name => "yay", :value => :on, :persistent => :false) end end diff --git a/spec/unit/type/selmodule_spec.rb b/spec/unit/type/selmodule_spec.rb index c067de01d..599efd0ba 100755 --- a/spec/unit/type/selmodule_spec.rb +++ b/spec/unit/type/selmodule_spec.rb @@ -1,17 +1,17 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:selmodule), "when validating attributes" do [:name, :selmoduledir, :selmodulepath].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:selmodule).attrtype(param).should == :param end end [:ensure, :syncversion].each do |param| it "should have a #{param} property" do Puppet::Type.type(:selmodule).attrtype(param).should == :property end end end diff --git a/spec/unit/type/service_spec.rb b/spec/unit/type/service_spec.rb index 3965ba9dd..a9373eb52 100755 --- a/spec/unit/type/service_spec.rb +++ b/spec/unit/type/service_spec.rb @@ -1,248 +1,248 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:service) do it "should have an :enableable feature that requires the :enable, :disable, and :enabled? methods" do Puppet::Type.type(:service).provider_feature(:enableable).methods.should == [:disable, :enable, :enabled?] end it "should have a :refreshable feature that requires the :restart method" do Puppet::Type.type(:service).provider_feature(:refreshable).methods.should == [:restart] end end describe Puppet::Type.type(:service), "when validating attributes" do [:name, :binary, :hasstatus, :path, :pattern, :start, :restart, :stop, :status, :hasrestart, :control].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:service).attrtype(param).should == :param end end [:ensure, :enable].each do |param| it "should have an #{param} property" do Puppet::Type.type(:service).attrtype(param).should == :property end end end describe Puppet::Type.type(:service), "when validating attribute values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:service).defaultprovider, :clear => nil, :controllable? => false Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(@provider) end it "should support :running as a value to :ensure" do Puppet::Type.type(:service).new(:name => "yay", :ensure => :running) end it "should support :stopped as a value to :ensure" do Puppet::Type.type(:service).new(:name => "yay", :ensure => :stopped) end it "should alias the value :true to :running in :ensure" do svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => true) svc.should(:ensure).should == :running end it "should alias the value :false to :stopped in :ensure" do svc = Puppet::Type.type(:service).new(:name => "yay", :ensure => false) svc.should(:ensure).should == :stopped end it "should support :true as a value to :enable" do Puppet::Type.type(:service).new(:name => "yay", :enable => :true) end it "should support :false as a value to :enable" do Puppet::Type.type(:service).new(:name => "yay", :enable => :false) end it "should support :manual as a value to :enable on Windows" do Puppet.features.stubs(:microsoft_windows?).returns true Puppet::Type.type(:service).new(:name => "yay", :enable => :manual) end it "should not support :manual as a value to :enable when not on Windows" do Puppet.features.stubs(:microsoft_windows?).returns false expect { Puppet::Type.type(:service).new(:name => "yay", :enable => :manual) }.to raise_error( Puppet::Error, /Setting enable to manual is only supported on Microsoft Windows\./ ) end it "should support :true as a value to :hasstatus" do Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :true) end it "should support :false as a value to :hasstatus" do Puppet::Type.type(:service).new(:name => "yay", :hasstatus => :false) end it "should specify :true as the default value of hasstatus" do Puppet::Type.type(:service).new(:name => "yay")[:hasstatus].should == :true end it "should support :true as a value to :hasrestart" do Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :true) end it "should support :false as a value to :hasrestart" do Puppet::Type.type(:service).new(:name => "yay", :hasrestart => :false) end it "should allow setting the :enable parameter if the provider has the :enableable feature" do Puppet::Type.type(:service).defaultprovider.stubs(:supports_parameter?).returns(true) Puppet::Type.type(:service).defaultprovider.expects(:supports_parameter?).with(Puppet::Type.type(:service).attrclass(:enable)).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :enable => true) svc.should(:enable).should == :true end it "should not allow setting the :enable parameter if the provider is missing the :enableable feature" do Puppet::Type.type(:service).defaultprovider.stubs(:supports_parameter?).returns(true) Puppet::Type.type(:service).defaultprovider.expects(:supports_parameter?).with(Puppet::Type.type(:service).attrclass(:enable)).returns(false) svc = Puppet::Type.type(:service).new(:name => "yay", :enable => true) svc.should(:enable).should be_nil end it "should split paths on '#{File::PATH_SEPARATOR}'" do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :path => "/one/two#{File::PATH_SEPARATOR}/three/four") svc[:path].should == %w{/one/two /three/four} end it "should accept arrays of paths joined by '#{File::PATH_SEPARATOR}'" do FileTest.stubs(:exist?).returns(true) FileTest.stubs(:directory?).returns(true) svc = Puppet::Type.type(:service).new(:name => "yay", :path => ["/one#{File::PATH_SEPARATOR}/two", "/three#{File::PATH_SEPARATOR}/four"]) svc[:path].should == %w{/one /two /three /four} end end describe Puppet::Type.type(:service), "when setting default attribute values" do it "should default to the provider's default path if one is available" do FileTest.stubs(:directory?).returns(true) FileTest.stubs(:exist?).returns(true) Puppet::Type.type(:service).defaultprovider.stubs(:respond_to?).returns(true) Puppet::Type.type(:service).defaultprovider.stubs(:defpath).returns("testing") svc = Puppet::Type.type(:service).new(:name => "other") svc[:path].should == ["testing"] end it "should default 'pattern' to the binary if one is provided" do svc = Puppet::Type.type(:service).new(:name => "other", :binary => "/some/binary") svc[:pattern].should == "/some/binary" end it "should default 'pattern' to the name if no pattern is provided" do svc = Puppet::Type.type(:service).new(:name => "other") svc[:pattern].should == "other" end it "should default 'control' to the upcased service name with periods replaced by underscores if the provider supports the 'controllable' feature" do provider = stub 'provider', :controllable? => true, :class => Puppet::Type.type(:service).defaultprovider, :clear => nil Puppet::Type.type(:service).defaultprovider.stubs(:new).returns(provider) svc = Puppet::Type.type(:service).new(:name => "nfs.client") svc[:control].should == "NFS_CLIENT_START" end end describe Puppet::Type.type(:service), "when retrieving the host's current state" do before do @service = Puppet::Type.type(:service).new(:name => "yay") end it "should use the provider's status to determine whether the service is running" do @service.provider.expects(:status).returns(:yepper) @service[:ensure] = :running @service.property(:ensure).retrieve.should == :yepper end it "should ask the provider whether it is enabled" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service.provider.expects(:enabled?).returns(:yepper) @service[:enable] = true @service.property(:enable).retrieve.should == :yepper end end describe Puppet::Type.type(:service), "when changing the host" do before do @service = Puppet::Type.type(:service).new(:name => "yay") end it "should start the service if it is supposed to be running" do @service[:ensure] = :running @service.provider.expects(:start) @service.property(:ensure).sync end it "should stop the service if it is supposed to be stopped" do @service[:ensure] = :stopped @service.provider.expects(:stop) @service.property(:ensure).sync end it "should enable the service if it is supposed to be enabled" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service[:enable] = true @service.provider.expects(:enable) @service.property(:enable).sync end it "should disable the service if it is supposed to be disabled" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service[:enable] = false @service.provider.expects(:disable) @service.property(:enable).sync end it "should sync the service's enable state when changing the state of :ensure if :enable is being managed" do @service.provider.class.stubs(:supports_parameter?).returns(true) @service[:enable] = false @service[:ensure] = :stopped @service.property(:enable).expects(:retrieve).returns("whatever") @service.property(:enable).expects(:insync?).returns(false) @service.property(:enable).expects(:sync) @service.provider.stubs(:stop) @service.property(:ensure).sync end end describe Puppet::Type.type(:service), "when refreshing the service" do before do @service = Puppet::Type.type(:service).new(:name => "yay") end it "should restart the service if it is running" do @service[:ensure] = :running @service.provider.expects(:status).returns(:running) @service.provider.expects(:restart) @service.refresh end it "should restart the service if it is running, even if it is supposed to stopped" do @service[:ensure] = :stopped @service.provider.expects(:status).returns(:running) @service.provider.expects(:restart) @service.refresh end it "should not restart the service if it is not running" do @service[:ensure] = :running @service.provider.expects(:status).returns(:stopped) @service.refresh end it "should add :ensure as a property if it is not being managed" do @service.provider.expects(:status).returns(:running) @service.provider.expects(:restart) @service.refresh end end diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb index 2beb61914..0cdc13daa 100755 --- a/spec/unit/type/ssh_authorized_key_spec.rb +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -1,255 +1,255 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) describe ssh_authorized_key, :unless => Puppet.features.microsoft_windows? do include PuppetSpec::Files before do @class = Puppet::Type.type(:ssh_authorized_key) @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true @class.stubs(:defaultprovider).returns(@provider_class) @class.stubs(:provider).returns(@provider_class) @provider = stub 'provider', :class => @provider_class, :file_path => make_absolute("/tmp/whatever"), :clear => nil @provider_class.stubs(:new).returns(@provider) @catalog = Puppet::Resource::Catalog.new end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:type, :key, :user, :target, :options, :ensure].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end end describe "when validating values" do describe "for name" do it "should support valid names" do proc { @class.new(:name => "username", :ensure => :present, :user => "nobody") }.should_not raise_error proc { @class.new(:name => "username@hostname", :ensure => :present, :user => "nobody") }.should_not raise_error end it "should support whitespace" do proc { @class.new(:name => "my test", :ensure => :present, :user => "nobody") }.should_not raise_error end end describe "for ensure" do it "should support :present" do proc { @class.new(:name => "whev", :ensure => :present, :user => "nobody") }.should_not raise_error end it "should support :absent" do proc { @class.new(:name => "whev", :ensure => :absent, :user => "nobody") }.should_not raise_error end it "should not support other values" do proc { @class.new(:name => "whev", :ensure => :foo, :user => "nobody") }.should raise_error(Puppet::Error, /Invalid value/) end end describe "for type" do [:'ssh-dss', :'ssh-rsa', :rsa, :dsa, :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521'].each do |keytype| it "should support #{keytype}" do proc { @class.new(:name => "whev", :type => keytype, :user => "nobody") }.should_not raise_error end end it "should alias :rsa to :ssh-rsa" do key = @class.new(:name => "whev", :type => :rsa, :user => "nobody") key.should(:type).should == :'ssh-rsa' end it "should alias :dsa to :ssh-dss" do key = @class.new(:name => "whev", :type => :dsa, :user => "nobody") key.should(:type).should == :'ssh-dss' end it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa" do proc { @class.new(:name => "whev", :type => :something) }.should raise_error(Puppet::Error,/Invalid value/) end end describe "for key" do it "should support a valid key like a 1024 bit rsa key" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCPfzW2ry7XvMc6E5Kj2e5fF/YofhKEvsNMUogR3PGL/HCIcBlsEjKisrY0aYgD8Ikp7ZidpXLbz5dBsmPy8hJiBWs5px9ZQrB/EOQAwXljvj69EyhEoGawmxQMtYw+OAIKHLJYRuk1QiHAMHLp5piqem8ZCV2mLb9AsJ6f7zUVw==')}.should_not raise_error end it "should support a valid key like a 4096 bit rsa key" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAACAQDEY4pZFyzSfRc9wVWI3DfkgT/EL033UZm/7x1M+d+lBD00qcpkZ6CPT7lD3Z+vylQlJ5S8Wcw6C5Smt6okZWY2WXA9RCjNJMIHQbJAzwuQwgnwU/1VMy9YPp0tNVslg0sUUgpXb13WW4mYhwxyGmIVLJnUrjrQmIFhtfHsJAH8ZVqCWaxKgzUoC/YIu1u1ScH93lEdoBPLlwm6J0aiM7KWXRb7Oq1nEDZtug1zpX5lhgkQWrs0BwceqpUbY+n9sqeHU5e7DCyX/yEIzoPRW2fe2Gx1Iq6JKM/5NNlFfaW8rGxh3Z3S1NpzPHTRjw8js3IeGiV+OPFoaTtM1LsWgPDSBlzIdyTbSQR7gKh0qWYCNV/7qILEfa0yIFB5wIo4667iSPZw2pNgESVtenm8uXyoJdk8iWQ4mecdoposV/znknNb2GPgH+n/2vme4btZ0Sl1A6rev22GQjVgbWOn8zaDglJ2vgCN1UAwmq41RXprPxENGeLnWQppTnibhsngu0VFllZR5kvSIMlekLRSOFLFt92vfd+tk9hZIiKm9exxcbVCGGQPsf6dZ27rTOmg0xM2Sm4J6RRKuz79HQgA4Eg18+bqRP7j/itb89DmtXEtoZFAsEJw8IgIfeGGDtHTkfAlAC92mtK8byeaxGq57XCTKbO/r5gcOMElZHy1AcB8kw==')}.should_not raise_error end it "should support a valid key like a 1024 bit dsa key" do proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody", :key => 'AAAAB3NzaC1kc3MAAACBAI80iR78QCgpO4WabVqHHdEDigOjUEHwIjYHIubR/7u7DYrXY+e+TUmZ0CVGkiwB/0yLHK5dix3Y/bpj8ZiWCIhFeunnXccOdE4rq5sT2V3l1p6WP33RpyVYbLmeuHHl5VQ1CecMlca24nHhKpfh6TO/FIwkMjghHBfJIhXK+0w/AAAAFQDYzLupuMY5uz+GVrcP+Kgd8YqMmwAAAIB3SVN71whLWjFPNTqGyyIlMy50624UfNOaH4REwO+Of3wm/cE6eP8n75vzTwQGBpJX3BPaBGW1S1Zp/DpTOxhCSAwZzAwyf4WgW7YyAOdxN3EwTDJZeyiyjWMAOjW9/AOWt9gtKg0kqaylbMHD4kfiIhBzo31ZY81twUzAfN7angAAAIBfva8sTSDUGKsWWIXkdbVdvM4X14K4gFdy0ZJVzaVOtZ6alysW6UQypnsl6jfnbKvsZ0tFgvcX/CPyqNY/gMR9lyh/TCZ4XQcbqeqYPuceGehz+jL5vArfqsW2fJYFzgCcklmr/VxtP5h6J/T0c9YcDgc/xIfWdZAlznOnphI/FA==')}.should_not raise_error end it "should not support whitespaces" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAA FA==')}.should raise_error(Puppet::Error,/Key must not contain whitespace/) end end describe "for options" do it "should support flags as options" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority')}.should_not raise_error proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'no-port-forwarding')}.should_not raise_error end it "should support key-value pairs as options" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'command="command"')}.should_not raise_error end it "should support key-value pairs where value consist of multiple items" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'from="*.domain1,host1.domain2"')}.should_not raise_error end it "should support environments as options" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'environment="NAME=value"')}.should_not raise_error end it "should support multiple options as an array" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ['cert-authority','environment="NAME=value"'])}.should_not raise_error end it "should not support a comma separated list" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority,no-port-forwarding')}.should raise_error(Puppet::Error, /must be provided as an array/) end it "should use :absent as a default value" do @class.new(:name => "whev", :type => :rsa, :user => "nobody").should(:options).should == [:absent] end it "property should return well formed string of arrays from is_to_s" do resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) resource.property(:options).is_to_s(["a","b","c"]).should == "a,b,c" end it "property should return well formed string of arrays from should_to_s" do resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) resource.property(:options).should_to_s(["a","b","c"]).should == "a,b,c" end end describe "for user" do it "should support present users" do proc { @class.new(:name => "whev", :type => :rsa, :user => "root") }.should_not raise_error end it "should support absent users" do proc { @class.new(:name => "whev", :type => :rsa, :user => "ihopeimabsent") }.should_not raise_error end end describe "for target" do it "should support absolute paths" do proc { @class.new(:name => "whev", :type => :rsa, :target => "/tmp/here") }.should_not raise_error end it "should use the user's path if not explicitly specified" do @class.new(:name => "whev", :user => 'root').should(:target).should == File.expand_path("~root/.ssh/authorized_keys") end it "should not consider the user's path if explicitly specified" do @class.new(:name => "whev", :user => 'root', :target => '/tmp/here').should(:target).should == '/tmp/here' end it "should inform about an absent user" do Puppet::Log.level = :debug @class.new(:name => "whev", :user => 'idontexist').should(:target) @logs.map(&:message).should include("The required user is not yet present on the system") end end end describe "when neither user nor target is specified" do it "should raise an error" do proc do @class.new( :name => "Test", :key => "AAA", :type => "ssh-rsa", :ensure => :present) end.should raise_error(Puppet::Error,/user.*or.*target.*mandatory/) end end describe "when both target and user are specified" do it "should use target" do resource = @class.new( :name => "Test", :user => "root", :target => "/tmp/blah" ) resource.should(:target).should == "/tmp/blah" end end describe "when user is specified" do it "should determine target" do resource = @class.create( :name => "Test", :user => "root" ) target = File.expand_path("~root/.ssh/authorized_keys") resource.should(:target).should == target end # Bug #2124 - ssh_authorized_key always changes target if target is not defined it "should not raise spurious change events" do resource = @class.new(:name => "Test", :user => "root") target = File.expand_path("~root/.ssh/authorized_keys") resource.property(:target).safe_insync?(target).should == true end end describe "when calling validate" do it "should not crash on a non-existant user" do resource = @class.create( :name => "Test", :user => "ihopesuchuserdoesnotexist" ) proc { resource.validate }.should_not raise_error end end end diff --git a/spec/unit/type/sshkey_spec.rb b/spec/unit/type/sshkey_spec.rb index ae4967856..f9376a6bb 100755 --- a/spec/unit/type/sshkey_spec.rb +++ b/spec/unit/type/sshkey_spec.rb @@ -1,68 +1,68 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' sshkey = Puppet::Type.type(:sshkey) describe sshkey do before do @class = sshkey end it "should have :name its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:host_aliases, :ensure, :key, :type].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end end describe "when validating values" do [:'ssh-dss', :'ssh-rsa', :rsa, :dsa, :'ecdsa-sha2-nistp256', :'ecdsa-sha2-nistp384', :'ecdsa-sha2-nistp521'].each do |keytype| it "should support #{keytype} as a type value" do proc { @class.new(:name => "foo", :type => keytype) }.should_not raise_error end end it "should alias :rsa to :ssh-rsa" do key = @class.new(:name => "foo", :type => :rsa) key.should(:type).should == :'ssh-rsa' end it "should alias :dsa to :ssh-dss" do key = @class.new(:name => "foo", :type => :dsa) key.should(:type).should == :'ssh-dss' end it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa for type" do proc { @class.new(:name => "whev", :type => :'ssh-dsa') }.should raise_error(Puppet::Error) end it "should accept one host_alias" do proc { @class.new(:name => "foo", :host_aliases => 'foo.bar.tld') }.should_not raise_error end it "should accept multiple host_aliases as an array" do proc { @class.new(:name => "foo", :host_aliases => ['foo.bar.tld','10.0.9.9']) }.should_not raise_error end it "should not accept spaces in any host_alias" do proc { @class.new(:name => "foo", :host_aliases => ['foo.bar.tld','foo bar']) }.should raise_error(Puppet::Error) end it "should not accept aliases in the resourcename" do proc { @class.new(:name => 'host,host.domain,ip') }.should raise_error(Puppet::Error) end end end diff --git a/spec/unit/type/stage_spec.rb b/spec/unit/type/stage_spec.rb index f5fed6c3c..515eec054 100755 --- a/spec/unit/type/stage_spec.rb +++ b/spec/unit/type/stage_spec.rb @@ -1,8 +1,8 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:stage) do it "should have a 'name' parameter'" do Puppet::Type.type(:stage).new(:name => :foo)[:name].should == :foo end end diff --git a/spec/unit/type/tidy_spec.rb b/spec/unit/type/tidy_spec.rb index bf892e836..a0c45d505 100755 --- a/spec/unit/type/tidy_spec.rb +++ b/spec/unit/type/tidy_spec.rb @@ -1,426 +1,426 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/file_bucket/dipper' tidy = Puppet::Type.type(:tidy) describe tidy do include PuppetSpec::Files before do @basepath = make_absolute("/what/ever") Puppet.settings.stubs(:use) # for an unknown reason some of these specs fails when run individually # with a failed expectation on File.lstat in the autoloader. File.stubs(:lstat) end it "should use :lstat when stating a file" do resource = tidy.new :path => "/foo/bar", :age => "1d" stat = mock 'stat' File.expects(:lstat).with("/foo/bar").returns stat resource.stat("/foo/bar").should == stat end [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:tidy).attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} param" do Puppet::Type.type(:tidy).attrclass(param).doc.should be_instance_of(String) end end describe "when validating parameter values" do describe "for 'recurse'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should allow 'true'" do lambda { @tidy[:recurse] = true }.should_not raise_error end it "should allow 'false'" do lambda { @tidy[:recurse] = false }.should_not raise_error end it "should allow integers" do lambda { @tidy[:recurse] = 10 }.should_not raise_error end it "should allow string representations of integers" do lambda { @tidy[:recurse] = "10" }.should_not raise_error end it "should allow 'inf'" do lambda { @tidy[:recurse] = "inf" }.should_not raise_error end it "should not allow arbitrary values" do lambda { @tidy[:recurse] = "whatever" }.should raise_error end end describe "for 'matches'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should object if matches is given with recurse is not specified" do lambda { @tidy[:matches] = '*.doh' }.should raise_error end it "should object if matches is given and recurse is 0" do lambda { @tidy[:recurse] = 0; @tidy[:matches] = '*.doh' }.should raise_error end it "should object if matches is given and recurse is false" do lambda { @tidy[:recurse] = false; @tidy[:matches] = '*.doh' }.should raise_error end it "should not object if matches is given and recurse is > 0" do lambda { @tidy[:recurse] = 1; @tidy[:matches] = '*.doh' }.should_not raise_error end it "should not object if matches is given and recurse is true" do lambda { @tidy[:recurse] = true; @tidy[:matches] = '*.doh' }.should_not raise_error end end end describe "when matching files by age" do convertors = { :second => 1, :minute => 60 } convertors[:hour] = convertors[:minute] * 60 convertors[:day] = convertors[:hour] * 24 convertors[:week] = convertors[:day] * 7 convertors.each do |unit, multiple| it "should consider a #{unit} to be #{multiple} seconds" do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :age => "5#{unit.to_s[0..0]}" @tidy[:age].should == 5 * multiple end end end describe "when matching files by size" do convertors = { :b => 0, :kb => 1, :mb => 2, :gb => 3, :tb => 4 } convertors.each do |unit, multiple| it "should consider a #{unit} to be 1024^#{multiple} bytes" do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :size => "5#{unit}" total = 5 multiple.times { total *= 1024 } @tidy[:size].should == total end end end describe "when tidying" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "directory" File.stubs(:lstat).with(@basepath).returns @stat end describe "and generating files" do it "should set the backup on the file if backup is set on the tidy instance" do @tidy[:backup] = "whatever" Puppet::Type.type(:file).expects(:new).with { |args| args[:backup] == "whatever" } @tidy.mkfile(@basepath) end it "should set the file's path to the tidy's path" do Puppet::Type.type(:file).expects(:new).with { |args| args[:path] == @basepath } @tidy.mkfile(@basepath) end it "should configure the file for deletion" do Puppet::Type.type(:file).expects(:new).with { |args| args[:ensure] == :absent } @tidy.mkfile(@basepath) end it "should force deletion on the file" do Puppet::Type.type(:file).expects(:new).with { |args| args[:force] == true } @tidy.mkfile(@basepath) end it "should do nothing if the targeted file does not exist" do File.expects(:lstat).with(@basepath).raises Errno::ENOENT @tidy.generate.should == [] end end describe "and recursion is not used" do it "should generate a file resource if the file should be tidied" do @tidy.expects(:tidy?).with(@basepath).returns true file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") @tidy.expects(:mkfile).with(@basepath).returns file @tidy.generate.should == [file] end it "should do nothing if the file should not be tidied" do @tidy.expects(:tidy?).with(@basepath).returns false @tidy.expects(:mkfile).never @tidy.generate.should == [] end end describe "and recursion is used" do before do @tidy[:recurse] = true Puppet::FileServing::Fileset.any_instance.stubs(:stat).returns mock("stat") @fileset = Puppet::FileServing::Fileset.new(@basepath) Puppet::FileServing::Fileset.stubs(:new).returns @fileset end it "should use a Fileset for infinite recursion" do Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should use a Fileset for limited recursion" do @tidy[:recurse] = 42 Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true, :recurselimit => 42).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should generate a file resource for every file that should be tidied but not for files that should not be tidied" do @fileset.expects(:files).returns %w{. one two} @tidy.expects(:tidy?).with(@basepath).returns true @tidy.expects(:tidy?).with(@basepath+"/one").returns true @tidy.expects(:tidy?).with(@basepath+"/two").returns false file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") @tidy.expects(:mkfile).with(@basepath).returns file @tidy.expects(:mkfile).with(@basepath+"/one").returns file @tidy.generate end end describe "and determining whether a file matches provided glob patterns" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :recurse => 1 @tidy[:matches] = %w{*foo* *bar*} @stat = mock 'stat' @matcher = @tidy.parameter(:matches) end it "should always convert the globs to an array" do @matcher.value = "*foo*" @matcher.value.should == %w{*foo*} end it "should return true if any pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.must be_tidy("/file/yaybarness", @stat) end it "should return false if no pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.should_not be_tidy("/file/yayness", @stat) end end describe "and determining whether a file is too old" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat' @tidy[:age] = "1s" @tidy[:type] = "mtime" @ager = @tidy.parameter(:age) end it "should use the age type specified" do @tidy[:type] = :ctime @stat.expects(:ctime).returns(Time.now) @ager.tidy?(@basepath, @stat) end it "should return false if the file is more recent than the specified age" do @stat.expects(:mtime).returns(Time.now) @ager.should_not be_tidy(@basepath, @stat) end it "should return true if the file is older than the specified age" do @stat.expects(:mtime).returns(Time.now - 10) @ager.must be_tidy(@basepath, @stat) end end describe "and determining whether a file is too large" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" @tidy[:size] = "1kb" @sizer = @tidy.parameter(:size) end it "should return false if the file is smaller than the specified size" do @stat.expects(:size).returns(4) # smaller than a kilobyte @sizer.should_not be_tidy(@basepath, @stat) end it "should return true if the file is larger than the specified size" do @stat.expects(:size).returns(1500) # larger than a kilobyte @sizer.must be_tidy(@basepath, @stat) end it "should return true if the file is equal to the specified size" do @stat.expects(:size).returns(1024) @sizer.must be_tidy(@basepath, @stat) end end describe "and determining whether a file should be tidied" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" File.stubs(:lstat).with(@basepath).returns @stat end it "should not try to recurse if the file does not exist" do @tidy[:recurse] = true File.stubs(:lstat).with(@basepath).returns nil @tidy.generate.should == [] end it "should not be tidied if the file does not exist" do File.expects(:lstat).with(@basepath).raises Errno::ENOENT @tidy.should_not be_tidy(@basepath) end it "should not be tidied if the user has no access to the file" do File.expects(:lstat).with(@basepath).raises Errno::EACCES @tidy.should_not be_tidy(@basepath) end it "should not be tidied if it is a directory and rmdirs is set to false" do stat = mock 'stat', :ftype => "directory" File.expects(:lstat).with(@basepath).returns stat @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match any provided globs" do @tidy[:recurse] = 1 @tidy[:matches] = "globs" matches = @tidy.parameter(:matches) matches.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match aging requirements" do @tidy[:age] = "1d" ager = @tidy.parameter(:age) ager.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match size requirements" do @tidy[:size] = "1b" sizer = @tidy.parameter(:size) sizer.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should tidy a file if age and size are set but only size matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns true @tidy.parameter(:age).stubs(:tidy?).returns false @tidy.should be_tidy(@basepath) end it "should tidy a file if age and size are set but only age matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns false @tidy.parameter(:age).stubs(:tidy?).returns true @tidy.should be_tidy(@basepath) end it "should tidy all files if neither age nor size is set" do @tidy.must be_tidy(@basepath) end it "should sort the results inversely by path length, so files are added to the catalog before their directories" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = Puppet::FileServing::Fileset.new(@basepath) Puppet::FileServing::Fileset.expects(:new).returns fileset fileset.expects(:files).returns %w{. one one/two} @tidy.stubs(:tidy?).returns true @tidy.generate.collect { |r| r[:path] }.should == [@basepath+"/one/two", @basepath+"/one", @basepath] end end it "should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = mock 'fileset' Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns fileset fileset.expects(:files).returns %w{. one two one/subone two/subtwo one/subone/ssone} @tidy.stubs(:tidy?).returns true result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash } { @basepath => [ @basepath+"/one", @basepath+"/two" ], @basepath+"/one" => [@basepath+"/one/subone"], @basepath+"/two" => [@basepath+"/two/subtwo"], @basepath+"/one/subone" => [@basepath+"/one/subone/ssone"] }.each do |parent, children| children.each do |child| ref = Puppet::Resource.new(:file, child) result[parent][:require].find { |req| req.to_s == ref.to_s }.should_not be_nil end end end end end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index bb235b798..6975fd16a 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -1,340 +1,340 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:user) do before :each do @provider_class = described_class.provide(:simple) do has_features :manages_expiry, :manages_password_age, :manages_passwords, :manages_solaris_rbac mk_resource_methods def create; end def delete; end def exists?; get(:ensure) != :absent; end def flush; end def self.instances; []; end end described_class.stubs(:defaultprovider).returns @provider_class end it "should be able to create a instance" do described_class.new(:name => "foo").should_not be_nil end it "should have an allows_duplicates feature" do described_class.provider_feature(:allows_duplicates).should_not be_nil end it "should have an manages_homedir feature" do described_class.provider_feature(:manages_homedir).should_not be_nil end it "should have an manages_passwords feature" do described_class.provider_feature(:manages_passwords).should_not be_nil end it "should have a manages_solaris_rbac feature" do described_class.provider_feature(:manages_solaris_rbac).should_not be_nil end it "should have a manages_expiry feature" do described_class.provider_feature(:manages_expiry).should_not be_nil end it "should have a manages_password_age feature" do described_class.provider_feature(:manages_password_age).should_not be_nil end it "should have a system_users feature" do described_class.provider_feature(:system_users).should_not be_nil end describe "instances" do it "should delegate existence questions to its provider" do @provider = @provider_class.new(:name => 'foo', :ensure => :absent) instance = described_class.new(:name => "foo", :provider => @provider) instance.exists?.should == false @provider.set(:ensure => :present) instance.exists?.should == true end end properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :password_min_age, :password_max_age, :groups, :roles, :auths, :profiles, :project, :keys, :expiry] properties.each do |property| it "should have a #{property} property" do described_class.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do described_class.attrclass(property).doc.should be_instance_of(String) end end list_properties = [:groups, :roles, :auths] list_properties.each do |property| it "should have a list '#{property}'" do described_class.attrclass(property).ancestors.should be_include(Puppet::Property::List) end end it "should have an ordered list 'profiles'" do described_class.attrclass(:profiles).ancestors.should be_include(Puppet::Property::OrderedList) end it "should have key values 'keys'" do described_class.attrclass(:keys).ancestors.should be_include(Puppet::Property::KeyValue) end describe "when retrieving all current values" do before do @provider = @provider_class.new(:name => 'foo', :ensure => :present, :uid => 15, :gid => 15) @user = described_class.new(:name => "foo", :uid => 10, :provider => @provider) end it "should return a hash containing values for all set properties" do @user[:gid] = 10 values = @user.retrieve [@user.property(:uid), @user.property(:gid)].each { |property| values.should be_include(property) } end it "should set all values to :absent if the user is absent" do @user.property(:ensure).expects(:retrieve).returns :absent @user.property(:uid).expects(:retrieve).never @user.retrieve[@user.property(:uid)].should == :absent end it "should include the result of retrieving each property's current value if the user is present" do @user.retrieve[@user.property(:uid)].should == 15 end end describe "when managing the ensure property" do it "should support a :present value" do lambda { described_class.new(:name => 'foo', :ensure => :present) }.should_not raise_error end it "should support an :absent value" do lambda { described_class.new(:name => 'foo', :ensure => :absent) }.should_not raise_error end it "should call :create on the provider when asked to sync to the :present state" do @provider = @provider_class.new(:name => 'foo', :ensure => :absent) @provider.expects(:create) described_class.new(:name => 'foo', :ensure => :present, :provider => @provider).parameter(:ensure).sync end it "should call :delete on the provider when asked to sync to the :absent state" do @provider = @provider_class.new(:name => 'foo', :ensure => :present) @provider.expects(:delete) described_class.new(:name => 'foo', :ensure => :absent, :provider => @provider).parameter(:ensure).sync end describe "and determining the current state" do it "should return :present when the provider indicates the user exists" do @provider = @provider_class.new(:name => 'foo', :ensure => :present) described_class.new(:name => 'foo', :ensure => :absent, :provider => @provider).parameter(:ensure).retrieve.should == :present end it "should return :absent when the provider indicates the user does not exist" do @provider = @provider_class.new(:name => 'foo', :ensure => :absent) described_class.new(:name => 'foo', :ensure => :present, :provider => @provider).parameter(:ensure).retrieve.should == :absent end end end describe "when managing the uid property" do it "should convert number-looking strings into actual numbers" do described_class.new(:name => 'foo', :uid => '50')[:uid].should == 50 end it "should support UIDs as numbers" do described_class.new(:name => 'foo', :uid => 50)[:uid].should == 50 end it "should support :absent as a value" do described_class.new(:name => 'foo', :uid => :absent)[:uid].should == :absent end end describe "when managing the gid" do it "should support :absent as a value" do described_class.new(:name => 'foo', :gid => :absent)[:gid].should == :absent end it "should convert number-looking strings into actual numbers" do described_class.new(:name => 'foo', :gid => '50')[:gid].should == 50 end it "should support GIDs specified as integers" do described_class.new(:name => 'foo', :gid => 50)[:gid].should == 50 end it "should support groups specified by name" do described_class.new(:name => 'foo', :gid => 'foo')[:gid].should == 'foo' end describe "when testing whether in sync" do it "should return true if no 'should' values are set" do # this is currently not the case because gid has no default value, so we would never even # call insync? on that property if param = described_class.new(:name => 'foo').parameter(:gid) param.must be_safe_insync(500) end end it "should return true if any of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 described_class.new(:name => 'baz', :gid => [ 'foo', 'bar' ]).parameter(:gid).must be_safe_insync(500) end it "should return false if none of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 described_class.new(:name => 'baz', :gid => [ 'foo', 'bar' ]).parameter(:gid).must_not be_safe_insync(700) end end describe "when syncing" do it "should use the first found, specified group as the desired value and send it to the provider" do Puppet::Util.expects(:gid).with("foo").returns nil Puppet::Util.expects(:gid).with("bar").returns 500 @provider = @provider_class.new(:name => 'foo') resource = described_class.new(:name => 'foo', :provider => @provider, :gid => [ 'foo', 'bar' ]) @provider.expects(:gid=).with 500 resource.parameter(:gid).sync end end end describe "when managing groups" do it "should support a singe group" do lambda { described_class.new(:name => 'foo', :groups => 'bar') }.should_not raise_error end it "should support multiple groups as an array" do lambda { described_class.new(:name => 'foo', :groups => [ 'bar' ]) }.should_not raise_error lambda { described_class.new(:name => 'foo', :groups => [ 'bar', 'baz' ]) }.should_not raise_error end it "should not support a comma separated list" do lambda { described_class.new(:name => 'foo', :groups => 'bar,baz') }.should raise_error(Puppet::Error, /Group names must be provided as an array/) end it "should not support an empty string" do lambda { described_class.new(:name => 'foo', :groups => '') }.should raise_error(Puppet::Error, /Group names must not be empty/) end describe "when testing is in sync" do before :each do # the useradd provider uses a single string to represent groups and so does Puppet::Property::List when converting to should values @provider = @provider_class.new(:name => 'foo', :groups => 'a,b,e,f') end it "should not care about order" do @property = described_class.new(:name => 'foo', :groups => [ 'a', 'c', 'b' ]).property(:groups) @property.must be_safe_insync([ 'a', 'b', 'c' ]) @property.must be_safe_insync([ 'a', 'c', 'b' ]) @property.must be_safe_insync([ 'b', 'a', 'c' ]) @property.must be_safe_insync([ 'b', 'c', 'a' ]) @property.must be_safe_insync([ 'c', 'a', 'b' ]) @property.must be_safe_insync([ 'c', 'b', 'a' ]) end it "should merge current value and desired value if membership minimal" do @instance = described_class.new(:name => 'foo', :groups => [ 'a', 'c', 'b' ], :provider => @provider) @instance[:membership] = :minimum @instance[:groups].should == 'a,b,c,e,f' end it "should not treat a subset of groups insync if membership inclusive" do @instance = described_class.new(:name => 'foo', :groups => [ 'a', 'c', 'b' ], :provider => @provider) @instance[:membership] = :inclusive @instance[:groups].should == 'a,b,c' end end end describe "when managing expiry" do it "should fail if given an invalid date" do lambda { described_class.new(:name => 'foo', :expiry => "200-20-20") }.should raise_error(Puppet::Error, /Expiry dates must be YYYY-MM-DD/) end end describe "when managing minimum password age" do it "should accept a negative minimum age" do expect { described_class.new(:name => 'foo', :password_min_age => '-1') }.should_not raise_error end it "should fail with an empty minimum age" do expect { described_class.new(:name => 'foo', :password_min_age => '') }.should raise_error(Puppet::Error, /minimum age must be provided as a number/) end end describe "when managing maximum password age" do it "should accept a negative maximum age" do expect { described_class.new(:name => 'foo', :password_max_age => '-1') }.should_not raise_error end it "should fail with an empty maximum age" do expect { described_class.new(:name => 'foo', :password_max_age => '') }.should raise_error(Puppet::Error, /maximum age must be provided as a number/) end end describe "when managing passwords" do before do @password = described_class.new(:name => 'foo', :password => 'mypass').parameter(:password) end it "should not include the password in the change log when adding the password" do @password.change_to_s(:absent, "mypass").should_not be_include("mypass") end it "should not include the password in the change log when changing the password" do @password.change_to_s("other", "mypass").should_not be_include("mypass") end it "should redact the password when displaying the old value" do @password.is_to_s("currentpassword").should =~ /^\[old password hash redacted\]$/ end it "should redact the password when displaying the new value" do @password.should_to_s("newpassword").should =~ /^\[new password hash redacted\]$/ end it "should fail if a ':' is included in the password" do lambda { described_class.new(:name => 'foo', :password => "some:thing") }.should raise_error(Puppet::Error, /Passwords cannot include ':'/) end it "should allow the value to be set to :absent" do lambda { described_class.new(:name => 'foo', :password => :absent) }.should_not raise_error end end describe "when manages_solaris_rbac is enabled" do it "should support a :role value for ensure" do lambda { described_class.new(:name => 'foo', :ensure => :role) }.should_not raise_error end end describe "when user has roles" do it "should autorequire roles" do testuser = described_class.new(:name => "testuser", :roles => ['testrole'] ) testrole = described_class.new(:name => "testrole") config = Puppet::Resource::Catalog.new :testing do |conf| [testuser, testrole].each { |resource| conf.add_resource resource } end Puppet::Type::User::ProviderDirectoryservice.stubs(:get_macosx_version_major).returns "10.5" rel = testuser.autorequire[0] rel.source.ref.should == testrole.ref rel.target.ref.should == testuser.ref end end end diff --git a/spec/unit/type/vlan_spec.rb b/spec/unit/type/vlan_spec.rb index 7d7a0b178..f973b1216 100755 --- a/spec/unit/type/vlan_spec.rb +++ b/spec/unit/type/vlan_spec.rb @@ -1,44 +1,44 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:vlan) do it "should have a 'name' parameter'" do Puppet::Type.type(:vlan).new(:name => "200")[:name].should == "200" end it "should have a 'device_url' parameter'" do Puppet::Type.type(:vlan).new(:name => "200", :device_url => :device)[:device_url].should == :device end it "should be applied on device" do Puppet::Type.type(:vlan).new(:name => "200").should be_appliable_to_device end it "should have an ensure property" do Puppet::Type.type(:vlan).attrtype(:ensure).should == :property end it "should have a description property" do Puppet::Type.type(:vlan).attrtype(:description).should == :property end describe "when validating attribute values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:vlan).defaultprovider, :clear => nil Puppet::Type.type(:vlan).defaultprovider.stubs(:new).returns(@provider) end it "should support :present as a value to :ensure" do Puppet::Type.type(:vlan).new(:name => "200", :ensure => :present) end it "should support :absent as a value to :ensure" do Puppet::Type.type(:vlan).new(:name => "200", :ensure => :absent) end it "should fail if vlan name is not a number" do lambda { Puppet::Type.type(:vlan).new(:name => "notanumber", :ensure => :present) }.should raise_error end end end diff --git a/spec/unit/type/whit_spec.rb b/spec/unit/type/whit_spec.rb index 9a076d1f1..cc7d05938 100755 --- a/spec/unit/type/whit_spec.rb +++ b/spec/unit/type/whit_spec.rb @@ -1,10 +1,10 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' whit = Puppet::Type.type(:whit).new(:name => "Foo::Bar") describe whit do it "should stringify in a way that users will regognise" do whit.to_s.should == "Foo::Bar" end end diff --git a/spec/unit/type/yumrepo_spec.rb b/spec/unit/type/yumrepo_spec.rb index 1ce12cf28..925e98e77 100644 --- a/spec/unit/type/yumrepo_spec.rb +++ b/spec/unit/type/yumrepo_spec.rb @@ -1,243 +1,243 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type.type(:yumrepo) do include PuppetSpec::Files describe "When validating attributes" do it "should have a 'name' parameter'" do Puppet::Type.type(:yumrepo).new(:name => "puppetlabs")[:name].should == "puppetlabs" end [:baseurl, :cost, :descr, :enabled, :enablegroups, :exclude, :failovermethod, :gpgcheck, :gpgkey, :http_caching, :include, :includepkgs, :keepalive, :metadata_expire, :mirrorlist, :priority, :protect, :proxy, :proxy_username, :proxy_password, :timeout, :sslcacert, :sslverify, :sslclientcert, :sslclientkey].each do |param| it "should have a '#{param}' parameter" do Puppet::Type.type(:yumrepo).attrtype(param).should == :property end end end describe "When validating attribute values" do [:cost, :enabled, :enablegroups, :failovermethod, :gpgcheck, :http_caching, :keepalive, :metadata_expire, :priority, :protect, :timeout].each do |param| it "should support :absent as a value to '#{param}' parameter" do Puppet::Type.type(:yumrepo).new(:name => "puppetlabs.repo", param => :absent) end end [:cost, :enabled, :enablegroups, :gpgcheck, :keepalive, :metadata_expire, :priority, :protect, :timeout].each do |param| it "should fail if '#{param}' is not a number" do lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "notanumber") }.should raise_error end end [:enabled, :enabledgroups, :gpgcheck, :keepalive, :protect].each do |param| it "should fail if '#{param}' does not have one of the following values (0|1)" do lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", param => "2") }.should raise_error end end it "should fail if 'failovermethod' does not have one of the following values (roundrobin|priority)" do lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :failovermethod => "notavalidvalue") }.should raise_error end it "should fail if 'http_caching' does not have one of the following values (packages|all|none)" do lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :http_caching => "notavalidvalue") }.should raise_error end it "should fail if 'sslverify' does not have one of the following values (True|False)" do lambda { Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "notavalidvalue") }.should raise_error end it "should succeed if 'sslverify' has one of the following values (True|False)" do Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "True")[:sslverify].should == "True" Puppet::Type.type(:yumrepo).new(:name => "puppetlabs", :sslverify => "False")[:sslverify].should == "False" end end # these tests were ported from the old spec/unit/type/yumrepo_spec.rb, pretty much verbatim describe "When manipulating config file" do def make_repo(name, hash={}) hash[:name] = name Puppet::Type.type(:yumrepo).new(hash) end def all_sections(inifile) sections = [] inifile.each_section { |section| sections << section.name } sections.sort end def create_data_files() File.open(File.join(@yumdir, "fedora.repo"), "w") do |f| f.print(FEDORA_REPO_FILE) end File.open(File.join(@yumdir, "fedora-devel.repo"), "w") do |f| f.print(FEDORA_DEVEL_REPO_FILE) end end before(:each) do @yumdir = tmpdir("yumrepo_spec_tmpdir") @yumconf = File.join(@yumdir, "yum.conf") File.open(@yumconf, "w") do |f| f.print "[main]\nreposdir=#{@yumdir} /no/such/dir\n" end Puppet::Type.type(:yumrepo).yumconf = @yumconf # It needs to be reset each time, otherwise the cache is used. Puppet::Type.type(:yumrepo).inifile = nil end it "should be able to create a valid config file" do values = { :descr => "Fedora Core $releasever - $basearch - Base", :baseurl => "http://example.com/yum/$releasever/$basearch/os/", :enabled => "1", :gpgcheck => "1", :includepkgs => "absent", :gpgkey => "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora", :proxy => "http://proxy.example.com:80/", :proxy_username => "username", :proxy_password => "password" } repo = make_repo("base", values) catalog = Puppet::Resource::Catalog.new # Stop Puppet from doing a bunch of magic; might want to think about a util for specs that handles this catalog.host_config = false catalog.add_resource(repo) catalog.apply inifile = Puppet::Type.type(:yumrepo).read sections = all_sections(inifile) sections.should == ['base', 'main'] text = inifile["base"].format text.should == EXPECTED_CONTENTS_FOR_CREATED_FILE end # Modify one existing section it "should be able to modify an existing config file" do create_data_files devel = make_repo("development", { :descr => "New description" }) current_values = devel.retrieve devel[:name].should == "development" current_values[devel.property(:descr)].should == 'Fedora Core $releasever - Development Tree' devel.property(:descr).should == 'New description' catalog = Puppet::Resource::Catalog.new # Stop Puppet from doing a bunch of magic; might want to think about a util for specs that handles this catalog.host_config = false catalog.add_resource(devel) catalog.apply inifile = Puppet::Type.type(:yumrepo).read inifile['development']['name'].should == 'New description' inifile['base']['name'].should == 'Fedora Core $releasever - $basearch - Base' inifile['base']['exclude'].should == "foo\n bar\n baz" all_sections(inifile).should == ['base', 'development', 'main'] end # Delete mirrorlist by setting it to :absent and enable baseurl it "should support 'absent' value" do create_data_files baseurl = 'http://example.com/' devel = make_repo( "development", { :mirrorlist => 'absent', :baseurl => baseurl }) devel.retrieve catalog = Puppet::Resource::Catalog.new # Stop Puppet from doing a bunch of magic; might want to think about a util for specs that handles this catalog.host_config = false catalog.add_resource(devel) catalog.apply inifile = Puppet::Type.type(:yumrepo).read sec = inifile["development"] sec["mirrorlist"].should == nil sec["baseurl"].should == baseurl end end end EXPECTED_CONTENTS_FOR_CREATED_FILE = <<'EOF' [base] name=Fedora Core $releasever - $basearch - Base baseurl=http://example.com/yum/$releasever/$basearch/os/ enabled=1 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora proxy=http://proxy.example.com:80/ proxy_username=username proxy_password=password EOF FEDORA_REPO_FILE = < "foo") foo_bar_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar") foo_bar_baz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz") foo_bar_baz_buz_zfs = Puppet::Type.type(:zfs).new(:name => "foo/bar/baz/buz") config = Puppet::Resource::Catalog.new :testing do |conf| [foo_pool, foo_bar_zfs, foo_bar_baz_zfs, foo_bar_baz_buz_zfs].each { |resource| conf.add_resource resource } end req = foo_bar_baz_buz_zfs.autorequire.collect { |edge| edge.source.ref } [foo_pool.ref, foo_bar_zfs.ref, foo_bar_baz_zfs.ref].each { |ref| req.include?(ref).should == true } end end diff --git a/spec/unit/type/zone_spec.rb b/spec/unit/type/zone_spec.rb index eb3d33bb0..d623afcf5 100755 --- a/spec/unit/type/zone_spec.rb +++ b/spec/unit/type/zone_spec.rb @@ -1,79 +1,79 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' zone = Puppet::Type.type(:zone) describe zone do before do zone = Puppet::Type.type(:zone) provider = stub 'provider' provider.stubs(:name).returns(:solaris) zone.stubs(:defaultprovider).returns(provider) resource = stub 'resource', :resource => nil, :provider => provider, :line => nil, :file => nil end parameters = [:create_args, :install_args, :sysidcfg, :path, :realhostname] parameters.each do |parameter| it "should have a #{parameter} parameter" do zone.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end end properties = [:ip, :iptype, :autoboot, :pool, :shares, :inherit] properties.each do |property| it "should have a #{property} property" do zone.attrclass(property).ancestors.should be_include(Puppet::Property) end end it "should be invalid when :path is missing" do lambda { zone.new(:name => "dummy") }.should raise_error end it "should be invalid when :ip is missing a \":\" and iptype is :shared" do lambda { zone.new(:name => "dummy", :ip => "if") }.should raise_error end it "should be invalid when :ip has a \":\" and iptype is :exclusive" do lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4", :iptype => :exclusive) }.should raise_error end it "should be invalid when :ip has two \":\" and iptype is :exclusive" do lambda { zone.new(:name => "dummy", :ip => "if:1.2.3.4:2.3.4.5", :iptype => :exclusive) }.should raise_error end it "should be valid when :iptype is :shared and using interface and ip" do zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4") end it "should be valid when :iptype is :shared and using interface, ip and default route" do zone.new(:name => "dummy", :path => "/dummy", :ip => "if:1.2.3.4:2.3.4.5") end it "should be valid when :iptype is :exclusive and using interface" do zone.new(:name => "dummy", :path => "/dummy", :ip => "if", :iptype => :exclusive) end it "should auto-require :dataset entries" do fs = 'random-pool/some-zfs' # ick provider = stub 'zfs::provider' provider.stubs(:name).returns(:solaris) Puppet::Type.type(:zfs).stubs(:defaultprovider).returns(provider) catalog = Puppet::Resource::Catalog.new zfs_instance = Puppet::Type.type(:zfs).new(:name => fs) catalog.add_resource zfs_instance zone_instance = zone.new(:name => "dummy", :path => "/foo", :ip => 'en1:1.0.0.0', :dataset => fs) catalog.add_resource zone_instance catalog.relationship_graph.dependencies(zone_instance).should == [zfs_instance] end end diff --git a/spec/unit/type/zpool_spec.rb b/spec/unit/type/zpool_spec.rb index 9f1800073..f70d89c4b 100755 --- a/spec/unit/type/zpool_spec.rb +++ b/spec/unit/type/zpool_spec.rb @@ -1,109 +1,109 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' zpool = Puppet::Type.type(:zpool) describe zpool do before do @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil end properties = [:ensure, :disk, :mirror, :raidz, :spare, :log] properties.each do |property| it "should have a #{property} property" do zpool.attrclass(property).ancestors.should be_include(Puppet::Property) end end parameters = [:pool, :raid_parity] parameters.each do |parameter| it "should have a #{parameter} parameter" do zpool.attrclass(parameter).ancestors.should be_include(Puppet::Parameter) end end end vdev_property = Puppet::Property::VDev describe vdev_property do before do vdev_property.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = vdev_property.new(:resource => @resource) end it "should be insync if the devices are the same" do @property.should = ["dev1 dev2"] @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the devices are not the same" do @property.should = ["dev1 dev3"] @property.safe_insync?(["dev2 dev1"]).must be_false end it "should be insync if the devices are the same and the should values are comma seperated" do @property.should = ["dev1", "dev2"] @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the device is absent and should has a value" do @property.should = ["dev1", "dev2"] @property.safe_insync?(:absent).must be_false end it "should be insync if the device is absent and should is absent" do @property.should = [:absent] @property.safe_insync?(:absent).must be_true end end multi_vdev_property = Puppet::Property::MultiVDev describe multi_vdev_property do before do multi_vdev_property.initvars @resource = stub 'resource', :[]= => nil, :property => nil @property = multi_vdev_property.new(:resource => @resource) end it "should be insync if the devices are the same" do @property.should = ["dev1 dev2"] @property.safe_insync?(["dev2 dev1"]).must be_true end it "should be out of sync if the devices are not the same" do @property.should = ["dev1 dev3"] @property.safe_insync?(["dev2 dev1"]).must be_false end it "should be out of sync if the device is absent and should has a value" do @property.should = ["dev1", "dev2"] @property.safe_insync?(:absent).must be_false end it "should be insync if the device is absent and should is absent" do @property.should = [:absent] @property.safe_insync?(:absent).must be_true end describe "when there are multiple lists of devices" do it "should be in sync if each group has the same devices" do @property.should = ["dev1 dev2", "dev3 dev4"] @property.safe_insync?(["dev2 dev1", "dev3 dev4"]).must be_true end it "should be out of sync if any group has the different devices" do @property.should = ["dev1 devX", "dev3 dev4"] @property.safe_insync?(["dev2 dev1", "dev3 dev4"]).must be_false end it "should be out of sync if devices are in the wrong group" do @property.should = ["dev1 dev2", "dev3 dev4"] @property.safe_insync?(["dev2 dev3", "dev1 dev4"]).must be_false end end end diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb index d06d5a321..02c829451 100755 --- a/spec/unit/type_spec.rb +++ b/spec/unit/type_spec.rb @@ -1,966 +1,966 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Type do include PuppetSpec::Files pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do it "should be Comparable" do a = Puppet::Type.type(:notify).new(:name => "a") b = Puppet::Type.type(:notify).new(:name => "b") c = Puppet::Type.type(:notify).new(:name => "c") [[a, b, c], [a, c, b], [b, a, c], [b, c, a], [c, a, b], [c, b, a]].each do |this| this.sort.should == [a, b, c] end a.should be < b a.should be < c b.should be > a b.should be < c c.should be > a c.should be > b [a, b, c].each {|x| a.should be <= x } [a, b, c].each {|x| c.should be >= x } b.should be_between(a, c) end it "should consider a parameter to be valid if it is a valid parameter" do Puppet::Type.type(:mount).should be_valid_parameter(:path) end it "should consider a parameter to be valid if it is a valid property" do Puppet::Type.type(:mount).should be_valid_parameter(:fstype) end it "should consider a parameter to be valid if it is a valid metaparam" do Puppet::Type.type(:mount).should be_valid_parameter(:noop) end it "should be able to retrieve a property by name" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.property(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) end it "should be able to retrieve a parameter by name" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.parameter(:name).must be_instance_of(Puppet::Type.type(:mount).attrclass(:name)) end it "should be able to retrieve a property by name using the :parameter method" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.parameter(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) end it "should be able to retrieve all set properties" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) props = resource.properties props.should_not be_include(nil) [:fstype, :ensure, :pass].each do |name| props.should be_include(resource.parameter(name)) end end it "should have a method for setting default values for resources" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:set_default) end it "should do nothing for attributes that have no defaults and no specified value" do Puppet::Type.type(:mount).new(:name => "foo").parameter(:noop).should be_nil end it "should have a method for adding tags" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:tags) end it "should use the tagging module" do Puppet::Type.type(:mount).ancestors.should be_include(Puppet::Util::Tagging) end it "should delegate to the tagging module when tags are added" do resource = Puppet::Type.type(:mount).new(:name => "foo") resource.stubs(:tag).with(:mount) resource.expects(:tag).with(:tag1, :tag2) resource.tags = [:tag1,:tag2] end it "should add the current type as tag" do resource = Puppet::Type.type(:mount).new(:name => "foo") resource.stubs(:tag) resource.expects(:tag).with(:mount) resource.tags = [:tag1,:tag2] end it "should have a method to know if the resource is exported" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:exported?) end it "should have a method to know if the resource is virtual" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:virtual?) end it "should consider its version to be its catalog version" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.version = 50 catalog.add_resource resource resource.version.should == 50 end it "should consider its version to be zero if it has no catalog" do Puppet::Type.type(:mount).new(:name => "foo").version.should == 0 end it "should provide source_descriptors" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.version = 50 catalog.add_resource resource resource.source_descriptors.should == {:tags=>["mount", "foo"], :path=>"/Mount[foo]"} end it "should consider its type to be the name of its class" do Puppet::Type.type(:mount).new(:name => "foo").type.should == :mount end it "should use any provided noop value" do Puppet::Type.type(:mount).new(:name => "foo", :noop => true).must be_noop end it "should use the global noop value if none is provided" do Puppet[:noop] = true Puppet::Type.type(:mount).new(:name => "foo").must be_noop end it "should not be noop if in a non-host_config catalog" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource resource.should_not be_noop end describe "when creating an event" do before do @resource = Puppet::Type.type(:mount).new :name => "foo" end it "should have the resource's reference as the resource" do @resource.event.resource.should == "Mount[foo]" end it "should have the resource's log level as the default log level" do @resource[:loglevel] = :warning @resource.event.default_log_level.should == :warning end {:file => "/my/file", :line => 50, :tags => %{foo bar}}.each do |attr, value| it "should set the #{attr}" do @resource.stubs(attr).returns value @resource.event.send(attr).should == value end end it "should allow specification of event attributes" do @resource.event(:status => "noop").status.should == "noop" end end describe "when creating a provider" do before :each do @type = Puppet::Type.newtype(:provider_test_type) do newparam(:name) { isnamevar } newparam(:foo) newproperty(:bar) end end after :each do @type.provider_hash.clear end describe "when determining if instances of the type are managed" do it "should not consider audit only resources to be managed" do @type.new(:name => "foo", :audit => 'all').managed?.should be_false end it "should not consider resources with only parameters to be managed" do @type.new(:name => "foo", :foo => 'did someone say food?').managed?.should be_false end it "should consider resources with any properties set to be managed" do @type.new(:name => "foo", :bar => 'Let us all go there').managed?.should be_true end end it "should have documentation for the 'provider' parameter if there are providers" do @type.provide(:test_provider) @type.paramdoc(:provider).should =~ /`provider_test_type`[\s\r]+resource/ end it "should not have documentation for the 'provider' parameter if there are no providers" do expect { @type.paramdoc(:provider) }.to raise_error(NoMethodError) end it "should create a subclass of Puppet::Provider for the provider" do provider = @type.provide(:test_provider) provider.ancestors.should include(Puppet::Provider) end it "should use a parent class if specified" do parent_provider = @type.provide(:parent_provider) child_provider = @type.provide(:child_provider, :parent => parent_provider) child_provider.ancestors.should include(parent_provider) end it "should use a parent class if specified by name" do parent_provider = @type.provide(:parent_provider) child_provider = @type.provide(:child_provider, :parent => :parent_provider) child_provider.ancestors.should include(parent_provider) end it "should raise an error when the parent class can't be found" do expect { @type.provide(:child_provider, :parent => :parent_provider) }.to raise_error(Puppet::DevError, /Could not find parent provider.+parent_provider/) end it "should ensure its type has a 'provider' parameter" do @type.provide(:test_provider) @type.parameters.should include(:provider) end it "should remove a previously registered provider with the same name" do old_provider = @type.provide(:test_provider) new_provider = @type.provide(:test_provider) old_provider.should_not equal(new_provider) end it "should register itself as a provider for the type" do provider = @type.provide(:test_provider) provider.should == @type.provider(:test_provider) end it "should create a provider when a provider with the same name previously failed" do @type.provide(:test_provider) do raise "failed to create this provider" end rescue nil provider = @type.provide(:test_provider) provider.ancestors.should include(Puppet::Provider) provider.should == @type.provider(:test_provider) end end describe "when choosing a default provider" do it "should choose the provider with the highest specificity" do # Make a fake type type = Puppet::Type.newtype(:defaultprovidertest) do newparam(:name) do end end basic = type.provide(:basic) {} greater = type.provide(:greater) {} basic.stubs(:specificity).returns 1 greater.stubs(:specificity).returns 2 type.defaultprovider.should equal(greater) end end describe "when initializing" do describe "and passed a Puppet::Resource instance" do it "should set its title to the title of the resource if the resource type is equal to the current type" do resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "/other"}) Puppet::Type.type(:mount).new(resource).title.should == "/foo" end it "should set its title to the resource reference if the resource type is not equal to the current type" do resource = Puppet::Resource.new(:user, "foo") Puppet::Type.type(:mount).new(resource).title.should == "User[foo]" end [:line, :file, :catalog, :exported, :virtual].each do |param| it "should copy '#{param}' from the resource if present" do resource = Puppet::Resource.new(:mount, "/foo") resource.send(param.to_s + "=", "foo") resource.send(param.to_s + "=", "foo") Puppet::Type.type(:mount).new(resource).send(param).should == "foo" end end it "should copy any tags from the resource" do resource = Puppet::Resource.new(:mount, "/foo") resource.tag "one", "two" tags = Puppet::Type.type(:mount).new(resource).tags tags.should be_include("one") tags.should be_include("two") end it "should copy the resource's parameters as its own" do resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:atboot => true, :fstype => "boo"}) params = Puppet::Type.type(:mount).new(resource).to_hash params[:fstype].should == "boo" params[:atboot].should == true end end describe "and passed a Hash" do it "should extract the title from the hash" do Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" end it "should work when hash keys are provided as strings" do Puppet::Type.type(:mount).new("title" => "/yay").title.should == "/yay" end it "should work when hash keys are provided as symbols" do Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" end it "should use the name from the hash as the title if no explicit title is provided" do Puppet::Type.type(:mount).new(:name => "/yay").title.should == "/yay" end it "should use the Resource Type's namevar to determine how to find the name in the hash" do yay = make_absolute('/yay') Puppet::Type.type(:file).new(:path => yay).title.should == yay end [:catalog].each do |param| it "should extract '#{param}' from the hash if present" do Puppet::Type.type(:mount).new(:name => "/yay", param => "foo").send(param).should == "foo" end end it "should use any remaining hash keys as its parameters" do resource = Puppet::Type.type(:mount).new(:title => "/foo", :catalog => "foo", :atboot => true, :fstype => "boo") resource[:fstype].must == "boo" resource[:atboot].must == true end end it "should fail if any invalid attributes have been provided" do lambda { Puppet::Type.type(:mount).new(:title => "/foo", :nosuchattr => "whatever") }.should raise_error(Puppet::Error) end it "should set its name to the resource's title if the resource does not have a :name or namevar parameter set" do resource = Puppet::Resource.new(:mount, "/foo") Puppet::Type.type(:mount).new(resource).name.should == "/foo" end it "should fail if no title, name, or namevar are provided" do lambda { Puppet::Type.type(:file).new(:atboot => true) }.should raise_error(Puppet::Error) end it "should set the attributes in the order returned by the class's :allattrs method" do Puppet::Type.type(:mount).stubs(:allattrs).returns([:name, :atboot, :noop]) resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot", :noop => "whatever"}) set = [] Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| set << param true end.returns(stub_everything("a property")) Puppet::Type.type(:mount).new(resource) set[-1].should == :noop set[-2].should == :atboot end it "should always set the name and then default provider before anything else" do Puppet::Type.type(:mount).stubs(:allattrs).returns([:provider, :name, :atboot]) resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot"}) set = [] Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| set << param true end.returns(stub_everything("a property")) Puppet::Type.type(:mount).new(resource) set[0].should == :name set[1].should == :provider end # This one is really hard to test :/ it "should set each default immediately if no value is provided" do defaults = [] Puppet::Type.type(:service).any_instance.stubs(:set_default).with { |value| defaults << value; true } Puppet::Type.type(:service).new :name => "whatever" defaults[0].should == :provider end it "should retain a copy of the originally provided parameters" do Puppet::Type.type(:mount).new(:name => "foo", :atboot => true, :noop => false).original_parameters.should == {:atboot => true, :noop => false} end it "should delete the name via the namevar from the originally provided parameters" do Puppet::Type.type(:file).new(:name => make_absolute('/foo')).original_parameters[:path].should be_nil end end it "should have a class method for converting a hash into a Puppet::Resource instance" do Puppet::Type.type(:mount).must respond_to(:hash2resource) end describe "when converting a hash to a Puppet::Resource instance" do before do @type = Puppet::Type.type(:mount) end it "should treat a :title key as the title of the resource" do @type.hash2resource(:name => "/foo", :title => "foo").title.should == "foo" end it "should use the name from the hash as the title if no explicit title is provided" do @type.hash2resource(:name => "foo").title.should == "foo" end it "should use the Resource Type's namevar to determine how to find the name in the hash" do @type.stubs(:key_attributes).returns([ :myname ]) @type.hash2resource(:myname => "foo").title.should == "foo" end [:catalog].each do |attr| it "should use any provided #{attr}" do @type.hash2resource(:name => "foo", attr => "eh").send(attr).should == "eh" end end it "should set all provided parameters on the resource" do @type.hash2resource(:name => "foo", :fstype => "boo", :boot => "fee").to_hash.should == {:name => "foo", :fstype => "boo", :boot => "fee"} end it "should not set the title as a parameter on the resource" do @type.hash2resource(:name => "foo", :title => "eh")[:title].should be_nil end it "should not set the catalog as a parameter on the resource" do @type.hash2resource(:name => "foo", :catalog => "eh")[:catalog].should be_nil end it "should treat hash keys equivalently whether provided as strings or symbols" do resource = @type.hash2resource("name" => "foo", "title" => "eh", "fstype" => "boo") resource.title.should == "eh" resource[:name].should == "foo" resource[:fstype].should == "boo" end end describe "when retrieving current property values" do before do @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) @resource.property(:ensure).stubs(:retrieve).returns :absent end it "should fail if its provider is unsuitable" do @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) @resource.provider.class.expects(:suitable?).returns false lambda { @resource.retrieve_resource }.should raise_error(Puppet::Error) end it "should return a Puppet::Resource instance with its type and title set appropriately" do result = @resource.retrieve_resource result.should be_instance_of(Puppet::Resource) result.type.should == "Mount" result.title.should == "foo" end it "should set the name of the returned resource if its own name and title differ" do @resource[:name] = "my name" @resource.title = "other name" @resource.retrieve_resource[:name].should == "my name" end it "should provide a value for all set properties" do values = @resource.retrieve_resource [:ensure, :fstype, :pass].each { |property| values[property].should_not be_nil } end it "should provide a value for 'ensure' even if no desired value is provided" do @resource = Puppet::Type.type(:file).new(:path => make_absolute("/my/file/that/can't/exist")) end it "should not call retrieve on non-ensure properties if the resource is absent and should consider the property absent" do @resource.property(:ensure).expects(:retrieve).returns :absent @resource.property(:fstype).expects(:retrieve).never @resource.retrieve_resource[:fstype].should == :absent end it "should include the result of retrieving each property's current value if the resource is present" do @resource.property(:ensure).expects(:retrieve).returns :present @resource.property(:fstype).expects(:retrieve).returns 15 @resource.retrieve_resource[:fstype] == 15 end end describe "#to_resource" do it "should return a Puppet::Resource that includes properties, parameters and tags" do type_resource = Puppet::Type.type(:mount).new( :ensure => :present, :name => "foo", :fstype => "bar", :remounts => true ) type_resource.tags = %w{bar baz} # If it's not a property it's a parameter type_resource.parameters[:remounts].should_not be_a(Puppet::Property) type_resource.parameters[:fstype].is_a?(Puppet::Property).should be_true type_resource.property(:ensure).expects(:retrieve).returns :present type_resource.property(:fstype).expects(:retrieve).returns 15 resource = type_resource.to_resource resource.should be_a Puppet::Resource resource[:fstype].should == 15 resource[:remounts].should == :true resource.tags.should =~ %w{foo bar baz mount} end end describe ".title_patterns" do describe "when there's one namevar" do before do @type_class = Puppet::Type.type(:notify) @type_class.stubs(:key_attributes).returns([:one]) end it "should have a default pattern for when there's one namevar" do patterns = @type_class.title_patterns patterns.length.should == 1 patterns[0].length.should == 2 end it "should have a regexp that captures the entire string" do patterns = @type_class.title_patterns string = "abc\n\tdef" patterns[0][0] =~ string $1.should == "abc\n\tdef" end end end describe "when in a catalog" do before do @catalog = Puppet::Resource::Catalog.new @container = Puppet::Type.type(:component).new(:name => "container") @one = Puppet::Type.type(:file).new(:path => make_absolute("/file/one")) @two = Puppet::Type.type(:file).new(:path => make_absolute("/file/two")) @catalog.add_resource @container @catalog.add_resource @one @catalog.add_resource @two @catalog.add_edge @container, @one @catalog.add_edge @container, @two end it "should have no parent if there is no in edge" do @container.parent.should be_nil end it "should set its parent to its in edge" do @one.parent.ref.should == @container.ref end after do @catalog.clear(true) end end it "should have a 'stage' metaparam" do Puppet::Type.metaparamclass(:stage).should be_instance_of(Class) end describe "#suitable?" do let(:type) { Puppet::Type.type(:file) } let(:resource) { type.new :path => tmpfile('suitable') } let(:provider) { resource.provider } it "should be suitable if its type doesn't use providers" do type.stubs(:paramclass).with(:provider).returns nil resource.should be_suitable end it "should be suitable if it has a provider which is suitable" do resource.should be_suitable end it "should not be suitable if it has a provider which is not suitable" do provider.class.stubs(:suitable?).returns false resource.should_not be_suitable end it "should be suitable if it does not have a provider and there is a default provider" do resource.stubs(:provider).returns nil resource.should be_suitable end it "should not be suitable if it doesn't have a provider and there is not default provider" do resource.stubs(:provider).returns nil type.stubs(:defaultprovider).returns nil resource.should_not be_suitable end end describe "::instances" do after :each do Puppet::Type.rmtype(:type_spec_fake_type) end let :type do Puppet::Type.newtype(:type_spec_fake_type) do newparam(:name) do isnamevar end newproperty(:prop1) {} end Puppet::Type.type(:type_spec_fake_type) end it "should not fail if no suitable providers are found" do type.provide(:fake1) do confine :exists => '/no/such/file' mk_resource_methods end expect { type.instances.should == [] }.should_not raise_error end context "with a default provider" do before :each do type.provide(:default) do defaultfor :operatingsystem => Facter.value(:operatingsystem) mk_resource_methods class << self attr_accessor :names end def self.instance(name) new(:name => name, :ensure => :present) end def self.instances @instances ||= names.collect { |name| instance(name.to_s) } end @names = [:one, :two] end end it "should return only instances of the type" do type.instances.should be_all {|x| x.is_a? type } end it "should return instances from the default provider" do type.instances.map(&:name).should == ["one", "two"] end it "should return instances from all providers" do type.provide(:fake1, :parent => :default) { @names = [:three, :four] } type.instances.map(&:name).should == ["one", "two", "three", "four"] end it "should not return instances from unsuitable providers" do type.provide(:fake1, :parent => :default) do @names = [:three, :four] confine :exists => "/no/such/file" end type.instances.map(&:name).should == ["one", "two"] end end end describe "::ensurable?" do before :each do class TestEnsurableType < Puppet::Type def exists?; end def create; end def destroy; end end end it "is true if the class has exists?, create, and destroy methods defined" do TestEnsurableType.should be_ensurable end it "is false if exists? is not defined" do TestEnsurableType.class_eval { remove_method(:exists?) } TestEnsurableType.should_not be_ensurable end it "is false if create is not defined" do TestEnsurableType.class_eval { remove_method(:create) } TestEnsurableType.should_not be_ensurable end it "is false if destroy is not defined" do TestEnsurableType.class_eval { remove_method(:destroy) } TestEnsurableType.should_not be_ensurable end end end end describe Puppet::Type::RelationshipMetaparam do include PuppetSpec::Files it "should be a subclass of Puppet::Parameter" do Puppet::Type::RelationshipMetaparam.superclass.should equal(Puppet::Parameter) end it "should be able to produce a list of subclasses" do Puppet::Type::RelationshipMetaparam.should respond_to(:subclasses) end describe "when munging relationships" do pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do before do @path = make_absolute('/foo') @resource = Puppet::Type.type(:mount).new :name => @path @metaparam = Puppet::Type.metaparamclass(:require).new :resource => @resource end it "should accept Puppet::Resource instances" do ref = Puppet::Resource.new(:file, @path) @metaparam.munge(ref)[0].should equal(ref) end it "should turn any string into a Puppet::Resource" do @metaparam.munge("File[/ref]")[0].should be_instance_of(Puppet::Resource) end end end it "should be able to validate relationships" do Puppet::Type.metaparamclass(:require).new(:resource => mock("resource")).should respond_to(:validate_relationship) end it "should fail if any specified resource is not found in the catalog" do catalog = mock 'catalog' resource = stub 'resource', :catalog => catalog, :ref => "resource" param = Puppet::Type.metaparamclass(:require).new(:resource => resource, :value => %w{Foo[bar] Class[test]}) catalog.expects(:resource).with("Foo[bar]").returns "something" catalog.expects(:resource).with("Class[Test]").returns nil param.expects(:fail).with { |string| string.include?("Class[Test]") } param.validate_relationship end end describe Puppet::Type.metaparamclass(:check) do include PuppetSpec::Files it "should warn and create an instance of ':audit'" do file = Puppet::Type.type(:file).new :path => make_absolute('/foo') file.expects(:warning) file[:check] = :mode file[:audit].should == [:mode] end end describe Puppet::Type.metaparamclass(:audit) do include PuppetSpec::Files before do @resource = Puppet::Type.type(:file).new :path => make_absolute('/foo') end it "should default to being nil" do @resource[:audit].should be_nil end it "should specify all possible properties when asked to audit all properties" do @resource[:audit] = :all list = @resource.class.properties.collect { |p| p.name } @resource[:audit].should == list end it "should accept the string 'all' to specify auditing all possible properties" do @resource[:audit] = 'all' list = @resource.class.properties.collect { |p| p.name } @resource[:audit].should == list end it "should fail if asked to audit an invalid property" do lambda { @resource[:audit] = :foobar }.should raise_error(Puppet::Error) end it "should create an attribute instance for each auditable property" do @resource[:audit] = :mode @resource.parameter(:mode).should_not be_nil end it "should accept properties specified as a string" do @resource[:audit] = "mode" @resource.parameter(:mode).should_not be_nil end it "should not create attribute instances for parameters, only properties" do @resource[:audit] = :noop @resource.parameter(:noop).should be_nil end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:path, :mode, :owner] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) myfile = make_absolute('/my/file') res = Puppet::Type.type(:file).new( :title => myfile, :path => myfile, :owner => 'root', :content => 'hello' ) res.uniqueness_key.should == [ nil, 'root', myfile] end end context "type attribute bracket methods" do after :each do Puppet::Type.rmtype(:attributes) end let :type do Puppet::Type.newtype(:attributes) do newparam(:name) {} end end it "should work with parameters" do type.newparam(:param) {} instance = type.new(:name => 'test') expect { instance[:param] = true }.should_not raise_error expect { instance["param"] = true }.should_not raise_error instance[:param].should == true instance["param"].should == true end it "should work with meta-parameters" do instance = type.new(:name => 'test') expect { instance[:noop] = true }.should_not raise_error expect { instance["noop"] = true }.should_not raise_error instance[:noop].should == true instance["noop"].should == true end it "should work with properties" do type.newproperty(:property) {} instance = type.new(:name => 'test') expect { instance[:property] = true }.should_not raise_error expect { instance["property"] = true }.should_not raise_error instance.property(:property).must be instance.should(:property).must be_true end it "should handle proprieties correctly" do # Order of assignment is significant in this test. props = {} [:one, :two, :three].each {|prop| type.newproperty(prop) {} } instance = type.new(:name => "test") instance[:one] = "boo" one = instance.property(:one) instance.properties.must == [one] instance[:three] = "rah" three = instance.property(:three) instance.properties.must == [one, three] instance[:two] = "whee" two = instance.property(:two) instance.properties.must == [one, two, three] end it "should handle parameter aliases correctly" do type.newparam(:one) {} type.newproperty(:two) {} type.set_attr_alias :three => :one type.attr_alias(:three).must == :one type.set_attr_alias :four => :two type.attr_alias(:four).must == :two type.attr_alias(:name).must_not be instance = type.new(:name => "my name") instance.must be instance[:three] = "value three" instance.value(:three).must == "value three" instance.value(:one).must == "value three" instance[:four] = "value four" instance.should(:four).must == "value four" instance.value(:four).must == "value four" instance.value(:two).must == "value four" end it "newattr should handle required features correctly" do Puppet::Util::Log.level = :debug type.feature :feature1, "one" type.feature :feature2, "two" none = type.newproperty(:none) {} one = type.newproperty(:one, :required_features => :feature1) {} two = type.newproperty(:two, :required_features => [:feature1, :feature2]) {} nope = type.provide(:nope) {} maybe = type.provide(:maybe) { has_features :feature1 } yep = type.provide(:yep) { has_features :feature1, :feature2 } [nope, maybe, yep].each_with_index do |provider, i| rsrc = type.new(:provider => provider.name, :name => "test#{i}", :none => "a", :one => "b", :two => "c") rsrc.should(:none).must be if provider.declared_feature? :feature1 rsrc.should(:one).must be else rsrc.should(:one).must_not be @logs.find {|l| l.message =~ /not managing attribute one/ }.should be end if provider.declared_feature? :feature2 rsrc.should(:two).must be else rsrc.should(:two).must_not be @logs.find {|l| l.message =~ /not managing attribute two/ }.should be end end end end end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index 1812a3c89..95827606a 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -1,264 +1,264 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/autoload' describe Puppet::Util::Autoload do include PuppetSpec::Files before do @autoload = Puppet::Util::Autoload.new("foo", "tmp") @autoload.stubs(:eachdir).yields make_absolute("/my/dir") @loaded = {} @autoload.class.stubs(:loaded).returns(@loaded) end describe "when building the search path" do before :each do ## modulepath/libdir can't be used until after app settings are initialized, so we need to simulate that: Puppet.settings.expects(:app_defaults_initialized?).returns(true).at_least_once @dira = File.expand_path('/a') @dirb = File.expand_path('/b') @dirc = File.expand_path('/c') end it "should collect all of the plugins and lib directories that exist in the current environment's module path" do Puppet.settings.expects(:value).with(:environment).returns "foo" Puppet.settings.expects(:value).with(:modulepath, :foo).returns "#{@dira}#{File::PATH_SEPARATOR}#{@dirb}#{File::PATH_SEPARATOR}#{@dirc}" Dir.expects(:entries).with(@dira).returns %w{one two} Dir.expects(:entries).with(@dirb).returns %w{one two} FileTest.stubs(:directory?).returns false FileTest.expects(:directory?).with(@dira).returns true FileTest.expects(:directory?).with(@dirb).returns true ["#{@dira}/one/plugins", "#{@dira}/two/lib", "#{@dirb}/one/plugins", "#{@dirb}/two/lib"].each do |d| FileTest.expects(:directory?).with(d).returns true end @autoload.class.module_directories.should == ["#{@dira}/one/plugins", "#{@dira}/two/lib", "#{@dirb}/one/plugins", "#{@dirb}/two/lib"] end it "should not look for lib directories in directories starting with '.'" do Puppet.settings.expects(:value).with(:environment).returns "foo" Puppet.settings.expects(:value).with(:modulepath, :foo).returns @dira Dir.expects(:entries).with(@dira).returns %w{. ..} FileTest.expects(:directory?).with(@dira).returns true FileTest.expects(:directory?).with("#{@dira}/./lib").never FileTest.expects(:directory?).with("#{@dira}/./plugins").never FileTest.expects(:directory?).with("#{@dira}/../lib").never FileTest.expects(:directory?).with("#{@dira}/../plugins").never @autoload.class.module_directories end it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do Puppet[:libdir] = %w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR) @autoload.class.expects(:module_directories).returns %w{/one /two} @autoload.class.search_directories.should == %w{/one /two} + Puppet[:libdir].split(File::PATH_SEPARATOR) + $LOAD_PATH end end describe "when loading a file" do before do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] FileTest.stubs(:directory?).returns true @time_a = Time.utc(2010, 'jan', 1, 6, 30) File.stubs(:mtime).returns @time_a end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do File.stubs(:exist?).returns true Kernel.expects(:load).raises error lambda { @autoload.load("foo") }.should raise_error(Puppet::Error) end end it "should not raise an error if the file is missing" do @autoload.load("foo").should == false end it "should register loaded files with the autoloader" do File.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.class.loaded?("tmp/myfile.rb").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should be seen by loaded? on the instance using the short name" do File.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.loaded?("myfile.rb").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do File.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") $LOADED_FEATURES.should be_include("tmp/myfile.rb") $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should load the first file in the searchpath" do @autoload.stubs(:search_directories).returns [make_absolute("/a"), make_absolute("/b")] FileTest.stubs(:directory?).returns true File.stubs(:exist?).returns true Kernel.expects(:load).with(make_absolute("/a/tmp/myfile.rb"), optionally(anything)) @autoload.load("myfile") $LOADED_FEATURES.delete("tmp/myfile.rb") end it "should treat equivalent paths to a loaded file as loaded" do File.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.class.loaded?("tmp/myfile").should be @autoload.class.loaded?("tmp/./myfile.rb").should be @autoload.class.loaded?("./tmp/myfile.rb").should be @autoload.class.loaded?("tmp/../tmp/myfile.rb").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end end describe "when loading all files" do before do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] FileTest.stubs(:directory?).returns true Dir.stubs(:glob).returns [make_absolute("/a/foo/file.rb")] File.stubs(:exist?).returns true @time_a = Time.utc(2010, 'jan', 1, 6, 30) File.stubs(:mtime).returns @time_a @autoload.class.stubs(:loaded?).returns(false) end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die an if a #{error.to_s} exception is thrown" do Kernel.expects(:load).raises error lambda { @autoload.loadall }.should raise_error(Puppet::Error) end end it "should require the full path to the file" do Kernel.expects(:load).with(make_absolute("/a/foo/file.rb"), optionally(anything)) @autoload.loadall end end describe "when reloading files" do before :each do @file_a = make_absolute("/a/file.rb") @file_b = make_absolute("/b/file.rb") @first_time = Time.utc(2010, 'jan', 1, 6, 30) @second_time = @first_time + 60 end after :each do $LOADED_FEATURES.delete("a/file.rb") $LOADED_FEATURES.delete("b/file.rb") end it "#changed? should return true for a file that was not loaded" do @autoload.class.changed?(@file_a).should be end it "changes should be seen by changed? on the instance using the short name" do File.stubs(:mtime).returns(@first_time) File.stubs(:exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") @autoload.loaded?("myfile").should be @autoload.changed?("myfile").should_not be File.stubs(:mtime).returns(@second_time) @autoload.changed?("myfile").should be $LOADED_FEATURES.delete("tmp/myfile.rb") end describe "in one directory" do before :each do @autoload.class.stubs(:search_directories).returns [make_absolute("/a")] File.expects(:mtime).with(@file_a).returns(@first_time) @autoload.class.mark_loaded("file", @file_a) end it "should reload if mtime changes" do File.stubs(:mtime).with(@file_a).returns(@first_time + 60) File.stubs(:exist?).with(@file_a).returns true Kernel.expects(:load).with(@file_a, optionally(anything)) @autoload.class.reload_changed end it "should do nothing if the file is deleted" do File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT) File.stubs(:exist?).with(@file_a).returns false Kernel.expects(:load).never @autoload.class.reload_changed end end describe "in two directories" do before :each do @autoload.class.stubs(:search_directories).returns [make_absolute("/a"), make_absolute("/b")] end it "should load b/file when a/file is deleted" do File.expects(:mtime).with(@file_a).returns(@first_time) @autoload.class.mark_loaded("file", @file_a) File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT) File.stubs(:exist?).with(@file_a).returns false File.stubs(:exist?).with(@file_b).returns true File.stubs(:mtime).with(@file_b).returns @first_time Kernel.expects(:load).with(@file_b, optionally(anything)) @autoload.class.reload_changed @autoload.class.send(:loaded)["file"].should == [@file_b, @first_time] end it "should load a/file when b/file is loaded and a/file is created" do File.stubs(:mtime).with(@file_b).returns @first_time File.stubs(:exist?).with(@file_b).returns true @autoload.class.mark_loaded("file", @file_b) File.stubs(:mtime).with(@file_a).returns @first_time File.stubs(:exist?).with(@file_a).returns true Kernel.expects(:load).with(@file_a, optionally(anything)) @autoload.class.reload_changed @autoload.class.send(:loaded)["file"].should == [@file_a, @first_time] end end end describe "#cleanpath" do it "should leave relative paths relative" do path = "hello/there" Puppet::Util::Autoload.cleanpath(path).should == path end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should convert c:\ to c:/" do Puppet::Util::Autoload.cleanpath('c:\\').should == 'c:/' end end end end diff --git a/spec/unit/util/backups_spec.rb b/spec/unit/util/backups_spec.rb index 50545001d..0e26225bc 100755 --- a/spec/unit/util/backups_spec.rb +++ b/spec/unit/util/backups_spec.rb @@ -1,160 +1,160 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/backups' describe Puppet::Util::Backups do include PuppetSpec::Files before do FileTest.stubs(:exists?).returns true @nosuchfile = make_absolute('/no/such/file') end describe "when backing up a file" do it "should noop if the file does not exist" do FileTest.expects(:exists?).with(@nosuchfile).returns false file = Puppet::Type.type(:file).new(:name => @nosuchfile) file.expects(:bucket).never file.perform_backup end it "should succeed silently if self[:backup] is false" do file = Puppet::Type.type(:file).new(:name => @nosuchfile, :backup => false) file.expects(:bucket).never FileTest.expects(:exists?).never file.perform_backup end it "a bucket should be used when provided" do path = make_absolute('/my/file') File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') bucket = stub('bucket', 'name' => 'foo') file.stubs(:bucket).returns bucket bucket.expects(:backup).with(path).returns("mysum") file.perform_backup end it "should propagate any exceptions encountered when backing up to a filebucket" do path = make_absolute('/my/file') File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') bucket = stub('bucket', 'name' => 'foo') file.stubs(:bucket).returns bucket bucket.expects(:backup).raises ArgumentError lambda { file.perform_backup }.should raise_error(ArgumentError) end describe "and no filebucket is configured" do it "should remove any local backup if one exists" do path = make_absolute('/my/file') FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") File.expects(:unlink).with(backup) FileUtils.stubs(:cp_r) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup end it "should fail when the old backup can't be removed" do path = make_absolute('/my/file') FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") File.expects(:unlink).raises ArgumentError FileUtils.expects(:cp_r).never file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') lambda { file.perform_backup }.should raise_error(Puppet::Error) end it "should not try to remove backups that don't exist" do path = make_absolute('/my/file') FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).raises(Errno::ENOENT) File.expects(:unlink).never FileUtils.stubs(:cp_r) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup end it "a copy should be created in the local directory" do path = make_absolute('/my/file') FileTest.stubs(:exists?).with(path).returns true FileUtils.expects(:cp_r).with(path, path + ".foo", :preserve => true) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup.should be_true end it "should propagate exceptions if no backup can be created" do path = make_absolute('/my/file') FileTest.stubs(:exists?).with(path).returns true FileUtils.expects(:cp_r).raises ArgumentError file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') lambda { file.perform_backup }.should raise_error(Puppet::Error) end end end describe "when backing up a directory" do it "a bucket should work when provided" do path = make_absolute('/my/dir') File.stubs(:file?).returns true Find.expects(:find).with(path).yields(make_absolute("/my/dir/file")) bucket = stub('bucket', :name => "eh") bucket.expects(:backup).with(make_absolute("/my/dir/file")).returns true file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') file.stubs(:bucket).returns bucket File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) file.perform_backup end it "should do nothing when recursing" do path = make_absolute('/my/dir') bucket = stub('bucket', :name => "eh") bucket.expects(:backup).never file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true) file.stubs(:bucket).returns bucket File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) Find.expects(:find).never file.perform_backup end end end diff --git a/spec/unit/util/cache_accumulator_spec.rb b/spec/unit/util/cache_accumulator_spec.rb index 9c35cc353..b48570cdd 100755 --- a/spec/unit/util/cache_accumulator_spec.rb +++ b/spec/unit/util/cache_accumulator_spec.rb @@ -1,74 +1,74 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/rails/cache_accumulator' describe Puppet::Util::CacheAccumulator do before :each do @test_class = Class.new do attr_accessor :name include Puppet::Util::CacheAccumulator accumulates :name def initialize(n) self.name = n end end end it 'should delegate to underlying find_or_create_by_* method and accumulate results' do @test_class.expects(:find_or_create_by_name).with('foo').returns(@test_class.new('foo')).once obj = @test_class.accumulate_by_name('foo') obj.name.should == 'foo' @test_class.accumulate_by_name('foo').should == obj end it 'should delegate bulk lookups to find with appropriate arguments and returning result count' do @test_class.expects(:find).with( :all, :conditions => {:name => ['a', 'b', 'c']} ).returns(['a','b','c'].collect {|n| @test_class.new(n)}).once @test_class.accumulate_by_name('a', 'b', 'c').should == 3 end it 'should only need find_or_create_by_name lookup for missing bulk entries' do @test_class.expects(:find).with( :all, :conditions => {:name => ['a', 'b']} ).returns([ @test_class.new('a') ]).once @test_class.expects(:find_or_create_by_name).with('b').returns(@test_class.new('b')).once @test_class.expects(:find_or_create_by_name).with('a').never @test_class.accumulate_by_name('a','b').should == 1 @test_class.accumulate_by_name('a').name.should == 'a' @test_class.accumulate_by_name('b').name.should == 'b' end it 'should keep consumer classes separate' do @alt_class = Class.new do attr_accessor :name include Puppet::Util::CacheAccumulator accumulates :name def initialize(n) self.name = n end end name = 'foo' @test_class.expects(:find_or_create_by_name).with(name).returns(@test_class.new(name)).once @alt_class.expects(:find_or_create_by_name).with(name).returns(@alt_class.new(name)).once [@test_class, @alt_class].each do |klass| klass.accumulate_by_name(name).name.should == name klass.accumulate_by_name(name).class.should == klass end end it 'should clear accumulated cache with reset_*_accumulator' do # figure out how to test this appropriately... end end diff --git a/spec/unit/util/cacher_spec.rb b/spec/unit/util/cacher_spec.rb index 16414c858..e57167fc0 100755 --- a/spec/unit/util/cacher_spec.rb +++ b/spec/unit/util/cacher_spec.rb @@ -1,107 +1,107 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/cacher' class CacheTest @@count = 0 def self.count @@count end include Puppet::Util::Cacher cached_attr(:instance_cache, 10) do @@count += 1 {:number => @@count} end end describe Puppet::Util::Cacher do before :each do CacheTest.set_attr_ttl(:instance_cache, 10) @object = CacheTest.new end it "should return a value calculated from the provided block" do @object.instance_cache.should == {:number => CacheTest.count} end it "should return the cached value from the getter every time if the value is not expired" do @object.instance_cache.should equal(@object.instance_cache) end it "should regenerate and return a new value using the provided block if the value has expired" do initial = @object.instance_cache # Ensure the value is expired immediately CacheTest.set_attr_ttl(:instance_cache, -10) @object.send(:set_expiration, :instance_cache) @object.instance_cache.should_not equal(initial) end it "should be able to cache false values" do @object.expects(:init_instance_cache).once.returns false @object.instance_cache.should be_false @object.instance_cache.should be_false end it "should cache values again after expiration" do initial = @object.instance_cache # Ensure the value is expired immediately CacheTest.set_attr_ttl(:instance_cache, -10) @object.send(:set_expiration, :instance_cache) # Reset ttl so this new value doesn't get expired CacheTest.set_attr_ttl(:instance_cache, 10) after_expiration = @object.instance_cache after_expiration.should_not == initial @object.instance_cache.should == after_expiration end it "should allow writing of the attribute" do initial = @object.instance_cache @object.instance_cache = "another value" @object.instance_cache.should == "another value" end it "should update the expiration when the cached attribute is set manually" do # Freeze time now = Time.now Time.stubs(:now).returns now @object.instance_cache # Set expiration to something far in the future CacheTest.set_attr_ttl(:instance_cache, 60) @object.send(:set_expiration, :instance_cache) CacheTest.set_attr_ttl(:instance_cache, 10) @object.instance_cache = "foo" @object.instance_variable_get(:@attr_expirations)[:instance_cache].should == now + 10 end it "should allow specification of a ttl as a string" do klass = Class.new do include Puppet::Util::Cacher end klass.cached_attr(:myattr, "5") { 10 } klass.attr_ttl(:myattr).should == 5 end it "should fail helpfully if the ttl cannot be converted to an integer" do klass = Class.new do include Puppet::Util::Cacher end lambda { klass.cached_attr(:myattr, "yep") { 10 } }.should raise_error(ArgumentError) end end diff --git a/spec/unit/util/checksums_spec.rb b/spec/unit/util/checksums_spec.rb index 4fbc097c0..4cc690221 100755 --- a/spec/unit/util/checksums_spec.rb +++ b/spec/unit/util/checksums_spec.rb @@ -1,175 +1,175 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/checksums' describe Puppet::Util::Checksums do include PuppetSpec::Files before do @summer = Object.new @summer.extend(Puppet::Util::Checksums) end content_sums = [:md5, :md5lite, :sha1, :sha1lite] file_only = [:ctime, :mtime, :none] content_sums.each do |sumtype| it "should be able to calculate #{sumtype} sums from strings" do @summer.should be_respond_to(sumtype) end end [content_sums, file_only].flatten.each do |sumtype| it "should be able to calculate #{sumtype} sums from files" do @summer.should be_respond_to(sumtype.to_s + "_file") end end [content_sums, file_only].flatten.each do |sumtype| it "should be able to calculate #{sumtype} sums from stream" do @summer.should be_respond_to(sumtype.to_s + "_stream") end end it "should have a method for determining whether a given string is a checksum" do @summer.should respond_to(:checksum?) end %w{{md5}asdfasdf {sha1}asdfasdf {ctime}asdasdf {mtime}asdfasdf}.each do |sum| it "should consider #{sum} to be a checksum" do @summer.should be_checksum(sum) end end %w{{nosuchsum}asdfasdf {a}asdfasdf {ctime}}.each do |sum| it "should not consider #{sum} to be a checksum" do @summer.should_not be_checksum(sum) end end it "should have a method for stripping a sum type from an existing checksum" do @summer.sumtype("{md5}asdfasdfa").should == "md5" end it "should have a method for stripping the data from a checksum" do @summer.sumdata("{md5}asdfasdfa").should == "asdfasdfa" end it "should return a nil sumtype if the checksum does not mention a checksum type" do @summer.sumtype("asdfasdfa").should be_nil end {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass| describe("when using #{sum}") do it "should use #{klass} to calculate string checksums" do klass.expects(:hexdigest).with("mycontent").returns "whatever" @summer.send(sum, "mycontent").should == "whatever" end it "should use incremental #{klass} sums to calculate file checksums" do digest = mock 'digest' klass.expects(:new).returns digest file = "/path/to/my/file" fh = mock 'filehandle' fh.expects(:read).with(4096).times(3).returns("firstline").then.returns("secondline").then.returns(nil) #fh.expects(:read).with(512).returns("secondline") #fh.expects(:read).with(512).returns(nil) File.expects(:open).with(file, "rb").yields(fh) digest.expects(:<<).with "firstline" digest.expects(:<<).with "secondline" digest.expects(:hexdigest).returns :mydigest @summer.send(sum.to_s + "_file", file).should == :mydigest end it "should yield #{klass} to the given block to calculate stream checksums" do digest = mock 'digest' klass.expects(:new).returns digest digest.expects(:hexdigest).returns :mydigest @summer.send(sum.to_s + "_stream") do |sum| sum.should == digest end.should == :mydigest end end end {:md5lite => Digest::MD5, :sha1lite => Digest::SHA1}.each do |sum, klass| describe("when using #{sum}") do it "should use #{klass} to calculate string checksums from the first 512 characters of the string" do content = "this is a test" * 100 klass.expects(:hexdigest).with(content[0..511]).returns "whatever" @summer.send(sum, content).should == "whatever" end it "should use #{klass} to calculate a sum from the first 512 characters in the file" do digest = mock 'digest' klass.expects(:new).returns digest file = "/path/to/my/file" fh = mock 'filehandle' fh.expects(:read).with(512).returns('my content') File.expects(:open).with(file, "rb").yields(fh) digest.expects(:<<).with "my content" digest.expects(:hexdigest).returns :mydigest @summer.send(sum.to_s + "_file", file).should == :mydigest end end end [:ctime, :mtime].each do |sum| describe("when using #{sum}") do it "should use the '#{sum}' on the file to determine the ctime" do file = "/my/file" stat = mock 'stat', sum => "mysum" File.expects(:stat).with(file).returns(stat) @summer.send(sum.to_s + "_file", file).should == "mysum" end it "should return nil for streams" do expectation = stub "expectation" expectation.expects(:do_something!).at_least_once @summer.send(sum.to_s + "_stream"){ |checksum| checksum << "anything" ; expectation.do_something! }.should be_nil end end end describe "when using the none checksum" do it "should return an empty string" do @summer.none_file("/my/file").should == "" end it "should return an empty string for streams" do expectation = stub "expectation" expectation.expects(:do_something!).at_least_once @summer.none_stream{ |checksum| checksum << "anything" ; expectation.do_something! }.should == "" end end {:md5 => Digest::MD5, :sha1 => Digest::SHA1}.each do |sum, klass| describe "when using #{sum}" do let(:content) { "hello\r\nworld" } let(:path) do path = tmpfile("checksum_#{sum}") File.open(path, 'wb') {|f| f.write(content)} path end it "should preserve nl/cr sequences" do @summer.send(sum.to_s + "_file", path).should == klass.hexdigest(content) end end end end diff --git a/spec/unit/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb index 5f3b8649f..8b2f6d98c 100755 --- a/spec/unit/util/command_line_spec.rb +++ b/spec/unit/util/command_line_spec.rb @@ -1,155 +1,155 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/face' require 'puppet/util/command_line' describe Puppet::Util::CommandLine do include PuppetSpec::Files let :tty do stub("tty", :tty? => true) end let :pipe do stub("pipe", :tty? => false) end context "#initialize" do it "should pull off the first argument if it looks like a subcommand" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ client --help whatever.pp }, tty) command_line.subcommand_name.should == "client" command_line.args.should == %w{ --help whatever.pp } end it "should return nil if the first argument looks like a .pp file" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ whatever.pp }, tty) command_line.subcommand_name.should == nil command_line.args.should == %w{ whatever.pp } end it "should return nil if the first argument looks like a .rb file" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ whatever.rb }, tty) command_line.subcommand_name.should == nil command_line.args.should == %w{ whatever.rb } end it "should return nil if the first argument looks like a flag" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ --debug }, tty) command_line.subcommand_name.should == nil command_line.args.should == %w{ --debug } end it "should return nil if the first argument is -" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ - }, tty) command_line.subcommand_name.should == nil command_line.args.should == %w{ - } end it "should return nil if the first argument is --help" do command_line = Puppet::Util::CommandLine.new("puppet", %w{ --help }, tty) command_line.subcommand_name.should == nil end it "should return nil if there are no arguments on a tty" do command_line = Puppet::Util::CommandLine.new("puppet", [], tty) command_line.subcommand_name.should == nil command_line.args.should == [] end it "should return nil if there are no arguments on a pipe" do command_line = Puppet::Util::CommandLine.new("puppet", [], pipe) command_line.subcommand_name.should == nil command_line.args.should == [] end end context "#execute" do %w{--version -V}.each do |arg| it "should print the version and exit if #{arg} is given" do expect do described_class.new("puppet", [arg], tty).execute end.to have_printed(Puppet.version) end end end describe "when dealing with puppet commands" do it "should return the executable name if it is not puppet" do command_line = Puppet::Util::CommandLine.new("puppetmasterd", [], tty) command_line.subcommand_name.should == "puppetmasterd" end describe "when the subcommand is not implemented" do it "should find and invoke an executable with a hyphenated name" do commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], tty) Puppet::Util.expects(:which).with('puppet-whatever'). returns('/dev/null/puppet-whatever') # It is important that we abort at the point exec is called, because # the code (reasonably) assumes that if `exec` is called processing # immediately terminates, and we are replaced by the executed process. # # This raise isn't a perfect simulation of that, but it is enough to # validate that the system works, and ... well, if exec is broken we # have two problems, y'know. commandline.expects(:exec).with('/dev/null/puppet-whatever', 'argument'). raises(SystemExit) expect { commandline.execute }.to raise_error SystemExit end describe "and an external implementation cannot be found" do it "should abort and show the usage message" do commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'], tty) Puppet::Util.expects(:which).with('puppet-whatever').returns(nil) commandline.expects(:exec).never expect { commandline.execute }.to have_printed(/Unknown Puppet subcommand 'whatever'/) end end end describe 'when loading commands' do let :core_apps do %w{describe filebucket kick queue resource agent cert apply doc master} end let :command_line do Puppet::Util::CommandLine.new("foo", %w{ client --help whatever.pp }, tty) end it "should expose available_subcommands as a class method" do core_apps.each do |command| command_line.available_subcommands.should include command end end it 'should be able to find all existing commands' do core_apps.each do |command| command_line.available_subcommands.should include command end end describe 'when multiple paths have applications' do before do @dir=tmpdir('command_line_plugin_test') @appdir="#{@dir}/puppet/application" FileUtils.mkdir_p(@appdir) FileUtils.touch("#{@appdir}/foo.rb") $LOAD_PATH.unshift(@dir) # WARNING: MUST MATCH THE AFTER ACTIONS! end it 'should be able to find commands from both paths' do found = command_line.available_subcommands found.should include 'foo' core_apps.each { |cmd| found.should include cmd } end after do $LOAD_PATH.shift # WARNING: MUST MATCH THE BEFORE ACTIONS! end end end end end diff --git a/spec/unit/util/config_timeout_spec.rb b/spec/unit/util/config_timeout_spec.rb index bada38c65..245300cd0 100644 --- a/spec/unit/util/config_timeout_spec.rb +++ b/spec/unit/util/config_timeout_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/config_timeout' describe Puppet::Util::ConfigTimeout do # NOTE: in the future it might be a good idea to add an explicit "integer" type to # the settings types, in which case this would no longer be necessary. class TestConfigTimeout include Puppet::Util::ConfigTimeout end let :instance do TestConfigTimeout.new end context "when the config setting is a String" do context "which contains an integer" do it "should convert the string to an integer" do Puppet[:configtimeout] = "12" instance.timeout_interval.should == 12 end end context "which does not contain an integer do" do it "should raise an ArgumentError" do Puppet[:configtimeout] = "foo" expect { instance.timeout_interval }.to raise_error(ArgumentError) end end end context "when the config setting is an Integer" do it "should return the integer" do Puppet[:configtimeout] = 12 instance.timeout_interval.should == 12 end end context "when the config setting is some other type" do # test a random smattering of types [Hash.new, Array.new, Object.new].each do |obj| context "when the config setting is a #{obj.class}" do it "should raise an ArgumentError" do Puppet[:configtimeout] = Hash.new expect { instance.timeout_interval }.to raise_error(ArgumentError) end end end end -end \ No newline at end of file +end diff --git a/spec/unit/util/constant_inflector_spec.rb b/spec/unit/util/constant_inflector_spec.rb index de0ba7b7d..1b14d0773 100755 --- a/spec/unit/util/constant_inflector_spec.rb +++ b/spec/unit/util/constant_inflector_spec.rb @@ -1,56 +1,56 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/constant_inflector' describe Puppet::Util::ConstantInflector, "when converting file names to constants" do it "should capitalize terms" do subject.file2constant("file").should == "File" end it "should switch all '/' characters to double colons" do subject.file2constant("file/other").should == "File::Other" end it "should remove underscores and capitalize the proceeding letter" do subject.file2constant("file_other").should == "FileOther" end it "should correctly replace as many underscores as exist in the file name" do subject.file2constant("two_under_scores/with_some_more_underscores").should == "TwoUnderScores::WithSomeMoreUnderscores" end it "should collapse multiple underscores" do subject.file2constant("many___scores").should == "ManyScores" end it "should correctly handle file names deeper than two directories" do subject.file2constant("one_two/three_four/five_six").should == "OneTwo::ThreeFour::FiveSix" end end describe Puppet::Util::ConstantInflector, "when converting constnats to file names" do it "should convert them to a string if necessary" do subject.constant2file(Puppet::Util::ConstantInflector).should be_instance_of(String) end it "should accept string inputs" do subject.constant2file("Puppet::Util::ConstantInflector").should be_instance_of(String) end it "should downcase all terms" do subject.constant2file("Puppet").should == "puppet" end it "should convert '::' to '/'" do subject.constant2file("Puppet::Util::Constant").should == "puppet/util/constant" end it "should convert mid-word capitalization to an underscore" do subject.constant2file("OneTwo::ThreeFour").should == "one_two/three_four" end it "should correctly handle constants with more than two parts" do subject.constant2file("OneTwoThree::FourFiveSixSeven").should == "one_two_three/four_five_six_seven" end end diff --git a/spec/unit/util/errors_spec.rb b/spec/unit/util/errors_spec.rb index d51a15ef4..c4aa9e756 100755 --- a/spec/unit/util/errors_spec.rb +++ b/spec/unit/util/errors_spec.rb @@ -1,37 +1,37 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/errors' class ErrorTester include Puppet::Util::Errors attr_accessor :line, :file end describe Puppet::Util::Errors do before do @tester = ErrorTester.new end it "should provide a 'fail' method" do @tester.should respond_to(:fail) end it "should provide a 'devfail' method" do @tester.should respond_to(:devfail) end it "should raise any provided error when failing" do lambda { @tester.fail(Puppet::ParseError, "stuff") }.should raise_error(Puppet::ParseError) end it "should default to Puppet::Error when failing" do lambda { @tester.fail("stuff") }.should raise_error(Puppet::Error) end it "should have a method for converting error context into a string" do @tester.file = "/my/file" @tester.line = 50 @tester.error_context.should == " at /my/file:50" end end diff --git a/spec/unit/util/execution_spec.rb b/spec/unit/util/execution_spec.rb index 2dc47767d..19f75c85e 100755 --- a/spec/unit/util/execution_spec.rb +++ b/spec/unit/util/execution_spec.rb @@ -1,542 +1,542 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Util::Execution do include Puppet::Util::Execution # utility method to help deal with some windows vs. unix differences def process_status(exitstatus) return exitstatus if Puppet.features.microsoft_windows? stub('child_status', :exitstatus => exitstatus) end # utility methods to help us test some private methods without being quite so verbose def call_exec_posix(command, arguments, stdin, stdout, stderr) Puppet::Util::Execution.send(:execute_posix, command, arguments, stdin, stdout, stderr) end def call_exec_windows(command, arguments, stdin, stdout, stderr) Puppet::Util::Execution.send(:execute_windows, command, arguments, stdin, stdout, stderr) end describe "execution methods" do let(:pid) { 5501 } let(:process_handle) { 0xDEADBEEF } let(:thread_handle) { 0xCAFEBEEF } let(:proc_info_stub) { stub 'processinfo', :process_handle => process_handle, :thread_handle => thread_handle, :process_id => pid} let(:null_file) { Puppet.features.microsoft_windows? ? 'NUL' : '/dev/null' } def stub_process_wait(exitstatus) if Puppet.features.microsoft_windows? Puppet::Util::Windows::Process.stubs(:wait_process).with(process_handle).returns(exitstatus) Process.stubs(:CloseHandle).with(process_handle) Process.stubs(:CloseHandle).with(thread_handle) else Process.stubs(:waitpid2).with(pid).returns([pid, stub('child_status', :exitstatus => exitstatus)]) end end describe "#execute_posix (stubs)", :unless => Puppet.features.microsoft_windows? do before :each do # Most of the things this method does are bad to do during specs. :/ Kernel.stubs(:fork).returns(pid).yields Process.stubs(:setsid) Kernel.stubs(:exec) Puppet::Util::SUIDManager.stubs(:change_user) Puppet::Util::SUIDManager.stubs(:change_group) $stdin.stubs(:reopen) $stdout.stubs(:reopen) $stderr.stubs(:reopen) @stdin = File.open(null_file, 'r') @stdout = Tempfile.new('stdout') @stderr = File.open(null_file, 'w') # there is a danger here that ENV will be modified by exec_posix. Normally it would only affect the ENV # of a forked process, but here, we're stubbing Kernel.fork, so the method has the ability to override the # "real" ENV. To guard against this, we'll capture a snapshot of ENV before each test. @saved_env = ENV.to_hash # Now, we're going to effectively "mock" the magic ruby 'ENV' variable by creating a local definition of it # inside of the module we're testing. Puppet::Util::Execution::ENV = {} end after :each do # And here we remove our "mock" version of 'ENV', which will allow us to validate that the real ENV has been # left unharmed. Puppet::Util::Execution.send(:remove_const, :ENV) # capture the current environment and make sure it's the same as it was before the test cur_env = ENV.to_hash # we will get some fairly useless output if we just use the raw == operator on the hashes here, so we'll # be a bit more explicit and laborious in the name of making the error more useful... @saved_env.each_pair { |key,val| cur_env[key].should == val } (cur_env.keys - @saved_env.keys).should == [] end it "should fork a child process to execute the command" do Kernel.expects(:fork).returns(pid).yields Kernel.expects(:exec).with('test command') call_exec_posix('test command', {}, @stdin, @stdout, @stderr) end it "should start a new session group" do Process.expects(:setsid) call_exec_posix('test command', {}, @stdin, @stdout, @stderr) end it "should permanently change to the correct user and group if specified" do Puppet::Util::SUIDManager.expects(:change_group).with(55, true) Puppet::Util::SUIDManager.expects(:change_user).with(50, true) call_exec_posix('test command', {:uid => 50, :gid => 55}, @stdin, @stdout, @stderr) end it "should exit failure if there is a problem execing the command" do Kernel.expects(:exec).with('test command').raises("failed to execute!") Puppet::Util::Execution.stubs(:puts) Puppet::Util::Execution.expects(:exit!).with(1) call_exec_posix('test command', {}, @stdin, @stdout, @stderr) end it "should properly execute commands specified as arrays" do Kernel.expects(:exec).with('test command', 'with', 'arguments') call_exec_posix(['test command', 'with', 'arguments'], {:uid => 50, :gid => 55}, @stdin, @stdout, @stderr) end it "should properly execute string commands with embedded newlines" do Kernel.expects(:exec).with("/bin/echo 'foo' ; \n /bin/echo 'bar' ;") call_exec_posix("/bin/echo 'foo' ; \n /bin/echo 'bar' ;", {:uid => 50, :gid => 55}, @stdin, @stdout, @stderr) end it "should return the pid of the child process" do call_exec_posix('test command', {}, @stdin, @stdout, @stderr).should == pid end end describe "#execute_windows (stubs)", :if => Puppet.features.microsoft_windows? do before :each do Process.stubs(:create).returns(proc_info_stub) stub_process_wait(0) @stdin = File.open(null_file, 'r') @stdout = Tempfile.new('stdout') @stderr = File.open(null_file, 'w') end it "should create a new process for the command" do Process.expects(:create).with( :command_line => "test command", :startup_info => {:stdin => @stdin, :stdout => @stdout, :stderr => @stderr}, :close_handles => false ).returns(proc_info_stub) call_exec_windows('test command', {}, @stdin, @stdout, @stderr) end it "should return the process info of the child process" do call_exec_windows('test command', {}, @stdin, @stdout, @stderr).should == proc_info_stub end it "should quote arguments containing spaces if command is specified as an array" do Process.expects(:create).with do |args| args[:command_line] == '"test command" with some "arguments \"with spaces"' end.returns(proc_info_stub) call_exec_windows(['test command', 'with', 'some', 'arguments "with spaces'], {}, @stdin, @stdout, @stderr) end end describe "#execute (stubs)" do before :each do stub_process_wait(0) end describe "when an execution stub is specified" do before :each do Puppet::Util::ExecutionStub.set do |command,args,stdin,stdout,stderr| "execution stub output" end end it "should call the block on the stub" do Puppet::Util::Execution.execute("/usr/bin/run_my_execute_stub").should == "execution stub output" end it "should not actually execute anything" do Puppet::Util::Execution.expects(:execute_posix).never Puppet::Util::Execution.expects(:execute_windows).never Puppet::Util::Execution.execute("/usr/bin/run_my_execute_stub") end end describe "when setting up input and output files" do include PuppetSpec::Files let(:executor) { Puppet.features.microsoft_windows? ? 'execute_windows' : 'execute_posix' } let(:rval) { Puppet.features.microsoft_windows? ? proc_info_stub : pid } before :each do Puppet::Util::Execution.stubs(:wait_for_output) end it "should set stdin to the stdinfile if specified" do input = tmpfile('stdin') FileUtils.touch(input) Puppet::Util::Execution.expects(executor).with do |_,_,stdin,_,_| stdin.path == input end.returns(rval) Puppet::Util::Execution.execute('test command', :stdinfile => input) end it "should set stdin to the null file if not specified" do Puppet::Util::Execution.expects(executor).with do |_,_,stdin,_,_| stdin.path == null_file end.returns(rval) Puppet::Util::Execution.execute('test command') end describe "when squelch is set" do it "should set stdout and stderr to the null file" do Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr| stdout.path == null_file and stderr.path == null_file end.returns(rval) Puppet::Util::Execution.execute('test command', :squelch => true) end end describe "when squelch is not set" do it "should set stdout to a temporary output file" do outfile = Tempfile.new('stdout') Tempfile.stubs(:new).returns(outfile) Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,_| stdout.path == outfile.path end.returns(rval) Puppet::Util::Execution.execute('test command', :squelch => false) end it "should set stderr to the same file as stdout if combine is true" do outfile = Tempfile.new('stdout') Tempfile.stubs(:new).returns(outfile) Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr| stdout.path == outfile.path and stderr.path == outfile.path end.returns(rval) Puppet::Util::Execution.execute('test command', :squelch => false, :combine => true) end it "should set stderr to the null device if combine is false" do outfile = Tempfile.new('stdout') Tempfile.stubs(:new).returns(outfile) Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr| stdout.path == outfile.path and stderr.path == null_file end.returns(rval) Puppet::Util::Execution.execute('test command', :squelch => false, :combine => false) end end end describe "on Windows", :if => Puppet.features.microsoft_windows? do it "should always close the process and thread handles" do Puppet::Util::Execution.stubs(:execute_windows).returns(proc_info_stub) Puppet::Util::Windows::Process.expects(:wait_process).with(process_handle).raises('whatever') Process.expects(:CloseHandle).with(thread_handle) Process.expects(:CloseHandle).with(process_handle) expect { Puppet::Util::Execution.execute('test command') }.should raise_error(RuntimeError) end end end describe "#execute (posix locale)", :unless => Puppet.features.microsoft_windows? do before :each do # there is a danger here that ENV will be modified by exec_posix. Normally it would only affect the ENV # of a forked process, but, in some of the previous tests in this file we're stubbing Kernel.fork., which could # allow the method to override the "real" ENV. This shouldn't be a problem for these tests because they are # not stubbing Kernel.fork, but, better safe than sorry... so, to guard against this, we'll capture a snapshot # of ENV before each test. @saved_env = ENV.to_hash end after :each do # capture the current environment and make sure it's the same as it was before the test cur_env = ENV.to_hash # we will get some fairly useless output if we just use the raw == operator on the hashes here, so we'll # be a bit more explicit and laborious in the name of making the error more useful... @saved_env.each_pair { |key,val| cur_env[key].should == val } (cur_env.keys - @saved_env.keys).should == [] end # build up a printf-style string that contains a command to get the value of an environment variable # from the operating system. We can substitute into this with the names of the desired environment variables later. get_env_var_cmd = 'echo $%s' # a sentinel value that we can use to emulate what locale environment variables might be set to on an international # system. lang_sentinel_value = "es_ES.UTF-8" # a temporary hash that contains sentinel values for each of the locale environment variables that we override in # "execute" locale_sentinel_env = {} Puppet::Util::POSIX::LOCALE_ENV_VARS.each { |var| locale_sentinel_env[var] = lang_sentinel_value } it "should override the locale environment variables when :override_locale is not set (defaults to true)" do # temporarily override the locale environment vars with a sentinel value, so that we can confirm that # execute is actually setting them. Puppet::Util.withenv(locale_sentinel_env) do Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| # we expect that all of the POSIX vars will have been cleared except for LANG and LC_ALL expected_value = (['LANG', 'LC_ALL'].include?(var)) ? "C" : "" Puppet::Util::execute(get_env_var_cmd % var).strip.should == expected_value end end end it "should override the LANG environment variable when :override_locale is set to true" do # temporarily override the locale environment vars with a sentinel value, so that we can confirm that # execute is actually setting them. Puppet::Util.withenv(locale_sentinel_env) do Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| # we expect that all of the POSIX vars will have been cleared except for LANG and LC_ALL expected_value = (['LANG', 'LC_ALL'].include?(var)) ? "C" : "" Puppet::Util::execute(get_env_var_cmd % var, {:override_locale => true}).strip.should == expected_value end end end it "should *not* override the LANG environment variable when :override_locale is set to false" do # temporarily override the locale environment vars with a sentinel value, so that we can confirm that # execute is not setting them. Puppet::Util.withenv(locale_sentinel_env) do Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| Puppet::Util::execute(get_env_var_cmd % var, {:override_locale => false}).strip.should == lang_sentinel_value end end end it "should have restored the LANG and locale environment variables after execution" do # we'll do this once without any sentinel values, to give us a little more test coverage orig_env_vals = {} Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| orig_env_vals[var] = ENV[var] end # now we can really execute any command--doesn't matter what it is... Puppet::Util::execute(get_env_var_cmd % 'anything', {:override_locale => true}) # now we check and make sure the original environment was restored Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| ENV[var].should == orig_env_vals[var] end # now, once more... but with our sentinel values Puppet::Util.withenv(locale_sentinel_env) do # now we can really execute any command--doesn't matter what it is... Puppet::Util::execute(get_env_var_cmd % 'anything', {:override_locale => true}) # now we check and make sure the original environment was restored Puppet::Util::POSIX::LOCALE_ENV_VARS.each do |var| ENV[var].should == locale_sentinel_env[var] end end end end describe "#execute (posix user env vars)", :unless => Puppet.features.microsoft_windows? do # build up a printf-style string that contains a command to get the value of an environment variable # from the operating system. We can substitute into this with the names of the desired environment variables later. get_env_var_cmd = 'echo $%s' # a sentinel value that we can use to emulate what locale environment variables might be set to on an international # system. user_sentinel_value = "Abracadabra" # a temporary hash that contains sentinel values for each of the locale environment variables that we override in # "execute" user_sentinel_env = {} Puppet::Util::POSIX::USER_ENV_VARS.each { |var| user_sentinel_env[var] = user_sentinel_value } it "should unset user-related environment vars during execution" do # first we set up a temporary execution environment with sentinel values for the user-related environment vars # that we care about. Puppet::Util.withenv(user_sentinel_env) do # with this environment, we loop over the vars in question Puppet::Util::POSIX::USER_ENV_VARS.each do |var| # ensure that our temporary environment is set up as we expect ENV[var].should == user_sentinel_env[var] # run an "exec" via the provider and ensure that it unsets the vars Puppet::Util::execute(get_env_var_cmd % var).strip.should == "" # ensure that after the exec, our temporary env is still intact ENV[var].should == user_sentinel_env[var] end end end it "should have restored the user-related environment variables after execution" do # we'll do this once without any sentinel values, to give us a little more test coverage orig_env_vals = {} Puppet::Util::POSIX::USER_ENV_VARS.each do |var| orig_env_vals[var] = ENV[var] end # now we can really execute any command--doesn't matter what it is... Puppet::Util::execute(get_env_var_cmd % 'anything') # now we check and make sure the original environment was restored Puppet::Util::POSIX::USER_ENV_VARS.each do |var| ENV[var].should == orig_env_vals[var] end # now, once more... but with our sentinel values Puppet::Util.withenv(user_sentinel_env) do # now we can really execute any command--doesn't matter what it is... Puppet::Util::execute(get_env_var_cmd % 'anything') # now we check and make sure the original environment was restored Puppet::Util::POSIX::USER_ENV_VARS.each do |var| ENV[var].should == user_sentinel_env[var] end end end end describe "after execution" do before :each do stub_process_wait(0) if Puppet.features.microsoft_windows? Puppet::Util::Execution.stubs(:execute_windows).returns(proc_info_stub) else Puppet::Util::Execution.stubs(:execute_posix).returns(pid) end end it "should wait for the child process to exit" do Puppet::Util::Execution.stubs(:wait_for_output) Puppet::Util::Execution.execute('test command') end it "should close the stdin/stdout/stderr files used by the child" do stdin = mock 'file', :close stdout = mock 'file', :close stderr = mock 'file', :close File.expects(:open). times(3). returns(stdin). then.returns(stdout). then.returns(stderr) Puppet::Util::Execution.execute('test command', {:squelch => true, :combine => false}) end it "should read and return the output if squelch is false" do stdout = Tempfile.new('test') Tempfile.stubs(:new).returns(stdout) stdout.write("My expected command output") Puppet::Util::Execution.execute('test command').should == "My expected command output" end it "should not read the output if squelch is true" do stdout = Tempfile.new('test') Tempfile.stubs(:new).returns(stdout) stdout.write("My expected command output") Puppet::Util::Execution.execute('test command', :squelch => true).should == nil end it "should delete the file used for output if squelch is false" do stdout = Tempfile.new('test') path = stdout.path Tempfile.stubs(:new).returns(stdout) Puppet::Util::Execution.execute('test command') File.should_not be_exist(path) end it "should raise an error if failonfail is true and the child failed" do stub_process_wait(1) expect { Puppet::Util::Execution.execute('fail command', :failonfail => true) }.to raise_error(Puppet::ExecutionFailure, /Execution of 'fail command' returned 1/) end it "should not raise an error if failonfail is false and the child failed" do stub_process_wait(1) expect { Puppet::Util::Execution.execute('fail command', :failonfail => false) }.not_to raise_error end it "should not raise an error if failonfail is true and the child succeeded" do expect { Puppet::Util::Execution.execute('fail command', :failonfail => true) }.not_to raise_error end it "should respect default values for args that aren't overridden if a partial arg list is passed in" do stub_process_wait(1) expect { # here we are passing in a non-nil value for "arguments", but we aren't specifying a value for # :failonfail. We expect it to be set to its normal default value (true). Puppet::Util::Execution.execute('fail command', { :squelch => true }) }.to raise_error(Puppet::ExecutionFailure, /Execution of 'fail command' returned 1/) end end end describe "#execpipe" do it "should execute a string as a string" do Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello') $CHILD_STATUS.expects(:==).with(0).returns(true) Puppet::Util::Execution.execpipe('echo hello').should == 'hello' end it "should execute an array by pasting together with spaces" do Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello') $CHILD_STATUS.expects(:==).with(0).returns(true) Puppet::Util::Execution.execpipe(['echo', 'hello']).should == 'hello' end it "should fail if asked to fail, and the child does" do Puppet::Util::Execution.stubs(:open).returns('error message') $CHILD_STATUS.expects(:==).with(0).returns(false) expect { Puppet::Util::Execution.execpipe('echo hello') }. to raise_error Puppet::ExecutionFailure, /error message/ end it "should not fail if asked not to fail, and the child does" do Puppet::Util::Execution.stubs(:open).returns('error message') $CHILD_STATUS.stubs(:==).with(0).returns(false) expect do Puppet::Util::Execution.execpipe('echo hello', false).should == 'error message' end.not_to raise_error end end end diff --git a/spec/unit/util/execution_stub_spec.rb b/spec/unit/util/execution_stub_spec.rb index a931489e8..56a709057 100755 --- a/spec/unit/util/execution_stub_spec.rb +++ b/spec/unit/util/execution_stub_spec.rb @@ -1,39 +1,39 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Util::ExecutionStub do it "should use the provided stub code when 'set' is called" do Puppet::Util::ExecutionStub.set do |command, options| command.should == ['/bin/foo', 'bar'] "stub output" end Puppet::Util::ExecutionStub.current_value.should_not == nil Puppet::Util::Execution.execute(['/bin/foo', 'bar']).should == "stub output" end it "should automatically restore normal execution at the conclusion of each spec test" do # Note: this test relies on the previous test creating a stub. Puppet::Util::ExecutionStub.current_value.should == nil end it "should restore normal execution after 'reset' is called" do # Note: "true" exists at different paths in different OSes if Puppet.features.microsoft_windows? true_command = [Puppet::Util.which('cmd.exe').tr('/', '\\'), '/c', 'exit 0'] else true_command = [Puppet::Util.which('true')] end stub_call_count = 0 Puppet::Util::ExecutionStub.set do |command, options| command.should == true_command stub_call_count += 1 'stub called' end Puppet::Util::Execution.execute(true_command).should == 'stub called' stub_call_count.should == 1 Puppet::Util::ExecutionStub.reset Puppet::Util::ExecutionStub.current_value.should == nil Puppet::Util::Execution.execute(true_command).should == '' stub_call_count.should == 1 end end diff --git a/spec/unit/util/feature_spec.rb b/spec/unit/util/feature_spec.rb index a0fdc1269..36992be8f 100755 --- a/spec/unit/util/feature_spec.rb +++ b/spec/unit/util/feature_spec.rb @@ -1,81 +1,81 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/feature' describe Puppet::Util::Feature do before do @features = Puppet::Util::Feature.new("features") @features.stubs(:warn) end it "should consider undefined features to be absent" do @features.should_not be_defined_feature end it "should be able to add new features" do @features.add(:myfeature) {} @features.should respond_to(:myfeature?) end it "should call associated code when loading a feature" do $loaded_feature = false @features.add(:myfeature) { $loaded_feature = true} $loaded_feature.should be_true end it "should consider a feature absent when the feature load fails" do @features.add(:failer) { raise "foo" } @features.should_not be_failer end it "should consider a feature to be absent when the feature load returns false" do @features.add(:failer) { false } @features.should_not be_failer end it "should consider a feature to be present when the feature load returns true" do @features.add(:available) { true } @features.should be_available end it "should cache the results of a feature load" do $loaded_feature = 0 @features.add(:myfeature) { $loaded_feature += 1 } @features.myfeature? @features.myfeature? $loaded_feature.should == 1 end it "should invalidate the cache for the feature when loading" do # block defined features are evaluated at load time @features.add(:myfeature) { false } @features.should_not be_myfeature # features with no block have deferred evaluation so an existing cached # value would take precedence @features.add(:myfeature) @features.should be_myfeature end it "should support features with libraries" do lambda { @features.add(:puppet, :libs => %w{puppet}) }.should_not raise_error end it "should consider a feature to be present if all of its libraries are present" do @features.add(:myfeature, :libs => %w{foo bar}) @features.expects(:require).with("foo") @features.expects(:require).with("bar") @features.should be_myfeature end it "should log and consider a feature to be absent if any of its libraries are absent" do @features.add(:myfeature, :libs => %w{foo bar}) @features.expects(:require).with("foo").raises(LoadError) @features.stubs(:require).with("bar") Puppet.expects(:debug) @features.should_not be_myfeature end end diff --git a/spec/unit/util/filetype_spec.rb b/spec/unit/util/filetype_spec.rb index a2c0da660..e0deddf4a 100755 --- a/spec/unit/util/filetype_spec.rb +++ b/spec/unit/util/filetype_spec.rb @@ -1,99 +1,99 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/filetype' # XXX Import all of the tests into this file. describe Puppet::Util::FileType do describe "when backing up a file" do before do @file = Puppet::Util::FileType.filetype(:flat).new("/my/file") end it "should do nothing if the file does not exist" do File.expects(:exists?).with("/my/file").returns false @file.expects(:bucket).never @file.backup end it "should use its filebucket to backup the file if it exists" do File.expects(:exists?).with("/my/file").returns true bucket = mock 'bucket' bucket.expects(:backup).with("/my/file") @file.expects(:bucket).returns bucket @file.backup end it "should use the default filebucket" do bucket = mock 'bucket' bucket.expects(:bucket).returns "mybucket" Puppet::Type.type(:filebucket).expects(:mkdefaultbucket).returns bucket @file.bucket.should == "mybucket" end end describe "the flat filetype" do before do @type = Puppet::Util::FileType.filetype(:flat) end it "should exist" do @type.should_not be_nil end describe "when the file already exists" do it "should return the file's contents when asked to read it" do file = @type.new("/my/file") File.expects(:exist?).with("/my/file").returns true File.expects(:read).with("/my/file").returns "my text" file.read.should == "my text" end it "should unlink the file when asked to remove it" do file = @type.new("/my/file") File.expects(:exist?).with("/my/file").returns true File.expects(:unlink).with("/my/file") file.remove end end describe "when the file does not exist" do it "should return an empty string when asked to read the file" do file = @type.new("/my/file") File.expects(:exist?).with("/my/file").returns false file.read.should == "" end end describe "when writing the file" do before do @file = @type.new("/my/file") FileUtils.stubs(:cp) @tempfile = stub 'tempfile', :print => nil, :close => nil, :flush => nil, :path => "/other/file" Tempfile.stubs(:new).returns @tempfile end it "should first create a temp file and copy its contents over to the file location" do Tempfile.expects(:new).with("puppet").returns @tempfile @tempfile.expects(:print).with("my text") @tempfile.expects(:flush) @tempfile.expects(:close) FileUtils.expects(:cp).with(@tempfile.path, "/my/file") @file.write "my text" end it "should set the selinux default context on the file" do @file.expects(:set_selinux_default_context).with("/my/file") @file.write "eh" end end end end diff --git a/spec/unit/util/inline_docs_spec.rb b/spec/unit/util/inline_docs_spec.rb index 1d88180b3..186314b1a 100755 --- a/spec/unit/util/inline_docs_spec.rb +++ b/spec/unit/util/inline_docs_spec.rb @@ -1,31 +1,31 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/inline_docs' class InlineDoccer include Puppet::Util::InlineDocs end describe Puppet::Util::InlineDocs do describe "when included" do it "should create a class method for specifying that docs should be associated" do InlineDoccer.expects(:use_docs=).with true InlineDoccer.associates_doc end it "should default to not associating docs" do (!! InlineDoccer.use_docs).should be_false end it "should create an instance method for setting documentation" do instance = InlineDoccer.new instance.doc = "foo" instance.doc.should == "foo" end it "should default to an empty string for docs" do InlineDoccer.new.doc.should == "" end end end diff --git a/spec/unit/util/instrumentation/data_spec.rb b/spec/unit/util/instrumentation/data_spec.rb index 4a5fd9508..fb2f335fb 100755 --- a/spec/unit/util/instrumentation/data_spec.rb +++ b/spec/unit/util/instrumentation/data_spec.rb @@ -1,44 +1,44 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'matchers/json' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/data' describe Puppet::Util::Instrumentation::Data do Puppet::Util::Instrumentation::Data before(:each) do @listener = stub 'listener', :name => "name" Puppet::Util::Instrumentation.stubs(:[]).with("name").returns(@listener) end it "should indirect instrumentation_data" do Puppet::Util::Instrumentation::Data.indirection.name.should == :instrumentation_data end it "should lookup the corresponding listener" do Puppet::Util::Instrumentation.expects(:[]).with("name").returns(@listener) Puppet::Util::Instrumentation::Data.new("name") end it "should error if the listener can not be found" do Puppet::Util::Instrumentation.expects(:[]).with("name").returns(nil) expect { Puppet::Util::Instrumentation::Data.new("name") }.to raise_error end it "should return pson data" do data = Puppet::Util::Instrumentation::Data.new("name") @listener.stubs(:data).returns({ :this_is_data => "here also" }) data.should set_json_attribute('name').to("name") data.should set_json_attribute('this_is_data').to("here also") end it "should not error if the underlying listener doesn't have data" do lambda { Puppet::Util::Instrumentation::Data.new("name").to_pson }.should_not raise_error end it "should return a hash containing data when unserializing from pson" do Puppet::Util::Instrumentation::Data.from_pson({:name => "name"}).should == {:name => "name"} end end diff --git a/spec/unit/util/instrumentation/indirection_probe_spec.rb b/spec/unit/util/instrumentation/indirection_probe_spec.rb index 5f4e82866..3c3f88141 100644 --- a/spec/unit/util/instrumentation/indirection_probe_spec.rb +++ b/spec/unit/util/instrumentation/indirection_probe_spec.rb @@ -1,19 +1,19 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'matchers/json' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/indirection_probe' describe Puppet::Util::Instrumentation::IndirectionProbe do Puppet::Util::Instrumentation::IndirectionProbe it "should indirect instrumentation_probe" do Puppet::Util::Instrumentation::IndirectionProbe.indirection.name.should == :instrumentation_probe end it "should return pson data" do probe = Puppet::Util::Instrumentation::IndirectionProbe.new("probe") probe.should set_json_attribute('name').to("probe") end end diff --git a/spec/unit/util/instrumentation/instrumentable_spec.rb b/spec/unit/util/instrumentation/instrumentable_spec.rb index 82f481dfd..ed280d716 100755 --- a/spec/unit/util/instrumentation/instrumentable_spec.rb +++ b/spec/unit/util/instrumentation/instrumentable_spec.rb @@ -1,186 +1,186 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/instrumentable' describe Puppet::Util::Instrumentation::Instrumentable::Probe do before(:each) do Puppet::Util::Instrumentation.stubs(:start) Puppet::Util::Instrumentation.stubs(:stop) class ProbeTest def mymethod(arg1, arg2, arg3) :it_worked end end end after(:each) do if ProbeTest.method_defined?(:instrumented_mymethod) ProbeTest.class_eval { remove_method(:mymethod) alias_method(:mymethod, :instrumented_mymethod) } end Puppet::Util::Instrumentation::Instrumentable.clear_probes end describe "when enabling a probe" do it "should raise an error if the probe is already enabled" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable lambda { probe.enable }.should raise_error end it "should rename the original method name" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable ProbeTest.new.should respond_to(:instrumented_mymethod) end it "should create a new method of the original name" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable ProbeTest.new.should respond_to(:mymethod) end end describe "when disabling a probe" do it "should raise an error if the probe is already enabled" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) lambda { probe.disable }.should raise_error end it "should rename the original method name" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable probe.disable Puppet::Util::Instrumentation.expects(:start).never Puppet::Util::Instrumentation.expects(:stop).never ProbeTest.new.mymethod(1,2,3).should == :it_worked end it "should remove the created method" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable probe.disable ProbeTest.new.should_not respond_to(:instrumented_mymethod) end end describe "when a probe is called" do it "should call the original method" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.expects(:instrumented_mymethod).with(1,2,3) test.mymethod(1,2,3) end it "should start the instrumentation" do Puppet::Util::Instrumentation.expects(:start) probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.mymethod(1,2,3) end it "should stop the instrumentation" do Puppet::Util::Instrumentation.expects(:stop) probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.mymethod(1,2,3) end describe "and the original method raises an exception" do it "should propagate the exception" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.expects(:instrumented_mymethod).with(1,2,3).raises lambda { test.mymethod(1,2,3) }.should raise_error end it "should stop the instrumentation" do Puppet::Util::Instrumentation.expects(:stop) probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest) probe.enable test = ProbeTest.new test.expects(:instrumented_mymethod).with(1,2,3).raises lambda { test.mymethod(1,2,3) }.should raise_error end end describe "with a static label" do it "should send the label to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :label => :mylabel) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| label == :mylabel }.returns(42) Puppet::Util::Instrumentation.expects(:stop).with(:mylabel, 42, {}) test.mymethod(1,2,3) end end describe "with a dynamic label" do it "should send the evaluated label to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :label => Proc.new { |parent,args| "dynamic#{args[0]}" } ) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| label == "dynamic1" }.returns(42) Puppet::Util::Instrumentation.expects(:stop).with("dynamic1",42,{}) test.mymethod(1,2,3) end end describe "with static data" do it "should send the data to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :data => { :static_data => "nothing" }) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| data == { :static_data => "nothing" }} test.mymethod(1,2,3) end end describe "with dynamic data" do it "should send the evaluated label to the instrumentation layer" do probe = Puppet::Util::Instrumentation::Instrumentable::Probe.new(:mymethod, ProbeTest, :data => Proc.new { |parent, args| { :key => args[0] } } ) probe.enable test = ProbeTest.new Puppet::Util::Instrumentation.expects(:start).with { |label,data| data == { :key => 1 } } Puppet::Util::Instrumentation.expects(:stop) test.mymethod(1,2,3) end end end end describe Puppet::Util::Instrumentation::Instrumentable do before(:each) do class ProbeTest2 extend Puppet::Util::Instrumentation::Instrumentable probe :mymethod def mymethod(arg1,arg2,arg3) end end end after do Puppet::Util::Instrumentation::Instrumentable.clear_probes end it "should allow probe definition" do Puppet::Util::Instrumentation::Instrumentable.probe_names.should be_include("ProbeTest2.mymethod") end it "should be able to enable all probes" do Puppet::Util::Instrumentation::Instrumentable.enable_probes ProbeTest2.new.should respond_to(:instrumented_mymethod) end end diff --git a/spec/unit/util/instrumentation/listener_spec.rb b/spec/unit/util/instrumentation/listener_spec.rb index bc7ffcf68..afa9e47c6 100755 --- a/spec/unit/util/instrumentation/listener_spec.rb +++ b/spec/unit/util/instrumentation/listener_spec.rb @@ -1,100 +1,100 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'matchers/json' require 'puppet/util/instrumentation' require 'puppet/util/instrumentation/listener' describe Puppet::Util::Instrumentation::Listener do Listener = Puppet::Util::Instrumentation::Listener before(:each) do @delegate = stub 'listener', :notify => nil, :name => 'listener' @listener = Listener.new(@delegate) @listener.enabled = true end it "should indirect instrumentation_listener" do Listener.indirection.name.should == :instrumentation_listener end it "should raise an error if delegate doesn't support notify" do lambda { Listener.new(Object.new) }.should raise_error end it "should not be enabled by default" do Listener.new(@delegate).should_not be_enabled end it "should delegate notification" do @delegate.expects(:notify).with(:event, :start, {}) listener = Listener.new(@delegate) listener.notify(:event, :start, {}) end it "should not listen is not enabled" do @listener.enabled = false @listener.should_not be_listen_to(:label) end it "should listen to all label if created without pattern" do @listener.should be_listen_to(:improbable_label) end it "should listen to specific string pattern" do listener = Listener.new(@delegate, "specific") listener.enabled = true listener.should be_listen_to(:specific) end it "should not listen to non-matching string pattern" do listener = Listener.new(@delegate, "specific") listener.enabled = true listener.should_not be_listen_to(:unspecific) end it "should listen to specific regex pattern" do listener = Listener.new(@delegate, /spe.*/) listener.enabled = true listener.should be_listen_to(:specific_pattern) end it "should not listen to non matching regex pattern" do listener = Listener.new(@delegate, /^match.*/) listener.enabled = true listener.should_not be_listen_to(:not_matching) end it "should delegate its name to the underlying listener" do @delegate.expects(:name).returns("myname") @listener.name.should == "myname" end it "should delegate data fetching to the underlying listener" do @delegate.expects(:data).returns(:data) @listener.data.should == {:data => :data } end describe "when serializing to pson" do it "should return a pson object containing pattern, name and status" do @listener.should set_json_attribute('enabled').to(true) @listener.should set_json_attribute('name').to("listener") end end describe "when deserializing from pson" do it "should lookup the archetype listener from the instrumentation layer" do Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener"}) end it "should create a new listener shell instance delegating to the archetypal listener" do Puppet::Util::Instrumentation.expects(:[]).with("listener").returns(@listener) @listener.stubs(:listener).returns(@delegate) Puppet::Util::Instrumentation::Listener.expects(:new).with(@delegate, nil, true) Puppet::Util::Instrumentation::Listener.from_pson({"name" => "listener", "enabled" => true}) end end end diff --git a/spec/unit/util/instrumentation_spec.rb b/spec/unit/util/instrumentation_spec.rb index ce6c32407..ee5b0b585 100755 --- a/spec/unit/util/instrumentation_spec.rb +++ b/spec/unit/util/instrumentation_spec.rb @@ -1,181 +1,181 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/instrumentation' describe Puppet::Util::Instrumentation do Instrumentation = Puppet::Util::Instrumentation after(:each) do Instrumentation.clear end it "should instance-load instrumentation listeners" do Instrumentation.instance_loader(:listener).should be_instance_of(Puppet::Util::Autoload) end it "should have a method for registering instrumentation listeners" do Instrumentation.should respond_to(:new_listener) end it "should have a method for retrieving instrumentation listener by name" do Instrumentation.should respond_to(:listener) end describe "when registering listeners" do it "should evaluate the supplied block as code for a class" do Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } end it "should subscribe a new listener instance" do Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } Instrumentation.listeners.size.should == 1 Instrumentation.listeners[0].pattern.should == "for_this_label" end it "should be possible to access listeners by name" do Instrumentation.expects(:genclass).returns(Class.new { def notify(label, event, data) ; end }) Instrumentation.new_listener(:testing, :label_pattern => :for_this_label, :event => :all) { } Instrumentation["testing"].should_not be_nil end it "should be possible to store a new listener by name" do listener = stub 'listener' Instrumentation["testing"] = listener Instrumentation["testing"].should == listener end it "should fail if listener is already subscribed" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) expect { Instrumentation.subscribe(listener, :for_this_label, :all) }.to raise_error end it 'should call #unsubscribed' do listener = stub 'listener', :notify => nil, :name => "mylistener" listener.expects(:subscribed) Instrumentation.subscribe(listener, :for_this_label, :all) end end describe "when unsubscribing listener" do it "should remove it from the listeners" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) Instrumentation.unsubscribe(listener) Instrumentation.listeners.size.should == 0 end it "should warn if the listener wasn't subscribed" do listener = stub 'listener', :notify => nil, :name => "mylistener" Puppet.expects(:warning) Instrumentation.unsubscribe(listener) end it 'should call #unsubscribed' do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) listener.expects(:unsubscribed) Instrumentation.unsubscribe(listener) end end describe "when firing events" do it "should be able to find all listeners matching a label" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) Instrumentation.listeners[0].enabled = true count = 0 Instrumentation.each_listener(:for_this_label) { |l| count += 1 } count.should == 1 end it "should fire events to matching listeners" do listener = stub 'listener', :notify => nil, :name => "mylistener" Instrumentation.subscribe(listener, :for_this_label, :all) Instrumentation.listeners[0].enabled = true listener.expects(:notify).with(:for_this_label, :start, {}) Instrumentation.publish(:for_this_label, :start, {}) end it "should not fire events to non-matching listeners" do listener1 = stub 'listener1', :notify => nil, :name => "mylistener1" listener2 = stub 'listener2', :notify => nil, :name => "mylistener2" Instrumentation.subscribe(listener1, :for_this_label, :all) Instrumentation.listeners[0].enabled = true Instrumentation.subscribe(listener2, :for_this_other_label, :all) Instrumentation.listeners[1].enabled = true listener1.expects(:notify).never listener2.expects(:notify).with(:for_this_other_label, :start, {}) Instrumentation.publish(:for_this_other_label, :start, {}) end end describe "when instrumenting code" do before(:each) do Instrumentation.stubs(:publish) end describe "with a block" do it "should execute it" do executed = false Instrumentation.instrument(:event) do executed = true end executed.should be_true end it "should publish an event before execution" do Instrumentation.expects(:publish).with { |label,event,data| label == :event && event == :start } Instrumentation.instrument(:event) {} end it "should publish an event after execution" do Instrumentation.expects(:publish).with { |label,event,data| label == :event && event == :stop } Instrumentation.instrument(:event) {} end it "should publish the event even when block raised an exception" do Instrumentation.expects(:publish).with { |label,event,data| label == :event } lambda { Instrumentation.instrument(:event) { raise "not working" } }.should raise_error end it "should retain start end finish time of the event" do Instrumentation.expects(:publish).with { |label,event,data| data.include?(:started) and data.include?(:finished) } Instrumentation.instrument(:event) {} end end describe "without a block" do it "should raise an error if stop is called with no matching start" do lambda{ Instrumentation.stop(:event) }.should raise_error end it "should publish an event on stop" do Instrumentation.expects(:publish).with { |label,event,data| event == :start } Instrumentation.expects(:publish).with { |label,event,data| event == :stop and data.include?(:started) and data.include?(:finished) } data = {} Instrumentation.start(:event, data) Instrumentation.stop(:event, 1, data) end it "should return a different id per event" do data = {} Instrumentation.start(:event, data).should == 1 Instrumentation.start(:event, data).should == 2 end end end end diff --git a/spec/unit/util/json_lockfile_spec.rb b/spec/unit/util/json_lockfile_spec.rb index a66e3866d..c4ae64026 100644 --- a/spec/unit/util/json_lockfile_spec.rb +++ b/spec/unit/util/json_lockfile_spec.rb @@ -1,29 +1,29 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/json_lockfile' describe Puppet::Util::JsonLockfile do require 'puppet_spec/files' include PuppetSpec::Files before(:each) do @lockfile = tmpfile("lock") @lock = Puppet::Util::JsonLockfile.new(@lockfile) end describe "#lock" do it "should create a lock file containing a json hash" do data = { "foo" => "foofoo", "bar" => "barbar" } @lock.lock(data) PSON.parse(File.read(@lockfile)).should == data end end it "should return the lock data" do data = { "foo" => "foofoo", "bar" => "barbar" } @lock.lock(data) @lock.lock_data.should == data end -end \ No newline at end of file +end diff --git a/spec/unit/util/ldap/connection_spec.rb b/spec/unit/util/ldap/connection_spec.rb index f02ea10cb..a58ff62be 100755 --- a/spec/unit/util/ldap/connection_spec.rb +++ b/spec/unit/util/ldap/connection_spec.rb @@ -1,165 +1,165 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/ldap/connection' # So our mocks and such all work, even when ldap isn't available. unless Puppet.features.ldap? class LDAP class Conn def initialize(*args) end end class SSLConn < Conn; end LDAP_OPT_PROTOCOL_VERSION = 1 LDAP_OPT_REFERRALS = 2 LDAP_OPT_ON = 3 end end describe Puppet::Util::Ldap::Connection do before do Puppet.features.stubs(:ldap?).returns true @ldapconn = mock 'ldap' LDAP::Conn.stubs(:new).returns(@ldapconn) LDAP::SSLConn.stubs(:new).returns(@ldapconn) @ldapconn.stub_everything @connection = Puppet::Util::Ldap::Connection.new("host", "port") end describe "when creating connections" do it "should require the host and port" do lambda { Puppet::Util::Ldap::Connection.new("myhost") }.should raise_error(ArgumentError) end it "should allow specification of a user and password" do lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :user => "blah", :password => "boo") }.should_not raise_error end it "should allow specification of ssl" do lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :ssl => :tsl) }.should_not raise_error end it "should support requiring a new connection" do lambda { Puppet::Util::Ldap::Connection.new("myhost", "myport", :reset => true) }.should_not raise_error end it "should fail if ldap is unavailable" do Puppet.features.expects(:ldap?).returns(false) lambda { Puppet::Util::Ldap::Connection.new("host", "port") }.should raise_error(Puppet::Error) end it "should use neither ssl nor tls by default" do LDAP::Conn.expects(:new).with("host", "port").returns(@ldapconn) @connection.start end it "should use LDAP::SSLConn if ssl is requested" do LDAP::SSLConn.expects(:new).with("host", "port").returns(@ldapconn) @connection.ssl = true @connection.start end it "should use LDAP::SSLConn and tls if tls is requested" do LDAP::SSLConn.expects(:new).with("host", "port", true).returns(@ldapconn) @connection.ssl = :tls @connection.start end it "should set the protocol version to 3 and enable referrals" do @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) @ldapconn.expects(:set_option).with(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) @connection.start end it "should bind with the provided user and password" do @connection.user = "myuser" @connection.password = "mypassword" @ldapconn.expects(:simple_bind).with("myuser", "mypassword") @connection.start end it "should bind with no user and password if none has been provided" do @ldapconn.expects(:simple_bind).with(nil, nil) @connection.start end end describe "when closing connections" do it "should not close connections that are not open" do @connection.stubs(:connection).returns(@ldapconn) @ldapconn.expects(:bound?).returns false @ldapconn.expects(:unbind).never @connection.close end end it "should have a class-level method for creating a default connection" do Puppet::Util::Ldap::Connection.should respond_to(:instance) end describe "when creating a default connection" do before do Puppet.settings.stubs(:value).returns "whatever" end it "should use the :ldapserver setting to determine the host" do Puppet.settings.expects(:value).with(:ldapserver).returns "myserv" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| host == "myserv" } Puppet::Util::Ldap::Connection.instance end it "should use the :ldapport setting to determine the port" do Puppet.settings.expects(:value).with(:ldapport).returns "456" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| port == "456" } Puppet::Util::Ldap::Connection.instance end it "should set ssl to :tls if tls is enabled" do Puppet.settings.expects(:value).with(:ldaptls).returns true Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == :tls } Puppet::Util::Ldap::Connection.instance end it "should set ssl to 'true' if ssl is enabled and tls is not" do Puppet.settings.expects(:value).with(:ldaptls).returns false Puppet.settings.expects(:value).with(:ldapssl).returns true Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == true } Puppet::Util::Ldap::Connection.instance end it "should set ssl to false if neither ssl nor tls are enabled" do Puppet.settings.expects(:value).with(:ldaptls).returns false Puppet.settings.expects(:value).with(:ldapssl).returns false Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:ssl] == false } Puppet::Util::Ldap::Connection.instance end it "should set the ldapuser if one is set" do Puppet.settings.expects(:value).with(:ldapuser).returns "foo" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" } Puppet::Util::Ldap::Connection.instance end it "should set the ldapuser and ldappassword if both is set" do Puppet.settings.expects(:value).with(:ldapuser).returns "foo" Puppet.settings.expects(:value).with(:ldappassword).returns "bar" Puppet::Util::Ldap::Connection.expects(:new).with { |host, port, options| options[:user] == "foo" and options[:password] == "bar" } Puppet::Util::Ldap::Connection.instance end end end diff --git a/spec/unit/util/ldap/generator_spec.rb b/spec/unit/util/ldap/generator_spec.rb index 51285e233..af3ac7080 100755 --- a/spec/unit/util/ldap/generator_spec.rb +++ b/spec/unit/util/ldap/generator_spec.rb @@ -1,50 +1,50 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/ldap/generator' describe Puppet::Util::Ldap::Generator do before do @generator = Puppet::Util::Ldap::Generator.new(:uno) end it "should require a parameter name at initialization" do lambda { Puppet::Util::Ldap::Generator.new }.should raise_error end it "should always return its name as a string" do g = Puppet::Util::Ldap::Generator.new(:myname) g.name.should == "myname" end it "should provide a method for declaring the source parameter" do @generator.from(:dos) end it "should always return a set source as a string" do @generator.from(:dos) @generator.source.should == "dos" end it "should return the source as nil if there is no source" do @generator.source.should be_nil end it "should return itself when declaring the source" do @generator.from(:dos).should equal(@generator) end it "should run the provided block when asked to generate the value" do @generator.with { "yayness" } @generator.generate.should == "yayness" end it "should pass in any provided value to the block" do @generator.with { |value| value.upcase } @generator.generate("myval").should == "MYVAL" end it "should return itself when declaring the code used for generating" do @generator.with { |value| value.upcase }.should equal(@generator) end end diff --git a/spec/unit/util/ldap/manager_spec.rb b/spec/unit/util/ldap/manager_spec.rb index 16c6b0601..551c699ae 100755 --- a/spec/unit/util/ldap/manager_spec.rb +++ b/spec/unit/util/ldap/manager_spec.rb @@ -1,650 +1,650 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/ldap/manager' # If the ldap classes aren't available, go ahead and # create some, so our tests will pass. unless defined?(LDAP::Mod) class LDAP LDAP_MOD_ADD = :adding LDAP_MOD_REPLACE = :replacing LDAP_MOD_DELETE = :deleting class ResultError < RuntimeError; end class Mod def initialize(*args) end end end end describe Puppet::Util::Ldap::Manager do before do @manager = Puppet::Util::Ldap::Manager.new end it "should return self when specifying objectclasses" do @manager.manages(:one, :two).should equal(@manager) end it "should allow specification of what objectclasses are managed" do @manager.manages(:one, :two).objectclasses.should == [:one, :two] end it "should return self when specifying the relative base" do @manager.at("yay").should equal(@manager) end it "should allow specification of the relative base" do @manager.at("yay").location.should == "yay" end it "should return self when specifying the attribute map" do @manager.maps(:one => :two).should equal(@manager) end it "should allow specification of the rdn attribute" do @manager.named_by(:uid).rdn.should == :uid end it "should allow specification of the attribute map" do @manager.maps(:one => :two).puppet2ldap.should == {:one => :two} end it "should have a no-op 'and' method that just returns self" do @manager.and.should equal(@manager) end it "should allow specification of generated attributes" do @manager.generates(:thing).should be_instance_of(Puppet::Util::Ldap::Generator) end describe "when generating attributes" do before do @generator = stub 'generator', :source => "one", :name => "myparam" Puppet::Util::Ldap::Generator.stubs(:new).with(:myparam).returns @generator end it "should create a generator to do the parameter generation" do Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns @generator @manager.generates(:myparam) end it "should return the generator from the :generates method" do @manager.generates(:myparam).should equal(@generator) end it "should not replace already present values" do @manager.generates(:myparam) attrs = {"myparam" => "testing"} @generator.expects(:generate).never @manager.generate attrs attrs["myparam"].should == "testing" end it "should look for the parameter as a string, not a symbol" do @manager.generates(:myparam) @generator.expects(:generate).with("yay").returns %w{double yay} attrs = {"one" => "yay"} @manager.generate attrs attrs["myparam"].should == %w{double yay} end it "should fail if a source is specified and no source value is not defined" do @manager.generates(:myparam) lambda { @manager.generate "two" => "yay" }.should raise_error(ArgumentError) end it "should use the source value to generate the new value if a source attribute is specified" do @manager.generates(:myparam) @generator.expects(:generate).with("yay").returns %w{double yay} @manager.generate "one" => "yay" end it "should not pass in any value if no source attribute is specified" do @generator.stubs(:source).returns nil @manager.generates(:myparam) @generator.expects(:generate).with.returns %w{double yay} @manager.generate "one" => "yay" end it "should convert any results to arrays of strings if necessary" do @generator.expects(:generate).returns :test @manager.generates(:myparam) attrs = {"one" => "two"} @manager.generate(attrs) attrs["myparam"].should == ["test"] end it "should add the result to the passed-in attribute hash" do @generator.expects(:generate).returns %w{test} @manager.generates(:myparam) attrs = {"one" => "two"} @manager.generate(attrs) attrs["myparam"].should == %w{test} end end it "should be considered invalid if it is missing a location" do @manager.manages :me @manager.maps :me => :you @manager.should_not be_valid end it "should be considered invalid if it is missing an objectclass list" do @manager.maps :me => :you @manager.at "ou=yayness" @manager.should_not be_valid end it "should be considered invalid if it is missing an attribute map" do @manager.manages :me @manager.at "ou=yayness" @manager.should_not be_valid end it "should be considered valid if it has an attribute map, location, and objectclass list" do @manager.maps :me => :you @manager.manages :me @manager.at "ou=yayness" @manager.should be_valid end it "should calculate an instance's dn using the :ldapbase setting and the relative base" do Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" @manager.at "ou=mybase" @manager.dn("me").should == "cn=me,ou=mybase,dc=testing" end it "should use the specified rdn when calculating an instance's dn" do Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" @manager.named_by :uid @manager.at "ou=mybase" @manager.dn("me").should =~ /^uid=me/ end it "should calculate its base using the :ldapbase setting and the relative base" do Puppet.settings.expects(:value).with(:ldapbase).returns "dc=testing" @manager.at "ou=mybase" @manager.base.should == "ou=mybase,dc=testing" end describe "when generating its search filter" do it "should using a single 'objectclass=' filter if a single objectclass is specified" do @manager.manages("testing") @manager.filter.should == "objectclass=testing" end it "should create an LDAP AND filter if multiple objectclasses are specified" do @manager.manages "testing", "okay", "done" @manager.filter.should == "(&(objectclass=testing)(objectclass=okay)(objectclass=done))" end end it "should have a method for converting a Puppet attribute name to an LDAP attribute name as a string" do @manager.maps :puppet_attr => :ldap_attr @manager.ldap_name(:puppet_attr).should == "ldap_attr" end it "should have a method for converting an LDAP attribute name to a Puppet attribute name" do @manager.maps :puppet_attr => :ldap_attr @manager.puppet_name(:ldap_attr).should == :puppet_attr end it "should have a :create method for creating ldap entries" do @manager.should respond_to(:create) end it "should have a :delete method for deleting ldap entries" do @manager.should respond_to(:delete) end it "should have a :modify method for modifying ldap entries" do @manager.should respond_to(:modify) end it "should have a method for finding an entry by name in ldap" do @manager.should respond_to(:find) end describe "when converting ldap entries to hashes for providers" do before do @manager.maps :uno => :one, :dos => :two @result = @manager.entry2provider("dn" => ["cn=one,ou=people,dc=madstop"], "one" => ["two"], "three" => %w{four}, "objectclass" => %w{yay ness}) end it "should set the name to the short portion of the dn" do @result[:name].should == "one" end it "should remove the objectclasses" do @result["objectclass"].should be_nil end it "should remove any attributes that are not mentioned in the map" do @result["three"].should be_nil end it "should rename convert to symbols all attributes to their puppet names" do @result[:uno].should == %w{two} end it "should set the value of all unset puppet attributes as :absent" do @result[:dos].should == :absent end end describe "when using an ldap connection" do before do @ldapconn = mock 'ldapconn' @conn = stub 'connection', :connection => @ldapconn, :start => nil, :close => nil Puppet::Util::Ldap::Connection.stubs(:new).returns(@conn) end it "should fail unless a block is given" do lambda { @manager.connect }.should raise_error(ArgumentError) end it "should open the connection with its server set to :ldapserver" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapserver).returns("myserver") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[0] == "myserver" }.returns @conn @manager.connect { |c| } end it "should open the connection with its port set to the :ldapport" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapport).returns("28") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[1] == "28" }.returns @conn @manager.connect { |c| } end it "should open the connection with no user if :ldapuser is not set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapuser).returns("") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user].nil? }.returns @conn @manager.connect { |c| } end it "should open the connection with its user set to the :ldapuser if it is set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapuser).returns("mypass") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:user] == "mypass" }.returns @conn @manager.connect { |c| } end it "should open the connection with no password if :ldappassword is not set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldappassword).returns("") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password].nil? }.returns @conn @manager.connect { |c| } end it "should open the connection with its password set to the :ldappassword if it is set" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldappassword).returns("mypass") Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:password] == "mypass" }.returns @conn @manager.connect { |c| } end it "should set ssl to :tls if ldaptls is enabled" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldaptls).returns(true) Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == :tls }.returns @conn @manager.connect { |c| } end it "should set ssl to true if ldapssl is enabled" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapssl).returns(true) Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == true }.returns @conn @manager.connect { |c| } end it "should set ssl to false if neither ldaptls nor ldapssl is enabled" do Puppet.settings.stubs(:value).returns(false) Puppet.settings.expects(:value).with(:ldapssl).returns(false) Puppet::Util::Ldap::Connection.expects(:new).with { |*args| args[2][:ssl] == false }.returns @conn @manager.connect { |c| } end it "should open, yield, and then close the connection" do @conn.expects(:start) @conn.expects(:close) Puppet::Util::Ldap::Connection.expects(:new).returns(@conn) @ldapconn.expects(:test) @manager.connect { |c| c.test } end it "should close the connection even if there's an exception in the passed block" do @conn.expects(:close) lambda { @manager.connect { |c| raise ArgumentError } }.should raise_error(ArgumentError) end end describe "when using ldap" do before do @conn = mock 'connection' @manager.stubs(:connect).yields @conn @manager.stubs(:objectclasses).returns [:oc1, :oc2] @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro end describe "to create entries" do it "should convert the first argument to its :create method to a full dn and pass the resulting argument list to its connection" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| name == "mydn" } @manager.create("myname", {"attr" => "myattrs"}) end it "should add the objectclasses to the attributes" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("oc1") and attrs["objectClass"].include?("oc2") } @manager.create("myname", {:one => :testing}) end it "should add the rdn to the attributes" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| attrs["cn"] == %w{myname} } @manager.create("myname", {:one => :testing}) end it "should add 'top' to the objectclasses if it is not listed" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:add).with { |name, attrs| attrs["objectClass"].include?("top") } @manager.create("myname", {:one => :testing}) end it "should add any generated values that are defined" do generator = stub 'generator', :source => :one, :name => "myparam" Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator @manager.generates(:myparam) @manager.stubs(:dn).with("myname").returns "mydn" generator.expects(:generate).with(:testing).returns ["generated value"] @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated value"] } @manager.create("myname", {:one => :testing}) end it "should convert any generated values to arrays of strings if necessary" do generator = stub 'generator', :source => :one, :name => "myparam" Puppet::Util::Ldap::Generator.expects(:new).with(:myparam).returns generator @manager.generates(:myparam) @manager.stubs(:dn).returns "mydn" generator.expects(:generate).returns :generated @conn.expects(:add).with { |name, attrs| attrs["myparam"] == ["generated"] } @manager.create("myname", {:one => :testing}) end end describe "do delete entries" do it "should convert the first argument to its :delete method to a full dn and pass the resulting argument list to its connection" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:delete).with("mydn") @manager.delete("myname") end end describe "to modify entries" do it "should convert the first argument to its :modify method to a full dn and pass the resulting argument list to its connection" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:modify).with("mydn", :mymods) @manager.modify("myname", :mymods) end end describe "to find a single entry" do it "should use the dn of the provided name as the search base, a scope of 0, and 'objectclass=*' as the filter for a search2 call" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:search2).with("mydn", 0, "objectclass=*") @manager.find("myname") end it "should return nil if an exception is thrown because no result is found" do @manager.expects(:dn).with("myname").returns "mydn" @conn.expects(:search2).raises LDAP::ResultError @manager.find("myname").should be_nil end it "should return a converted provider hash if the result is found" do @manager.expects(:dn).with("myname").returns "mydn" result = {"one" => "two"} @conn.expects(:search2).yields result @manager.expects(:entry2provider).with(result).returns "myprovider" @manager.find("myname").should == "myprovider" end end describe "to search for multiple entries" do before do @manager.stubs(:filter).returns "myfilter" end it "should use the manager's search base as the dn of the provided name as the search base" do @manager.expects(:base).returns "mybase" @conn.expects(:search2).with { |base, scope, filter| base == "mybase" } @manager.search end it "should use a scope of 1" do @conn.expects(:search2).with { |base, scope, filter| scope == 1 } @manager.search end it "should use any specified search filter" do @manager.expects(:filter).never @conn.expects(:search2).with { |base, scope, filter| filter == "boo" } @manager.search("boo") end it "should turn its objectclass list into its search filter if one is not specified" do @manager.expects(:filter).returns "yay" @conn.expects(:search2).with { |base, scope, filter| filter == "yay" } @manager.search end it "should return nil if no result is found" do @conn.expects(:search2) @manager.search.should be_nil end it "should return an array of the found results converted to provider hashes" do # LAK: AFAICT, it's impossible to yield multiple times in an expectation. one = {"dn" => "cn=one,dc=madstop,dc=com", "one" => "two"} @conn.expects(:search2).yields(one) @manager.expects(:entry2provider).with(one).returns "myprov" @manager.search.should == ["myprov"] end end end describe "when an instance" do before do @name = "myname" @manager.maps :one => :uno, :two => :dos, :three => :tres, :four => :quatro end describe "is being updated" do it "should get created if the current attribute list is empty and the desired attribute list has :ensure == :present" do @manager.expects(:create) @manager.update(@name, {}, {:ensure => :present}) end it "should get created if the current attribute list has :ensure == :absent and the desired attribute list has :ensure == :present" do @manager.expects(:create) @manager.update(@name, {:ensure => :absent}, {:ensure => :present}) end it "should get deleted if the current attribute list has :ensure == :present and the desired attribute list has :ensure == :absent" do @manager.expects(:delete) @manager.update(@name, {:ensure => :present}, {:ensure => :absent}) end it "should get modified if both attribute lists have :ensure == :present" do @manager.expects(:modify) @manager.update(@name, {:ensure => :present, :one => :two}, {:ensure => :present, :one => :three}) end end describe "is being deleted" do it "should call the :delete method with its name and manager" do @manager.expects(:delete).with(@name) @manager.update(@name, {}, {:ensure => :absent}) end end describe "is being created" do before do @is = {} @should = {:ensure => :present, :one => :yay, :two => :absent} end it "should call the :create method with its name" do @manager.expects(:create).with { |name, attrs| name == @name } @manager.update(@name, @is, @should) end it "should call the :create method with its property hash converted to ldap attribute names" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should convert the property names to strings" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should convert the property values to arrays if necessary" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should convert the property values to strings if necessary" do @manager.expects(:create).with { |name, attrs| attrs["uno"] == ["yay"] } @manager.update(@name, @is, @should) end it "should not include :ensure in the properties sent" do @manager.expects(:create).with { |*args| args[1][:ensure].nil? } @manager.update(@name, @is, @should) end it "should not include attributes set to :absent in the properties sent" do @manager.expects(:create).with { |*args| args[1][:dos].nil? } @manager.update(@name, @is, @should) end end describe "is being modified" do it "should call the :modify method with its name and an array of LDAP::Mod instances" do LDAP::Mod.stubs(:new).returns "whatever" @is = {:one => :yay} @should = {:one => :yay, :two => :foo} @manager.expects(:modify).with { |name, mods| name == @name } @manager.update(@name, @is, @should) end it "should create the LDAP::Mod with the property name converted to the ldap name as a string" do @is = {:one => :yay} @should = {:one => :yay, :two => :foo} mod = mock 'module' LDAP::Mod.expects(:new).with { |form, name, value| name == "dos" }.returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should create an LDAP::Mod instance of type LDAP_MOD_ADD for each attribute being added, with the attribute value converted to a string of arrays" do @is = {:one => :yay} @should = {:one => :yay, :two => :foo} mod = mock 'module' LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_ADD, "dos", ["foo"]).returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should create an LDAP::Mod instance of type LDAP_MOD_DELETE for each attribute being deleted" do @is = {:one => :yay, :two => :foo} @should = {:one => :yay, :two => :absent} mod = mock 'module' LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_DELETE, "dos", []).returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should create an LDAP::Mod instance of type LDAP_MOD_REPLACE for each attribute being modified, with the attribute converted to a string of arrays" do @is = {:one => :yay, :two => :four} @should = {:one => :yay, :two => :five} mod = mock 'module' LDAP::Mod.expects(:new).with(LDAP::LDAP_MOD_REPLACE, "dos", ["five"]).returns mod @manager.stubs(:modify) @manager.update(@name, @is, @should) end it "should pass all created Mod instances to the modify method" do @is = {:one => :yay, :two => :foo, :three => :absent} @should = {:one => :yay, :two => :foe, :three => :fee, :four => :fie} LDAP::Mod.expects(:new).times(3).returns("mod1").then.returns("mod2").then.returns("mod3") @manager.expects(:modify).with do |name, mods| mods.sort == %w{mod1 mod2 mod3}.sort end @manager.update(@name, @is, @should) end end end end diff --git a/spec/unit/util/loadedfile_spec.rb b/spec/unit/util/loadedfile_spec.rb index dcea53e45..9716a37d9 100755 --- a/spec/unit/util/loadedfile_spec.rb +++ b/spec/unit/util/loadedfile_spec.rb @@ -1,71 +1,71 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'tempfile' require 'puppet/util/loadedfile' describe Puppet::Util::LoadedFile do include PuppetSpec::Files before(:each) do @f = Tempfile.new('loadedfile_test') @f.puts "yayness" @f.flush @loaded = Puppet::Util::LoadedFile.new(@f.path) fake_ctime = Time.now - (2 * Puppet[:filetimeout]) @stat = stub('stat', :ctime => fake_ctime) @fake_now = Time.now + (2 * Puppet[:filetimeout]) end it "should accept files that don't exist" do nofile = tmpfile('testfile') File.exists?(nofile).should == false lambda{ Puppet::Util::LoadedFile.new(nofile) }.should_not raise_error end it "should recognize when the file has not changed" do # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should == false end it "should recognize when the file has changed" do # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() # just didn't do the job. File.stubs(:stat).returns(@stat) # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should be_an_instance_of(Time) end it "should not catch a change until the timeout has elapsed" do # Fake File.stat so we don't have to depend on the filesystem granularity. Doing a flush() # just didn't do the job. File.stubs(:stat).returns(@stat) @loaded.changed?.should be(false) # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should_not be(false) end it "should consider a file changed when that file is missing" do @f.close! # Use fake "now" so that we can be sure changed? actually checks, without sleeping # for Puppet[:filetimeout] seconds. Time.stubs(:now).returns(@fake_now) @loaded.changed?.should_not be(false) end it "should disable checking if Puppet[:filetimeout] is negative" do Puppet[:filetimeout] = -1 @loaded.changed?.should_not be(false) end after(:each) do @f.close end end diff --git a/spec/unit/util/lockfile_spec.rb b/spec/unit/util/lockfile_spec.rb index 5c3261fe3..8b799c308 100644 --- a/spec/unit/util/lockfile_spec.rb +++ b/spec/unit/util/lockfile_spec.rb @@ -1,76 +1,76 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/lockfile' describe Puppet::Util::Lockfile do require 'puppet_spec/files' include PuppetSpec::Files before(:each) do @lockfile = tmpfile("lock") @lock = Puppet::Util::Lockfile.new(@lockfile) end describe "#lock" do it "should return false if already locked" do @lock.stubs(:locked?).returns(true) @lock.lock.should be_false end it "should return true if it successfully locked" do @lock.lock.should be_true end it "should create a lock file" do @lock.lock File.should be_exists(@lockfile) end it "should create a lock file containing a string" do data = "foofoo barbar" @lock.lock(data) File.read(@lockfile).should == data end end describe "#unlock" do it "should return true when unlocking" do @lock.lock @lock.unlock.should be_true end it "should return false when not locked" do @lock.unlock.should be_false end it "should clear the lock file" do File.open(@lockfile, 'w') { |fd| fd.print("locked") } @lock.unlock File.should_not be_exists(@lockfile) end end it "should be locked when locked" do @lock.lock @lock.should be_locked end it "should not be locked when not locked" do @lock.should_not be_locked end it "should not be locked when unlocked" do @lock.lock @lock.unlock @lock.should_not be_locked end it "should return the lock data" do data = "foofoo barbar" @lock.lock(data) @lock.lock_data.should == data end -end \ No newline at end of file +end diff --git a/spec/unit/util/logging_spec.rb b/spec/unit/util/logging_spec.rb index 70e9d273d..eba1570e6 100755 --- a/spec/unit/util/logging_spec.rb +++ b/spec/unit/util/logging_spec.rb @@ -1,151 +1,151 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/logging' class LoggingTester include Puppet::Util::Logging end describe Puppet::Util::Logging do before do @logger = LoggingTester.new end Puppet::Util::Log.eachlevel do |level| it "should have a method for sending '#{level}' logs" do @logger.should respond_to(level) end end it "should have a method for sending a log with a specified log level" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" and args[:level] == "loglevel" and args[:message] == "mymessage" } @logger.send_log "loglevel", "mymessage" end describe "when sending a log" do it "should use the Log's 'create' entrance method" do Puppet::Util::Log.expects(:create) @logger.notice "foo" end it "should send itself converted to a string as the log source" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" } @logger.notice "foo" end it "should queue logs sent without a specified destination" do Puppet::Util::Log.close_all Puppet::Util::Log.expects(:queuemessage) @logger.notice "foo" end it "should use the path of any provided resource type" do resource = Puppet::Type.type(:host).new :name => "foo" resource.expects(:path).returns "/path/to/host".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/host" } resource.notice "foo" end it "should use the path of any provided resource parameter" do resource = Puppet::Type.type(:host).new :name => "foo" param = resource.parameter(:name) param.expects(:path).returns "/path/to/param".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/param" } param.notice "foo" end it "should send the provided argument as the log message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo" } @logger.notice "foo" end it "should join any provided arguments into a single string for the message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo bar baz" } @logger.notice ["foo", "bar", "baz"] end [:file, :line, :tags].each do |attr| it "should include #{attr} if available" do @logger.singleton_class.send(:attr_accessor, attr) @logger.send(attr.to_s + "=", "myval") Puppet::Util::Log.expects(:create).with { |args| args[attr] == "myval" } @logger.notice "foo" end end end describe "when sending a deprecation warning" do it "should log the message with warn" do @logger.expects(:warning).with do |msg| msg =~ /^foo\n/ end @logger.deprecation_warning 'foo' end it "should only log each offending line once" do @logger.expects(:warning).with do |msg| msg =~ /^foo\n/ end .once 5.times { @logger.deprecation_warning 'foo' } end it "should only log the first 100 messages" do (1..100).each { |i| @logger.expects(:warning).with do |msg| msg =~ /^#{i}\n/ end .once # since the deprecation warning will only log each offending line once, we have to do some tomfoolery # here in order to make it think each of these calls is coming from a unique call stack; we're basically # mocking the method that it would normally use to find the call stack. @logger.expects(:get_deprecation_offender).returns("deprecation log count test ##{i}") @logger.deprecation_warning i } @logger.expects(:warning).with(101).never @logger.deprecation_warning 101 end end describe "when formatting exceptions" do it "should be able to format a chain of exceptions" do exc3 = Puppet::Error.new("original") exc3.set_backtrace(["1.rb:4:in `a'","2.rb:2:in `b'","3.rb:1"]) exc2 = Puppet::Error.new("second", exc3) exc2.set_backtrace(["4.rb:8:in `c'","5.rb:1:in `d'","6.rb:3"]) exc1 = Puppet::Error.new("third", exc2) exc1.set_backtrace(["7.rb:31:in `e'","8.rb:22:in `f'","9.rb:9"]) # whoa ugly @logger.format_exception(exc1).should =~ /third .*7\.rb:31 .*8\.rb:22 .*9\.rb:9 Wrapped exception: second .*4\.rb:8 .*5\.rb:1 .*6\.rb:3 Wrapped exception: original .*1\.rb:4 .*2\.rb:2 .*3\.rb:1/ end end end diff --git a/spec/unit/util/metric_spec.rb b/spec/unit/util/metric_spec.rb index 07a9e4945..00b0b988f 100755 --- a/spec/unit/util/metric_spec.rb +++ b/spec/unit/util/metric_spec.rb @@ -1,94 +1,94 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/metric' describe Puppet::Util::Metric do before do @metric = Puppet::Util::Metric.new("foo") end it "should be aliased to Puppet::Metric" do Puppet::Util::Metric.should equal(Puppet::Metric) end [:type, :name, :value, :label, :basedir].each do |name| it "should have a #{name} attribute" do @metric.should respond_to(name) @metric.should respond_to(name.to_s + "=") end end it "should default to the :rrdir as the basedir "do Puppet.settings.expects(:value).with(:rrddir).returns "myrrd" @metric.basedir.should == "myrrd" end it "should use any provided basedir" do @metric.basedir = "foo" @metric.basedir.should == "foo" end it "should require a name at initialization" do lambda { Puppet::Util::Metric.new }.should raise_error(ArgumentError) end it "should always convert its name to a string" do Puppet::Util::Metric.new(:foo).name.should == "foo" end it "should support a label" do Puppet::Util::Metric.new("foo", "mylabel").label.should == "mylabel" end it "should autogenerate a label if none is provided" do Puppet::Util::Metric.new("foo_bar").label.should == "Foo bar" end it "should have a method for adding values" do @metric.should respond_to(:newvalue) end it "should have a method for returning values" do @metric.should respond_to(:values) end it "should require a name and value for its values" do lambda { @metric.newvalue }.should raise_error(ArgumentError) end it "should support a label for values" do @metric.newvalue("foo", 10, "label") @metric.values[0][1].should == "label" end it "should autogenerate value labels if none is provided" do @metric.newvalue("foo_bar", 10) @metric.values[0][1].should == "Foo bar" end it "should return its values sorted by label" do @metric.newvalue("foo", 10, "b") @metric.newvalue("bar", 10, "a") @metric.values.should == [["bar", "a", 10], ["foo", "b", 10]] end it "should use an array indexer method to retrieve individual values" do @metric.newvalue("foo", 10) @metric["foo"].should == 10 end it "should return nil if the named value cannot be found" do @metric["foo"].should == 0 end # LAK: I'm not taking the time to develop these tests right now. # I expect they should actually be extracted into a separate class # anyway. it "should be able to graph metrics using RRDTool" it "should be able to create a new RRDTool database" it "should be able to store metrics into an RRDTool database" end diff --git a/spec/unit/util/monkey_patches/lines_spec.rb b/spec/unit/util/monkey_patches/lines_spec.rb index 1ea379182..0bbcf2672 100755 --- a/spec/unit/util/monkey_patches/lines_spec.rb +++ b/spec/unit/util/monkey_patches/lines_spec.rb @@ -1,83 +1,83 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/monkey_patches/lines' class Puppet::Util::MonkeyPatches::Lines::TestHelper < String include Puppet::Util::MonkeyPatches::Lines end describe Puppet::Util::MonkeyPatches::Lines::TestHelper do ["\n", "\n\n", "$", "$$", "@", "@@"].each do |sep| context "with #{sep.inspect}" do it "should delegate to each_line if given a block" do subject.expects(:each_line).with(sep) subject.lines(sep) do |x| nil end end it "should delegate to each_line without a block" do subject.expects(:enum_for).with(:each_line, sep) subject.lines(sep) end context "with one line" do context "with trailing separator" do subject { described_class.new("foo" + sep) } it "should yield the string" do got = [] subject.lines(sep) do |x| got << x end got.should == ["foo#{sep}"] end it "should return the string" do subject.lines(sep).to_a.should == ["foo#{sep}"] end end context "without trailing separator" do subject { described_class.new("foo") } it "should yield the string" do got = [] subject.lines(sep) do |x| got << x end got.should == ["foo"] end it "should return the string" do subject.lines(sep).to_a.should == ["foo"] end end end context "with multiple lines" do context "with trailing separator" do subject { described_class.new("foo#{sep}bar#{sep}baz#{sep}") } it "should yield the strings" do got = [] subject.lines(sep) do |x| got << x end got.should == ["foo#{sep}", "bar#{sep}", "baz#{sep}"] end it "should return the strings" do subject.lines(sep).to_a.should == ["foo#{sep}", "bar#{sep}", "baz#{sep}"] end end context "without trailing separator" do subject { described_class.new("foo#{sep}bar#{sep}baz") } it "should yield the strings" do got = [] subject.lines(sep) do |x| got << x end got.should == ["foo#{sep}", "bar#{sep}", "baz"] end it "should return the strings" do subject.lines(sep).to_a.should == ["foo#{sep}", "bar#{sep}", "baz"] end end end end end end diff --git a/spec/unit/util/monkey_patches_spec.rb b/spec/unit/util/monkey_patches_spec.rb index cb0f37f0a..b38448c8a 100755 --- a/spec/unit/util/monkey_patches_spec.rb +++ b/spec/unit/util/monkey_patches_spec.rb @@ -1,249 +1,249 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/monkey_patches' describe "yaml deserialization" do it "should call yaml_initialize when deserializing objects that have that method defined" do class Puppet::TestYamlInitializeClass attr_reader :foo def yaml_initialize(tag, var) var.should == {'foo' => 100} instance_variables.should == [] @foo = 200 end end obj = YAML.load("--- !ruby/object:Puppet::TestYamlInitializeClass\n foo: 100") obj.foo.should == 200 end it "should not call yaml_initialize if not defined" do class Puppet::TestYamlNonInitializeClass attr_reader :foo end obj = YAML.load("--- !ruby/object:Puppet::TestYamlNonInitializeClass\n foo: 100") obj.foo.should == 100 end end # In Ruby > 1.8.7 this is a builtin, otherwise we monkey patch the method in describe Array do describe "#combination" do it "should fail if wrong number of arguments given" do expect { [1,2,3].combination() }.to raise_error(ArgumentError, /wrong number/) expect { [1,2,3].combination(1,2) }.to raise_error(ArgumentError, /wrong number/) end it "should return an empty array if combo size than array size or negative" do [1,2,3].combination(4).to_a.should == [] [1,2,3].combination(-1).to_a.should == [] end it "should return an empty array with an empty array if combo size == 0" do [1,2,3].combination(0).to_a.should == [[]] end it "should all provide all combinations of size passed in" do [1,2,3,4].combination(1).to_a.should == [[1], [2], [3], [4]] [1,2,3,4].combination(2).to_a.should == [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]] [1,2,3,4].combination(3).to_a.should == [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] end end describe "#count" do it "should equal length" do [].count.should == [].length [1].count.should == [1].length end end describe "#drop" do it "should raise if asked to drop less than zero items" do expect { [].drop(-1) }.to raise_error ArgumentError end it "should return the array when drop 0" do [].drop(0).should == [] [1].drop(0).should == [1] [1,2].drop(0).should == [1,2] end it "should return an empty array when dropping more items than the array" do (1..10).each do |n| [].drop(n).should == [] [1].drop(n).should == [] end end it "should drop the right number of items" do [1,2,3].drop(0).should == [1,2,3] [1,2,3].drop(1).should == [2,3] [1,2,3].drop(2).should == [3] [1,2,3].drop(3).should == [] end end end describe IO do include PuppetSpec::Files let(:file) { tmpfile('io-binary') } let(:content) { "\x01\x02\x03\x04" } describe "::binread" do it "should read in binary mode" do File.open(file, 'wb') {|f| f.write(content) } IO.binread(file).should == content end it "should read with a length and offset" do offset = 1 length = 2 File.open(file, 'wb') {|f| f.write(content) } IO.binread(file, length, offset).should == content[offset..length] end it "should raise an error if the file doesn't exist" do expect { IO.binread('/path/does/not/exist') }.to raise_error(Errno::ENOENT) end end describe "::binwrite" do it "should write in binary mode" do IO.binwrite(file, content).should == content.length File.open(file, 'rb') {|f| f.read.should == content } end (0..10).each do |offset| it "should write correctly using an offset of #{offset}" do IO.binwrite(file, content, offset).should == content.length File.open(file, 'rb') {|f| f.read.should == ("\x00" * offset) + content } end end context "truncation" do let :input do "welcome to paradise, population ... YOU!" end before :each do IO.binwrite(file, input) end it "should truncate if no offset is given" do IO.binwrite(file, "boo").should == 3 File.read(file).should == "boo" end (0..10).each do |offset| it "should not truncate if an offset of #{offset} is given" do expect = input.dup expect[offset, 3] = "BAM" IO.binwrite(file, "BAM", offset).should == 3 File.read(file).should == expect end end it "should pad with NULL bytes if writing past EOF without truncate" do expect = input + ("\x00" * 4) + "BAM" IO.binwrite(file, "BAM", input.length + 4).should == 3 File.read(file).should == expect end end it "should raise an error if the directory containing the file doesn't exist" do expect { IO.binwrite('/path/does/not/exist', 'foo') }.to raise_error(Errno::ENOENT) end end end describe Range do def do_test( range, other, expected ) result = range.intersection(other) result.should == expected end it "should return expected ranges for iterable things" do iterable_tests = { 1 .. 4 => nil, # before 11 .. 15 => nil, # after 1 .. 6 => 5 .. 6, # overlap_begin 9 .. 15 => 9 .. 10, # overlap_end 1 .. 5 => 5 .. 5, # overlap_begin_edge 10 .. 15 => 10 .. 10, # overlap_end_edge 5 .. 10 => 5 .. 10, # overlap_all 6 .. 9 => 6 .. 9, # overlap_inner 1 ... 5 => nil, # before (exclusive range) 1 ... 7 => 5 ... 7, # overlap_begin (exclusive range) 1 ... 6 => 5 ... 6, # overlap_begin_edge (exclusive range) 5 ... 11 => 5 .. 10, # overlap_all (exclusive range) 6 ... 10 => 6 ... 10, # overlap_inner (exclusive range) } iterable_tests.each do |other, expected| do_test( 5..10, other, expected ) do_test( other, 5..10, expected ) end end it "should return expected ranges for noniterable things" do inclusive_base_case = { 1.to_f .. 4.to_f => nil, # before 11.to_f .. 15.to_f => nil, # after 1.to_f .. 6.to_f => 5.to_f .. 6.to_f, # overlap_begin 9.to_f .. 15.to_f => 9.to_f .. 10.to_f, # overlap_end 1.to_f .. 5.to_f => 5.to_f .. 5.to_f, # overlap_begin_edge 10.to_f .. 15.to_f => 10.to_f .. 10.to_f, # overlap_end_edge 5.to_f .. 10.to_f => 5.to_f .. 10.to_f, # overlap_all 6.to_f .. 9.to_f => 6.to_f .. 9.to_f, # overlap_inner 1.to_f ... 5.to_f => nil, # before (exclusive range) 1.to_f ... 7.to_f => 5.to_f ... 7.to_f, # overlap_begin (exclusive range) 1.to_f ... 6.to_f => 5.to_f ... 6.to_f, # overlap_begin_edge (exclusive range) 5.to_f ... 11.to_f => 5.to_f .. 10.to_f, # overlap_all (exclusive range) 6.to_f ... 10.to_f => 6.to_f ... 10.to_f, # overlap_inner (exclusive range) } inclusive_base_case.each do |other, expected| do_test( 5.to_f..10.to_f, other, expected ) do_test( other, 5.to_f..10.to_f, expected ) end exclusive_base_case = { 1.to_f .. 4.to_f => nil, # before 11.to_f .. 15.to_f => nil, # after 1.to_f .. 6.to_f => 5.to_f .. 6.to_f, # overlap_begin 9.to_f .. 15.to_f => 9.to_f ... 10.to_f, # overlap_end 1.to_f .. 5.to_f => 5.to_f .. 5.to_f, # overlap_begin_edge 10.to_f .. 15.to_f => nil, # overlap_end_edge 5.to_f .. 10.to_f => 5.to_f ... 10.to_f, # overlap_all 6.to_f .. 9.to_f => 6.to_f .. 9.to_f, # overlap_inner 1.to_f ... 5.to_f => nil, # before (exclusive range) 1.to_f ... 7.to_f => 5.to_f ... 7.to_f, # overlap_begin (exclusive range) 1.to_f ... 6.to_f => 5.to_f ... 6.to_f, # overlap_begin_edge (exclusive range) 5.to_f ... 11.to_f => 5.to_f ... 10.to_f, # overlap_all (exclusive range) 6.to_f ... 10.to_f => 6.to_f ... 10.to_f, # overlap_inner (exclusive range) } exclusive_base_case.each do |other, expected| do_test( 5.to_f...10.to_f, other, expected ) do_test( other, 5.to_f...10.to_f, expected ) end end end describe Object, "#instance_variables" do it "should work with no instance variables" do Object.new.instance_variables.should == [] end it "should return symbols, not strings" do o = Object.new ["@foo", "@bar", "@baz"].map {|x| o.instance_variable_set(x, x) } o.instance_variables.should =~ [:@foo, :@bar, :@baz] end end diff --git a/spec/unit/util/nagios_maker_spec.rb b/spec/unit/util/nagios_maker_spec.rb index 9cd038ff3..b4fe0fbd0 100755 --- a/spec/unit/util/nagios_maker_spec.rb +++ b/spec/unit/util/nagios_maker_spec.rb @@ -1,122 +1,122 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/nagios_maker' describe Puppet::Util::NagiosMaker do before do @module = Puppet::Util::NagiosMaker @nagtype = stub 'nagios type', :parameters => [], :namevar => :name Nagios::Base.stubs(:type).with(:test).returns(@nagtype) @provider = stub 'provider', :nagios_type => nil @type = stub 'type', :newparam => nil, :newproperty => nil, :provide => @provider, :desc => nil, :ensurable => nil end it "should be able to create a new nagios type" do @module.should respond_to(:create_nagios_type) end it "should fail if it cannot find the named Naginator type" do Nagios::Base.stubs(:type).returns(nil) lambda { @module.create_nagios_type(:no_such_type) }.should raise_error(Puppet::DevError) end it "should create a new RAL type with the provided name prefixed with 'nagios_'" do Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should mark the created type as ensurable" do @type.expects(:ensurable) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should create a namevar parameter for the nagios type's name parameter" do @type.expects(:newparam).with(:name, :namevar => true) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should create a property for all non-namevar parameters" do @nagtype.stubs(:parameters).returns([:one, :two]) @type.expects(:newproperty).with(:one) @type.expects(:newproperty).with(:two) @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should skip parameters that start with integers" do @nagtype.stubs(:parameters).returns(["2dcoords".to_sym, :other]) @type.expects(:newproperty).with(:other) @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should deduplicate the parameter list" do @nagtype.stubs(:parameters).returns([:one, :one]) @type.expects(:newproperty).with(:one) @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end it "should create a target property" do @type.expects(:newproperty).with(:target) Puppet::Type.expects(:newtype).with(:nagios_test).returns(@type) @module.create_nagios_type(:test) end end describe Puppet::Util::NagiosMaker, " when creating the naginator provider" do before do @module = Puppet::Util::NagiosMaker @provider = stub 'provider', :nagios_type => nil @nagtype = stub 'nagios type', :parameters => [], :namevar => :name Nagios::Base.stubs(:type).with(:test).returns(@nagtype) @type = stub 'type', :newparam => nil, :ensurable => nil, :newproperty => nil, :desc => nil Puppet::Type.stubs(:newtype).with(:nagios_test).returns(@type) end it "should add a naginator provider" do @type.expects(:provide).with { |name, options| name == :naginator }.returns @provider @module.create_nagios_type(:test) end it "should set Puppet::Provider::Naginator as the parent class of the provider" do @type.expects(:provide).with { |name, options| options[:parent] == Puppet::Provider::Naginator }.returns @provider @module.create_nagios_type(:test) end it "should use /etc/nagios/$name.cfg as the default target" do @type.expects(:provide).with { |name, options| options[:default_target] == "/etc/nagios/nagios_test.cfg" }.returns @provider @module.create_nagios_type(:test) end it "should trigger the lookup of the Nagios class" do @type.expects(:provide).returns @provider @provider.expects(:nagios_type) @module.create_nagios_type(:test) end end diff --git a/spec/unit/util/network_device/cisco/device_spec.rb b/spec/unit/util/network_device/cisco/device_spec.rb index cb2d161e3..489a2eeb2 100755 --- a/spec/unit/util/network_device/cisco/device_spec.rb +++ b/spec/unit/util/network_device/cisco/device_spec.rb @@ -1,408 +1,408 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device/cisco/device' describe Puppet::Util::NetworkDevice::Cisco::Device do before(:each) do @transport = stub_everything 'transport', :is_a? => true, :command => "" @cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/") @cisco.transport = @transport end describe "when creating the device" do it "should find the enable password from the url" do cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password") cisco.enable_password.should == "enable_password" end it "should find the enable password from the options" do cisco = Puppet::Util::NetworkDevice::Cisco::Device.new("telnet://user:password@localhost:23/?enable=enable_password", :enable_password => "mypass") cisco.enable_password.should == "mypass" end end describe "when connecting to the physical device" do it "should connect to the transport" do @transport.expects(:connect) @cisco.command end it "should attempt to login" do @cisco.expects(:login) @cisco.command end it "should tell the device to not page" do @transport.expects(:command).with("terminal length 0") @cisco.command end it "should enter the enable password if returned prompt is not privileged" do @transport.stubs(:command).yields("Switch>").returns("") @cisco.expects(:enable) @cisco.command end it "should find device capabilities" do @cisco.expects(:find_capabilities) @cisco.command end it "should execute given command" do @transport.expects(:command).with("mycommand") @cisco.command("mycommand") end it "should yield to the command block if one is provided" do @transport.expects(:command).with("mycommand") @cisco.command do |c| c.command("mycommand") end end it "should close the device transport" do @transport.expects(:close) @cisco.command end describe "when login in" do it "should not login if transport handles login" do @transport.expects(:handles_login?).returns(true) @transport.expects(:command).never @transport.expects(:expect).never @cisco.login end it "should send username if one has been provided" do @transport.expects(:command).with("user", :prompt => /^Password:/) @cisco.login end it "should send password after the username" do @transport.expects(:command).with("user", :prompt => /^Password:/) @transport.expects(:command).with("password") @cisco.login end it "should expect the Password: prompt if no user was sent" do @cisco.url.user = '' @transport.expects(:expect).with(/^Password:/) @transport.expects(:command).with("password") @cisco.login end end describe "when entering enable password" do it "should raise an error if no enable password has been set" do @cisco.enable_password = nil lambda{ @cisco.enable }.should raise_error end it "should send the enable command and expect an enable prompt" do @cisco.enable_password = 'mypass' @transport.expects(:command).with("enable", :prompt => /^Password:/) @cisco.enable end it "should send the enable password" do @cisco.enable_password = 'mypass' @transport.stubs(:command).with("enable", :prompt => /^Password:/) @transport.expects(:command).with("mypass") @cisco.enable end end end describe "when finding network device capabilities" do it "should try to execute sh vlan brief" do @transport.expects(:command).with("sh vlan brief").returns("") @cisco.find_capabilities end it "should detect errors" do @transport.stubs(:command).with("sh vlan brief").returns(< "FastEthernet0/1", "Fa0/1" => "FastEthernet0/1", "FastEth 0/1" => "FastEthernet0/1", "Gi1" => "GigabitEthernet1", "Te2" => "TenGigabitEthernet2", "Di9" => "Dialer9", "Ethernet 0/0/1" => "Ethernet0/0/1", "E0" => "Ethernet0", "ATM 0/1.1" => "ATM0/1.1", "VLAN99" => "VLAN99" }.each do |input,expected| it "should canonicalize #{input} to #{expected}" do @cisco.canonalize_ifname(input).should == expected end end describe "when updating device vlans" do describe "when removing a vlan" do it "should issue the no vlan command" do @transport.expects(:command).with("no vlan 200") @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :absent}) end end describe "when updating a vlan" do it "should issue the vlan command to enter global vlan modifications" do @transport.expects(:command).with("vlan 200") @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200"}) end it "should issue the name command to modify the vlan description" do @transport.expects(:command).with("name myvlan") @cisco.update_vlan("200", {:ensure => :present, :name => "200"}, { :ensure=> :present, :name => "200", :description => "myvlan"}) end end end describe "when parsing interface" do it "should parse interface output" do @cisco.expects(:parse_interface).returns({ :ensure => :present }) @cisco.interface("FastEthernet0/1").should == { :ensure => :present } end it "should parse trunking and merge results" do @cisco.stubs(:parse_interface).returns({ :ensure => :present }) @cisco.expects(:parse_trunking).returns({ :native_vlan => "100" }) @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :native_vlan => "100" } end it "should return an absent interface if parse_interface returns nothing" do @cisco.stubs(:parse_interface).returns({}) @cisco.interface("FastEthernet0/1").should == { :ensure => :absent } end it "should parse ip address information and merge results" do @cisco.stubs(:parse_interface).returns({ :ensure => :present }) @cisco.expects(:parse_interface_config).returns({ :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] }) @cisco.interface("FastEthernet0/1").should == { :ensure => :present, :ipaddress => [24,IPAddr.new('192.168.0.24'), nil] } end it "should parse the sh interface command" do @transport.stubs(:command).with("sh interface FastEthernet0/1").returns(< :absent, :duplex => :auto, :speed => :auto } end it "should be able to parse the sh vlan brief command output" do @cisco.stubs(:support_vlan_brief?).returns(true) @transport.stubs(:command).with("sh vlan brief").returns(<{:status=>"active", :interfaces=>["FastEthernet0/1", "FastEthernet0/2"], :description=>"management", :name=>"100"}, "1"=>{:status=>"active", :interfaces=>["FastEthernet0/3", "FastEthernet0/4", "FastEthernet0/5", "FastEthernet0/6", "FastEthernet0/7", "FastEthernet0/8", "FastEthernet0/9", "FastEthernet0/10", "FastEthernet0/11", "FastEthernet0/12", "FastEthernet0/13", "FastEthernet0/14", "FastEthernet0/15", "FastEthernet0/16", "FastEthernet0/17", "FastEthernet0/18", "FastEthernet0/23", "FastEthernet0/24"], :description=>"default", :name=>"1"}, "10"=>{:status=>"active", :interfaces=>[], :description=>"VLAN0010", :name=>"10"}} end it "should parse trunk switchport information" do @transport.stubs(:command).with("sh interface FastEthernet0/21 switchport").returns(< :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>:all, } end it "should parse trunk switchport information with allowed vlans" do @transport.stubs(:command).with("sh interface GigabitEthernet 0/1 switchport").returns(< :trunk, :encapsulation => :dot1q, :allowed_trunk_vlans=>"1,99", } end it "should parse access switchport information" do @transport.stubs(:command).with("sh interface FastEthernet0/1 switchport").returns(< :access, :native_vlan => "100" } end it "should parse ip addresses" do @transport.stubs(:command).with("sh running-config interface Vlan 1 | begin interface").returns(<[[24, IPAddr.new('192.168.0.24'), 'secondary'], [24, IPAddr.new('192.168.0.1'), nil], [64, IPAddr.new('2001:07a8:71c1::'), "eui-64"]]} end it "should parse etherchannel membership" do @transport.stubs(:command).with("sh running-config interface Gi0/17 | begin interface").returns(<"1"} end end describe "when finding device facts" do it "should delegate to the cisco facts entity" do facts = stub 'facts' Puppet::Util::NetworkDevice::Cisco::Facts.expects(:new).returns(facts) facts.expects(:retrieve).returns(:facts) @cisco.facts.should == :facts end end end diff --git a/spec/unit/util/network_device/cisco/facts_spec.rb b/spec/unit/util/network_device/cisco/facts_spec.rb index 7d1eb8060..2e1fb841b 100755 --- a/spec/unit/util/network_device/cisco/facts_spec.rb +++ b/spec/unit/util/network_device/cisco/facts_spec.rb @@ -1,64 +1,64 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device' require 'puppet/util/network_device/cisco/facts' describe Puppet::Util::NetworkDevice::Cisco::Facts do before(:each) do @transport = stub_everything 'transport' @facts = Puppet::Util::NetworkDevice::Cisco::Facts.new(@transport) end { "cisco WS-C2924C-XL (PowerPC403GA) processor (revision 0x11) with 8192K/1024K bytes of memory." => {:hardwaremodel => "WS-C2924C-XL", :memorysize => "8192K", :processor => "PowerPC403GA", :hardwarerevision => "0x11" }, "Cisco 1841 (revision 5.0) with 355328K/37888K bytes of memory." => {:hardwaremodel=>"1841", :memorysize => "355328K", :hardwarerevision => "5.0" }, "Cisco 877 (MPC8272) processor (revision 0x200) with 118784K/12288K bytes of memory." => {:hardwaremodel=>"877", :memorysize => "118784K", :processor => "MPC8272", :hardwarerevision => "0x200" }, "cisco WS-C2960G-48TC-L (PowerPC405) processor (revision C0) with 61440K/4088K bytes of memory." => {:hardwaremodel=>"WS-C2960G-48TC-L", :memorysize => "61440K", :processor => "PowerPC405", :hardwarerevision => "C0" }, "cisco WS-C2950T-24 (RC32300) processor (revision R0) with 19959K bytes of memory." => {:hardwaremodel=>"WS-C2950T-24", :memorysize => "19959K", :processor => "RC32300", :hardwarerevision => "R0" } }.each do |ver, expected| it "should parse show ver output for hardware device facts" do @transport.stubs(:command).with("sh ver").returns(<sh ver #{ver} Switch> eos @facts.parse_show_ver.should == expected end end { "Switch uptime is 1 year, 12 weeks, 6 days, 22 hours, 32 minutes" => { :hostname => "Switch", :uptime => "1 year, 12 weeks, 6 days, 22 hours, 32 minutes", :uptime_seconds => 39393120, :uptime_days => 455 }, "c2950 uptime is 3 weeks, 1 day, 23 hours, 36 minutes" => { :hostname => "c2950", :uptime => "3 weeks, 1 day, 23 hours, 36 minutes", :uptime_days => 22, :uptime_seconds => 1985760}, "router uptime is 5 weeks, 1 day, 3 hours, 30 minutes" => { :hostname => "router", :uptime => "5 weeks, 1 day, 3 hours, 30 minutes", :uptime_days => 36, :uptime_seconds => 3123000 }, "c2950 uptime is 1 minute" => { :hostname => "c2950", :uptime => "1 minute", :uptime_days => 0, :uptime_seconds => 60 }, "c2950 uptime is 20 weeks, 6 minutes" => { :hostname => "c2950", :uptime=>"20 weeks, 6 minutes", :uptime_seconds=>12096360, :uptime_days=>140 }, "c2950 uptime is 2 years, 20 weeks, 6 minutes" => { :hostname => "c2950", :uptime=>"2 years, 20 weeks, 6 minutes", :uptime_seconds=>75168360, :uptime_days=>870 } }.each do |ver, expected| it "should parse show ver output for device uptime facts" do @transport.stubs(:command).with("sh ver").returns(<sh ver #{ver} Switch> eos @facts.parse_show_ver.should == expected end end { "IOS (tm) C2900XL Software (C2900XL-C3H2S-M), Version 12.0(5)WC10, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.0(5)WC10", :operatingsystemmajrelease => "12.0WC", :operatingsystemfeature => "C3H2S"}, "IOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA8a, RELEASE SOFTWARE (fc1)"=> { :operatingsystem => "IOS", :operatingsystemrelease => "12.1(22)EA8a", :operatingsystemmajrelease => "12.1EA", :operatingsystemfeature => "I6K2L2Q4"}, "Cisco IOS Software, C2960 Software (C2960-LANBASEK9-M), Version 12.2(44)SE, RELEASE SOFTWARE (fc1)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.2(44)SE", :operatingsystemmajrelease => "12.2SE", :operatingsystemfeature => "LANBASEK9"}, "Cisco IOS Software, C870 Software (C870-ADVIPSERVICESK9-M), Version 12.4(11)XJ4, RELEASE SOFTWARE (fc2)"=>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(11)XJ4", :operatingsystemmajrelease => "12.4XJ", :operatingsystemfeature => "ADVIPSERVICESK9"}, "Cisco IOS Software, 1841 Software (C1841-ADVSECURITYK9-M), Version 12.4(24)T4, RELEASE SOFTWARE (fc2)" =>{ :operatingsystem => "IOS", :operatingsystemrelease => "12.4(24)T4", :operatingsystemmajrelease => "12.4T", :operatingsystemfeature => "ADVSECURITYK9"}, }.each do |ver, expected| it "should parse show ver output for device software version facts" do @transport.stubs(:command).with("sh ver").returns(<sh ver #{ver} Switch> eos @facts.parse_show_ver.should == expected end end end diff --git a/spec/unit/util/network_device/cisco/interface_spec.rb b/spec/unit/util/network_device/cisco/interface_spec.rb index b0561c6fd..be05b412c 100755 --- a/spec/unit/util/network_device/cisco/interface_spec.rb +++ b/spec/unit/util/network_device/cisco/interface_spec.rb @@ -1,88 +1,88 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device' require 'puppet/util/network_device/cisco/interface' describe Puppet::Util::NetworkDevice::Cisco::Interface do before(:each) do @transport = stub_everything 'transport' @interface = Puppet::Util::NetworkDevice::Cisco::Interface.new("FastEthernet0/1",@transport) end it "should include IPCalc" do @interface.class.include?(Puppet::Util::NetworkDevice::IPCalc) end describe "when updating the physical device" do it "should enter global configuration mode" do @transport.expects(:command).with("conf t") @interface.update end it "should enter interface configuration mode" do @transport.expects(:command).with("interface FastEthernet0/1") @interface.update end it "should 'execute' all differing properties" do @interface.expects(:execute).with(:description, "b") @interface.expects(:execute).with(:mode, :access).never @interface.update({ :description => "a", :mode => :access }, { :description => "b", :mode => :access }) end it "should execute in cisco ios defined order" do speed = states('speed').starts_as('notset') @interface.expects(:execute).with(:speed, :auto).then(speed.is('set')) @interface.expects(:execute).with(:duplex, :auto).when(speed.is('set')) @interface.update({ :duplex => :half, :speed => "10" }, { :duplex => :auto, :speed => :auto }) end it "should execute absent properties with a no prefix" do @interface.expects(:execute).with(:description, "a", "no ") @interface.update({ :description => "a"}, { }) end it "should exit twice" do @transport.expects(:command).with("exit").twice @interface.update end end describe "when executing commands" do it "should execute string commands directly" do @transport.expects(:command).with("speed auto") @interface.execute(:speed, :auto) end it "should execute string commands with the given prefix" do @transport.expects(:command).with("no speed auto") @interface.execute(:speed, :auto, "no ") end it "should stop at executing the first command that works for array" do @transport.expects(:command).with("channel-group 1").yields("% Invalid command") @transport.expects(:command).with("port group 1") @interface.execute(:etherchannel, "1") end it "should execute the block for block commands" do @transport.expects(:command).with("ip address 192.168.0.1 255.255.255.0") @interface.execute(:ipaddress, [[24, IPAddr.new('192.168.0.1'), nil]]) end it "should execute the block for block commands" do @transport.expects(:command).with("ipv6 address fe08::/76 link-local") @interface.execute(:ipaddress, [[76, IPAddr.new('fe08::'), 'link-local']]) end end describe "when sending commands to the device" do it "should detect errors" do Puppet.expects(:err) @transport.stubs(:command).yields("% Invalid Command") @interface.command("sh ver") end end end diff --git a/spec/unit/util/network_device/config_spec.rb b/spec/unit/util/network_device/config_spec.rb index 8b246777d..f7e349b47 100755 --- a/spec/unit/util/network_device/config_spec.rb +++ b/spec/unit/util/network_device/config_spec.rb @@ -1,110 +1,110 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device/config' describe Puppet::Util::NetworkDevice::Config do include PuppetSpec::Files before(:each) do Puppet[:deviceconfig] = make_absolute("/dummy") FileTest.stubs(:exists?).with(make_absolute("/dummy")).returns(true) end describe "when initializing" do before :each do Puppet::Util::NetworkDevice::Config.any_instance.stubs(:read) end it "should use the deviceconfig setting as pathname" do Puppet.expects(:[]).with(:deviceconfig).returns(make_absolute("/dummy")) Puppet::Util::NetworkDevice::Config.new end it "should raise an error if no file is defined finally" do Puppet.expects(:[]).with(:deviceconfig).returns(nil) lambda { Puppet::Util::NetworkDevice::Config.new }.should raise_error(Puppet::DevError) end it "should read and parse the file" do Puppet::Util::NetworkDevice::Config.any_instance.expects(:read) Puppet::Util::NetworkDevice::Config.new end end describe "when parsing device" do before :each do @config = Puppet::Util::NetworkDevice::Config.new @config.stubs(:changed?).returns(true) @fd = stub 'fd' File.stubs(:open).yields(@fd) end it "should skip comments" do @fd.stubs(:each).yields(' # comment') OpenStruct.expects(:new).never @config.read end it "should increment line number even on commented lines" do @fd.stubs(:each).multiple_yields(' # comment','[router.puppetlabs.com]') @config.read @config.devices.should be_include('router.puppetlabs.com') end it "should skip blank lines" do @fd.stubs(:each).yields(' ') @config.read @config.devices.should be_empty end it "should produce the correct line number" do @fd.stubs(:each).multiple_yields(' ', '[router.puppetlabs.com]') @config.read @config.devices['router.puppetlabs.com'].line.should == 2 end it "should throw an error if the current device already exists" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', '[router.puppetlabs.com]') lambda { @config.read }.should raise_error end it "should accept device certname containing dashes" do @fd.stubs(:each).yields('[router-1.puppetlabs.com]') @config.read @config.devices.should include('router-1.puppetlabs.com') end it "should create a new device for each found device line" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', '[swith.puppetlabs.com]') @config.read @config.devices.size.should == 2 end it "should parse the device type" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', 'type cisco') @config.read @config.devices['router.puppetlabs.com'].provider.should == 'cisco' end it "should parse the device url" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', 'type cisco', 'url ssh://test/') @config.read @config.devices['router.puppetlabs.com'].url.should == 'ssh://test/' end end end diff --git a/spec/unit/util/network_device/ipcalc_spec.rb b/spec/unit/util/network_device/ipcalc_spec.rb index 82c5390a4..7f5851bbd 100755 --- a/spec/unit/util/network_device/ipcalc_spec.rb +++ b/spec/unit/util/network_device/ipcalc_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device/ipcalc' describe Puppet::Util::NetworkDevice::IPCalc do class TestIPCalc include Puppet::Util::NetworkDevice::IPCalc end before(:each) do @ipcalc = TestIPCalc.new end describe "when parsing ip/prefix" do it "should parse ipv4 without prefixes" do @ipcalc.parse('127.0.0.1').should == [32,IPAddr.new('127.0.0.1')] end it "should parse ipv4 with prefixes" do @ipcalc.parse('127.0.1.2/8').should == [8,IPAddr.new('127.0.1.2')] end it "should parse ipv6 without prefixes" do @ipcalc.parse('FE80::21A:2FFF:FE30:ECF0').should == [128,IPAddr.new('FE80::21A:2FFF:FE30:ECF0')] end it "should parse ipv6 with prefixes" do @ipcalc.parse('FE80::21A:2FFF:FE30:ECF0/56').should == [56,IPAddr.new('FE80::21A:2FFF:FE30:ECF0')] end end describe "when building netmask" do it "should produce the correct ipv4 netmask from prefix length" do @ipcalc.netmask(Socket::AF_INET, 27).should == IPAddr.new('255.255.255.224') end it "should produce the correct ipv6 netmask from prefix length" do @ipcalc.netmask(Socket::AF_INET6, 56).should == IPAddr.new('ffff:ffff:ffff:ff00::0') end end describe "when building wildmask" do it "should produce the correct ipv4 wildmask from prefix length" do @ipcalc.wildmask(Socket::AF_INET, 27).should == IPAddr.new('0.0.0.31') end it "should produce the correct ipv6 wildmask from prefix length" do @ipcalc.wildmask(Socket::AF_INET6, 126).should == IPAddr.new('::3') end end describe "when computing prefix length from netmask" do it "should produce the correct ipv4 prefix length" do @ipcalc.prefix_length(IPAddr.new('255.255.255.224')).should == 27 end it "should produce the correct ipv6 prefix length" do @ipcalc.prefix_length(IPAddr.new('fffe::0')).should == 15 end end end diff --git a/spec/unit/util/network_device/transport/base_spec.rb b/spec/unit/util/network_device/transport/base_spec.rb index f05a62fbb..0035f532a 100755 --- a/spec/unit/util/network_device/transport/base_spec.rb +++ b/spec/unit/util/network_device/transport/base_spec.rb @@ -1,41 +1,41 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device/transport/base' describe Puppet::Util::NetworkDevice::Transport::Base do class TestTransport < Puppet::Util::NetworkDevice::Transport::Base end before(:each) do @transport = TestTransport.new end describe "when sending commands" do it "should send the command to the telnet session" do @transport.expects(:send).with("line") @transport.command("line") end it "should expect an output matching the given prompt" do @transport.expects(:expect).with(/prompt/) @transport.command("line", :prompt => /prompt/) end it "should expect an output matching the default prompt" do @transport.default_prompt = /defprompt/ @transport.expects(:expect).with(/defprompt/) @transport.command("line") end it "should yield telnet output to the given block" do @transport.expects(:expect).yields("output") @transport.command("line") { |out| out.should == "output" } end it "should return telnet output to the caller" do @transport.expects(:expect).returns("output") @transport.command("line").should == "output" end end end diff --git a/spec/unit/util/network_device/transport/ssh_spec.rb b/spec/unit/util/network_device/transport/ssh_spec.rb index d3e6b643b..e72ceaa6b 100755 --- a/spec/unit/util/network_device/transport/ssh_spec.rb +++ b/spec/unit/util/network_device/transport/ssh_spec.rb @@ -1,218 +1,218 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device/transport/ssh' describe Puppet::Util::NetworkDevice::Transport::Ssh, :if => Puppet.features.ssh? do before(:each) do @transport = Puppet::Util::NetworkDevice::Transport::Ssh.new() end it "should handle login through the transport" do @transport.should be_handles_login end it "should connect to the given host and port" do Net::SSH.expects(:start).with { |host, user, args| host == "localhost" && args[:port] == 22 }.returns stub_everything @transport.host = "localhost" @transport.port = 22 @transport.connect end it "should connect using the given username and password" do Net::SSH.expects(:start).with { |host, user, args| user == "user" && args[:password] == "pass" }.returns stub_everything @transport.user = "user" @transport.password = "pass" @transport.connect end it "should raise a Puppet::Error when encountering an authentication failure" do Net::SSH.expects(:start).raises Net::SSH::AuthenticationFailed @transport.host = "localhost" @transport.user = "user" lambda { @transport.connect }.should raise_error Puppet::Error end describe "when connected" do before(:each) do @ssh = stub_everything 'ssh' @channel = stub_everything 'channel' Net::SSH.stubs(:start).returns @ssh @ssh.stubs(:open_channel).yields(@channel) @transport.stubs(:expect) end it "should open a channel" do @ssh.expects(:open_channel) @transport.connect end it "should request a pty" do @channel.expects(:request_pty) @transport.connect end it "should create a shell channel" do @channel.expects(:send_channel_request).with("shell") @transport.connect end it "should raise an error if shell channel creation fails" do @channel.expects(:send_channel_request).with("shell").yields(@channel, false) lambda { @transport.connect }.should raise_error end it "should register an on_data and on_extended_data callback" do @channel.expects(:send_channel_request).with("shell").yields(@channel, true) @channel.expects(:on_data) @channel.expects(:on_extended_data) @transport.connect end it "should accumulate data to the buffer on data" do @channel.expects(:send_channel_request).with("shell").yields(@channel, true) @channel.expects(:on_data).yields(@channel, "data") @transport.connect @transport.buf.should == "data" end it "should accumulate data to the buffer on extended data" do @channel.expects(:send_channel_request).with("shell").yields(@channel, true) @channel.expects(:on_extended_data).yields(@channel, 1, "data") @transport.connect @transport.buf.should == "data" end it "should mark eof on close" do @channel.expects(:send_channel_request).with("shell").yields(@channel, true) @channel.expects(:on_close).yields(@channel) @transport.connect @transport.should be_eof end it "should expect output to conform to the default prompt" do @channel.expects(:send_channel_request).with("shell").yields(@channel, true) @transport.expects(:default_prompt).returns("prompt") @transport.expects(:expect).with("prompt") @transport.connect end it "should start the ssh loop" do @ssh.expects(:loop) @transport.connect end end describe "when closing" do before(:each) do @ssh = stub_everything 'ssh' @channel = stub_everything 'channel' Net::SSH.stubs(:start).returns @ssh @ssh.stubs(:open_channel).yields(@channel) @channel.stubs(:send_channel_request).with("shell").yields(@channel, true) @transport.stubs(:expect) @transport.connect end it "should close the channel" do @channel.expects(:close) @transport.close end it "should close the ssh session" do @ssh.expects(:close) @transport.close end end describe "when sending commands" do before(:each) do @ssh = stub_everything 'ssh' @channel = stub_everything 'channel' Net::SSH.stubs(:start).returns @ssh @ssh.stubs(:open_channel).yields(@channel) @channel.stubs(:send_channel_request).with("shell").yields(@channel, true) @transport.stubs(:expect) @transport.connect end it "should send data to the ssh channel" do @channel.expects(:send_data).with("data\n") @transport.command("data") end it "should expect the default prompt afterward" do @transport.expects(:default_prompt).returns("prompt") @transport.expects(:expect).with("prompt") @transport.command("data") end it "should expect the given prompt" do @transport.expects(:expect).with("myprompt") @transport.command("data", :prompt => "myprompt") end it "should yield the buffer output to given block" do @transport.expects(:expect).yields("output") @transport.command("data") do |out| out.should == "output" end end it "should return buffer output" do @transport.expects(:expect).returns("output") @transport.command("data").should == "output" end end describe "when expecting output" do before(:each) do @connection = stub_everything 'connection' @socket = stub_everything 'socket' transport = stub 'transport', :socket => @socket @ssh = stub_everything 'ssh', :transport => transport @channel = stub_everything 'channel', :connection => @connection @transport.ssh = @ssh @transport.channel = @channel end it "should process the ssh event loop" do IO.stubs(:select) @transport.buf = "output" @transport.expects(:process_ssh) @transport.expect(/output/) end it "should return the output" do IO.stubs(:select) @transport.buf = "output" @transport.stubs(:process_ssh) @transport.expect(/output/).should == "output" end it "should return the output" do IO.stubs(:select) @transport.buf = "output" @transport.stubs(:process_ssh) @transport.expect(/output/).should == "output" end describe "when processing the ssh loop" do it "should advance one tick in the ssh event loop and exit on eof" do @transport.buf = '' @connection.expects(:process).then.raises(EOFError) @transport.process_ssh end end end end diff --git a/spec/unit/util/network_device/transport/telnet_spec.rb b/spec/unit/util/network_device/transport/telnet_spec.rb index 0f73f5229..9e94dbacc 100755 --- a/spec/unit/util/network_device/transport/telnet_spec.rb +++ b/spec/unit/util/network_device/transport/telnet_spec.rb @@ -1,84 +1,84 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/network_device/transport/telnet' describe Puppet::Util::NetworkDevice::Transport::Telnet do before(:each) do TCPSocket.stubs(:open).returns stub_everything('tcp') @transport = Puppet::Util::NetworkDevice::Transport::Telnet.new() end it "should not handle login through the transport" do @transport.should_not be_handles_login end it "should not open any files" do File.expects(:open).never @transport.host = "localhost" @transport.port = 23 @transport.connect end it "should connect to the given host and port" do Net::Telnet.expects(:new).with { |args| args["Host"] == "localhost" && args["Port"] == 23 }.returns stub_everything @transport.host = "localhost" @transport.port = 23 @transport.connect end it "should connect and specify the default prompt" do @transport.default_prompt = "prompt" Net::Telnet.expects(:new).with { |args| args["Prompt"] == "prompt" }.returns stub_everything @transport.host = "localhost" @transport.port = 23 @transport.connect end describe "when connected" do before(:each) do @telnet = stub_everything 'telnet' Net::Telnet.stubs(:new).returns(@telnet) @transport.connect end it "should send line to the telnet session" do @telnet.expects(:puts).with("line") @transport.send("line") end describe "when expecting output" do it "should waitfor output on the telnet session" do @telnet.expects(:waitfor).with(/regex/) @transport.expect(/regex/) end it "should return telnet session output" do @telnet.expects(:waitfor).returns("output") @transport.expect(/regex/).should == "output" end it "should yield telnet session output to the given block" do @telnet.expects(:waitfor).yields("output") @transport.expect(/regex/) { |out| out.should == "output" } end end end describe "when closing" do before(:each) do @telnet = stub_everything 'telnet' Net::Telnet.stubs(:new).returns(@telnet) @transport.connect end it "should close the telnet session" do @telnet.expects(:close) @transport.close end end end diff --git a/spec/unit/util/network_device_spec.rb b/spec/unit/util/network_device_spec.rb index 0f7c6036b..ebba774f6 100644 --- a/spec/unit/util/network_device_spec.rb +++ b/spec/unit/util/network_device_spec.rb @@ -1,50 +1,50 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'ostruct' require 'puppet/util/network_device' describe Puppet::Util::NetworkDevice do before(:each) do @device = OpenStruct.new(:name => "name", :provider => "test") end after(:each) do Puppet::Util::NetworkDevice.teardown end class Puppet::Util::NetworkDevice::Test class Device def initialize(device) end end end describe "when initializing the remote network device singleton" do it "should load the network device code" do Puppet::Util::NetworkDevice.expects(:require) Puppet::Util::NetworkDevice.init(@device) end it "should create a network device instance" do Puppet::Util::NetworkDevice.stubs(:require) Puppet::Util::NetworkDevice::Test::Device.expects(:new) Puppet::Util::NetworkDevice.init(@device) end it "should raise an error if the remote device instance can't be created" do Puppet::Util::NetworkDevice.stubs(:require).raises("error") lambda { Puppet::Util::NetworkDevice.init(@device) }.should raise_error end it "should let caller to access the singleton device" do device = stub 'device' Puppet::Util::NetworkDevice.stubs(:require) Puppet::Util::NetworkDevice::Test::Device.expects(:new).returns(device) Puppet::Util::NetworkDevice.init(@device) Puppet::Util::NetworkDevice.current.should == device end end end diff --git a/spec/unit/util/package_spec.rb b/spec/unit/util/package_spec.rb index 78c114afc..a1723e2da 100755 --- a/spec/unit/util/package_spec.rb +++ b/spec/unit/util/package_spec.rb @@ -1,20 +1,20 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/package' describe Puppet::Util::Package, " versioncmp" do it "should be able to be used as a module function" do Puppet::Util::Package.should respond_to(:versioncmp) end it "should be able to sort a long set of various unordered versions" do ary = %w{ 1.1.6 2.3 1.1a 3.0 1.5 1 2.4 1.1-4 2.3.1 1.2 2.3.0 1.1-3 2.4b 2.4 2.40.2 2.3a.1 3.1 0002 1.1-5 1.1.a 1.06} newary = ary.sort { |a, b| Puppet::Util::Package.versioncmp(a,b) } newary.should == ["0002", "1", "1.06", "1.1-3", "1.1-4", "1.1-5", "1.1.6", "1.1.a", "1.1a", "1.2", "1.5", "2.3", "2.3.0", "2.3.1", "2.3a.1", "2.4", "2.4", "2.4b", "2.40.2", "3.0", "3.1"] end end diff --git a/spec/unit/util/pidlock_spec.rb b/spec/unit/util/pidlock_spec.rb index 20f8649b8..9c63220a0 100644 --- a/spec/unit/util/pidlock_spec.rb +++ b/spec/unit/util/pidlock_spec.rb @@ -1,179 +1,179 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/pidlock' describe Puppet::Util::Pidlock do require 'puppet_spec/files' include PuppetSpec::Files before(:each) do @lockfile = tmpfile("lock") @lock = Puppet::Util::Pidlock.new(@lockfile) end describe "#lock" do it "should not be locked at start" do @lock.should_not be_locked end it "should not be mine at start" do @lock.should_not be_mine end it "should become locked" do @lock.lock @lock.should be_locked end it "should become mine" do @lock.lock @lock.should be_mine end it "should be possible to lock multiple times" do @lock.lock lambda { @lock.lock }.should_not raise_error end it "should return true when locking" do @lock.lock.should be_true end it "should return true if locked by me" do @lock.lock @lock.lock.should be_true end it "should create a lock file" do @lock.lock File.should be_exists(@lockfile) end end describe "#unlock" do it "should not be locked anymore" do @lock.lock @lock.unlock @lock.should_not be_locked end it "should return false if not locked" do @lock.unlock.should be_false end it "should return true if properly unlocked" do @lock.lock @lock.unlock.should be_true end it "should get rid of the lock file" do @lock.lock @lock.unlock File.should_not be_exists(@lockfile) end end describe "#locked?" do it "should return true if locked" do @lock.lock @lock.should be_locked end end describe "with a stale lock" do before(:each) do # fake our pid to be 1234 Process.stubs(:pid).returns(1234) # lock the file @lock.lock # fake our pid to be a different pid, to simulate someone else # holding the lock Process.stubs(:pid).returns(6789) Process.stubs(:kill).with(0, 6789) Process.stubs(:kill).with(0, 1234).raises(Errno::ESRCH) end it "should not be locked" do @lock.should_not be_locked end describe "#lock" do it "should clear stale locks" do @lock.locked? File.should_not be_exists(@lockfile) end it "should replace with new locks" do @lock.lock File.should be_exists(@lockfile) @lock.lock_pid.should == 6789 @lock.should be_mine @lock.should be_locked end end describe "#unlock" do it "should not be allowed" do @lock.unlock.should be_false end it "should not remove the lock file" do @lock.unlock File.should be_exists(@lockfile) end end end describe "with another process lock" do before(:each) do # fake our pid to be 1234 Process.stubs(:pid).returns(1234) # lock the file @lock.lock # fake our pid to be a different pid, to simulate someone else # holding the lock Process.stubs(:pid).returns(6789) Process.stubs(:kill).with(0, 6789) Process.stubs(:kill).with(0, 1234) end it "should be locked" do @lock.should be_locked end it "should not be mine" do @lock.should_not be_mine end describe "#lock" do it "should not be possible" do @lock.lock.should be_false end it "should not overwrite the lock" do @lock.lock @lock.should_not be_mine end end describe "#unlock" do it "should not be possible" do @lock.unlock.should be_false end it "should not remove the lock file" do @lock.unlock File.should be_exists(@lockfile) end it "should still not be our lock" do @lock.unlock @lock.should_not be_mine end end end -end \ No newline at end of file +end diff --git a/spec/unit/util/posix_spec.rb b/spec/unit/util/posix_spec.rb index db7c5c3a7..920fa20a4 100755 --- a/spec/unit/util/posix_spec.rb +++ b/spec/unit/util/posix_spec.rb @@ -1,255 +1,255 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/posix' class PosixTest include Puppet::Util::POSIX end describe Puppet::Util::POSIX do before do @posix = PosixTest.new end [:group, :gr].each do |name| it "should return :gid as the field for #{name}" do @posix.idfield(name).should == :gid end it "should return :getgrgid as the id method for #{name}" do @posix.methodbyid(name).should == :getgrgid end it "should return :getgrnam as the name method for #{name}" do @posix.methodbyname(name).should == :getgrnam end end [:user, :pw, :passwd].each do |name| it "should return :uid as the field for #{name}" do @posix.idfield(name).should == :uid end it "should return :getpwuid as the id method for #{name}" do @posix.methodbyid(name).should == :getpwuid end it "should return :getpwnam as the name method for #{name}" do @posix.methodbyname(name).should == :getpwnam end end describe "when retrieving a posix field" do before do @thing = stub 'thing', :field => "asdf" end it "should fail if no id was passed" do lambda { @posix.get_posix_field("asdf", "bar", nil) }.should raise_error(Puppet::DevError) end describe "and the id is an integer" do it "should log an error and return nil if the specified id is greater than the maximum allowed ID" do Puppet.settings.expects(:value).with(:maximum_uid).returns 100 Puppet.expects(:err) @posix.get_posix_field("asdf", "bar", 200).should be_nil end it "should use the method return by :methodbyid and return the specified field" do Etc.expects(:getgrgid).returns @thing @thing.expects(:field).returns "myval" @posix.get_posix_field(:gr, :field, 200).should == "myval" end it "should return nil if the method throws an exception" do Etc.expects(:getgrgid).raises ArgumentError @thing.expects(:field).never @posix.get_posix_field(:gr, :field, 200).should be_nil end end describe "and the id is not an integer" do it "should use the method return by :methodbyid and return the specified field" do Etc.expects(:getgrnam).returns @thing @thing.expects(:field).returns "myval" @posix.get_posix_field(:gr, :field, "asdf").should == "myval" end it "should return nil if the method throws an exception" do Etc.expects(:getgrnam).raises ArgumentError @thing.expects(:field).never @posix.get_posix_field(:gr, :field, "asdf").should be_nil end end end describe "when returning the gid" do before do @posix.stubs(:get_posix_field) end describe "and the group is an integer" do it "should convert integers specified as a string into an integer" do @posix.expects(:get_posix_field).with(:group, :name, 100) @posix.gid("100") end it "should look up the name for the group" do @posix.expects(:get_posix_field).with(:group, :name, 100) @posix.gid(100) end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.gid(100).should be_nil end it "should use the found name to look up the id" do @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 @posix.gid(100).should == 100 end # LAK: This is because some platforms have a broken Etc module that always return # the same group. it "should use :search_posix_field if the discovered id does not match the passed-in id" do @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 50 @posix.expects(:search_posix_field).with(:group, :gid, 100).returns "asdf" @posix.gid(100).should == "asdf" end end describe "and the group is a string" do it "should look up the gid for the group" do @posix.expects(:get_posix_field).with(:group, :gid, "asdf") @posix.gid("asdf") end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.gid("asdf").should be_nil end it "should use the found gid to look up the nam" do @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:group, :name, 100).returns "asdf" @posix.gid("asdf").should == 100 end it "should use :search_posix_field if the discovered name does not match the passed-in name" do @posix.expects(:get_posix_field).with(:group, :gid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:group, :name, 100).returns "boo" @posix.expects(:search_posix_field).with(:group, :gid, "asdf").returns "asdf" @posix.gid("asdf").should == "asdf" end end end describe "when returning the uid" do before do @posix.stubs(:get_posix_field) end describe "and the group is an integer" do it "should convert integers specified as a string into an integer" do @posix.expects(:get_posix_field).with(:passwd, :name, 100) @posix.uid("100") end it "should look up the name for the group" do @posix.expects(:get_posix_field).with(:passwd, :name, 100) @posix.uid(100) end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.uid(100).should be_nil end it "should use the found name to look up the id" do @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 @posix.uid(100).should == 100 end # LAK: This is because some platforms have a broken Etc module that always return # the same group. it "should use :search_posix_field if the discovered id does not match the passed-in id" do @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 50 @posix.expects(:search_posix_field).with(:passwd, :uid, 100).returns "asdf" @posix.uid(100).should == "asdf" end end describe "and the group is a string" do it "should look up the uid for the group" do @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf") @posix.uid("asdf") end it "should return nil if the group cannot be found" do @posix.expects(:get_posix_field).once.returns nil @posix.expects(:search_posix_field).never @posix.uid("asdf").should be_nil end it "should use the found uid to look up the nam" do @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "asdf" @posix.uid("asdf").should == 100 end it "should use :search_posix_field if the discovered name does not match the passed-in name" do @posix.expects(:get_posix_field).with(:passwd, :uid, "asdf").returns 100 @posix.expects(:get_posix_field).with(:passwd, :name, 100).returns "boo" @posix.expects(:search_posix_field).with(:passwd, :uid, "asdf").returns "asdf" @posix.uid("asdf").should == "asdf" end end end it "should be able to iteratively search for posix values" do @posix.should respond_to(:search_posix_field) end describe "when searching for posix values iteratively" do it "should iterate across all of the structs returned by Etc and return the appropriate field from the first matching value" end end diff --git a/spec/unit/util/pson_spec.rb b/spec/unit/util/pson_spec.rb index 151b59386..7816c4219 100755 --- a/spec/unit/util/pson_spec.rb +++ b/spec/unit/util/pson_spec.rb @@ -1,64 +1,64 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # Encoding: UTF-8 require 'spec_helper' require 'puppet/util/pson' class PsonUtil include Puppet::Util::Pson end describe Puppet::Util::Pson do it "should fail if no data is provided" do expect { PsonUtil.new.pson_create("type" => "foo") }.should raise_error(ArgumentError) end it "should call 'from_pson' with the provided data" do pson = PsonUtil.new pson.expects(:from_pson).with("mydata") pson.pson_create("type" => "foo", "data" => "mydata") end { 'foo' => '"foo"', 1 => '1', "\x80" => "\"\x80\"", [] => '[]' }.each do |str, expect| it "should be able to encode #{str.inspect}" do got = str.to_pson if got.respond_to? :force_encoding got.force_encoding('binary').should == expect.force_encoding('binary') else got.should == expect end end end it "should be able to handle arbitrary binary data" do bin_string = (1..20000).collect { |i| ((17*i+13*i*i) % 255).chr }.join parsed = PSON.parse(%Q{{ "type": "foo", "data": #{bin_string.to_pson} }})["data"] if parsed.respond_to? :force_encoding parsed.force_encoding('binary') bin_string.force_encoding('binary') end parsed.should == bin_string end it "should be able to handle UTF8 that isn't a real unicode character" do s = ["\355\274\267"] PSON.parse( [s].to_pson ).should == [s] end it "should be able to handle UTF8 for \\xFF" do s = ["\xc3\xbf"] PSON.parse( [s].to_pson ).should == [s] end it "should be able to handle invalid UTF8 bytes" do s = ["\xc3\xc3"] PSON.parse( [s].to_pson ).should == [s] end end diff --git a/spec/unit/util/queue/stomp_spec.rb b/spec/unit/util/queue/stomp_spec.rb index c4dc525ca..5bb3a293c 100755 --- a/spec/unit/util/queue/stomp_spec.rb +++ b/spec/unit/util/queue/stomp_spec.rb @@ -1,140 +1,140 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/queue' describe Puppet::Util::Queue, :if => Puppet.features.stomp? do it 'should load :stomp client appropriately' do Puppet.settings.stubs(:value).returns 'faux_queue_source' Puppet::Util::Queue.queue_type_to_class(:stomp).name.should == 'Puppet::Util::Queue::Stomp' end end describe 'Puppet::Util::Queue::Stomp', :if => Puppet.features.stomp? do before do # So we make sure we never create a real client instance. # Otherwise we'll try to connect, and that's bad. Stomp::Client.stubs(:new).returns stub("client", :publish => true) end it 'should be registered with Puppet::Util::Queue as :stomp type' do Puppet::Util::Queue.queue_type_to_class(:stomp).should == Puppet::Util::Queue::Stomp end describe "when initializing" do it "should create a Stomp client instance" do Stomp::Client.expects(:new).returns stub("stomp_client", :publish => true) Puppet::Util::Queue::Stomp.new end it "should provide helpful failures when the queue source is not a valid source" do # Stub rather than expect, so we can include the source in the error Puppet.settings.stubs(:value).with(:queue_source).returns "-----" lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) end it "should fail unless the queue source is a stomp URL" do # Stub rather than expect, so we can include the source in the error Puppet.settings.stubs(:value).with(:queue_source).returns "http://foo/bar" lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) end it "should fail somewhat helpfully if the Stomp client cannot be created" do Stomp::Client.expects(:new).raises RuntimeError lambda { Puppet::Util::Queue::Stomp.new }.should raise_error(ArgumentError) end list = %w{user password host port} {"user" => "myuser", "password" => "mypass", "host" => "foohost", "port" => 42}.each do |name, value| it "should use the #{name} from the queue source as the queueing #{name}" do Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser:mypass@foohost:42/" Stomp::Client.expects(:new).with { |*args| args[list.index(name)] == value } Puppet::Util::Queue::Stomp.new end end it "should create a reliable client instance" do Puppet.settings.expects(:value).with(:queue_source).returns "stomp://myuser@foohost:42/" Stomp::Client.expects(:new).with { |*args| args[4] == true } Puppet::Util::Queue::Stomp.new end end describe "when publishing a message" do before do @client = stub 'client', :publish => true Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end it "should publish it to the queue client instance" do @client.expects(:publish).with { |queue, msg, options| msg == "Smite!" } @queue.publish_message('fooqueue', 'Smite!') end it "should publish it to the transformed queue name" do @client.expects(:publish).with { |queue, msg, options| queue == "/queue/fooqueue" } @queue.publish_message('fooqueue', 'Smite!') end it "should publish it as a persistent message" do @client.expects(:publish).with { |queue, msg, options| options[:persistent] == true } @queue.publish_message('fooqueue', 'Smite!') end it "should use send when the gem does not support publish" do Stomp::Client.stubs(:new).returns(stub('client', :send => true)) Puppet::Util::Queue::Stomp.new.publish_message('fooqueue', 'Smite!') end end describe "when subscribing to a queue" do before do @client = stub 'client', :acknowledge => true, :publish => true Stomp::Client.stubs(:new).returns @client @queue = Puppet::Util::Queue::Stomp.new end it "should subscribe via the queue client instance" do @client.expects(:subscribe) @queue.subscribe('fooqueue') end it "should subscribe to the transformed queue name" do @client.expects(:subscribe).with { |queue, options| queue == "/queue/fooqueue" } @queue.subscribe('fooqueue') end it "should specify that its messages should be acknowledged" do @client.expects(:subscribe).with { |queue, options| options[:ack] == :client } @queue.subscribe('fooqueue') end it "should yield the body of any received message" do message = mock 'message' message.expects(:body).returns "mybody" @client.expects(:subscribe).yields(message) body = nil @queue.subscribe('fooqueue') { |b| body = b } body.should == "mybody" end it "should acknowledge all successfully processed messages" do message = stub 'message', :body => "mybode" @client.stubs(:subscribe).yields(message) @client.expects(:acknowledge).with(message) @queue.subscribe('fooqueue') { |b| "eh" } end end it 'should transform the simple queue name to "/queue/"' do Puppet::Util::Queue::Stomp.new.stompify_target('blah').should == '/queue/blah' end end diff --git a/spec/unit/util/queue_spec.rb b/spec/unit/util/queue_spec.rb index 59ea57d9b..c624cf2f9 100755 --- a/spec/unit/util/queue_spec.rb +++ b/spec/unit/util/queue_spec.rb @@ -1,95 +1,95 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/queue' require 'spec/mocks' def make_test_client_class(n) c = Class.new do class < make_test_client_class('Bogus::Default'), :setup => make_test_client_class('Bogus::Setup') } describe Puppet::Util::Queue do before :all do mod.register_queue_type(client_classes[:default], :default) mod.register_queue_type(client_classes[:setup], :setup) end before :each do @class = Class.new do extend mod end end after :all do instances = mod.instance_hash(:queue_clients) [:default, :setup, :bogus, :aardvark, :conflict, :test_a, :test_b].each{ |x| instances.delete(x) } end context 'when determining a type name from a class' do it 'should handle a simple one-word class name' do mod.queue_type_from_class(make_test_client_class('Foo')).should == :foo end it 'should handle a simple two-word class name' do mod.queue_type_from_class(make_test_client_class('FooBar')).should == :foo_bar end it 'should handle a two-part class name with one terminating word' do mod.queue_type_from_class(make_test_client_class('Foo::Bar')).should == :bar end it 'should handle a two-part class name with two terminating words' do mod.queue_type_from_class(make_test_client_class('Foo::BarBah')).should == :bar_bah end end context 'when registering a queue client class' do c = make_test_client_class('Foo::Bogus') it 'uses the proper default name logic when type is unspecified' do mod.register_queue_type(c) mod.queue_type_to_class(:bogus).should == c end it 'uses an explicit type name when provided' do mod.register_queue_type(c, :aardvark) mod.queue_type_to_class(:aardvark).should == c end it 'throws an exception when type names conflict' do mod.register_queue_type( make_test_client_class('Conflict') ) lambda { mod.register_queue_type( c, :conflict) }.should raise_error end it 'handle multiple, non-conflicting registrations' do a = make_test_client_class('TestA') b = make_test_client_class('TestB') mod.register_queue_type(a) mod.register_queue_type(b) mod.queue_type_to_class(:test_a).should == a mod.queue_type_to_class(:test_b).should == b end it 'throws an exception when type name is unknown' do lambda { mod.queue_type_to_class(:nope) }.should raise_error end end context 'when determining client type' do it 'returns client class based on the :queue_type setting' do Puppet.settings.expects(:value).with(:queue_type).returns(:myqueue) Puppet::Util::Queue.expects(:queue_type_to_class).with(:myqueue).returns "eh" @class.client_class.should == "eh" end end end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index c24adf763..10198755c 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -1,594 +1,594 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe "RDoc::Parser", :if => Puppet.features.rdoc1? do before :all do require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' end include PuppetSpec::Files before :each do File.stubs(:stat).with("init.pp") @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) end describe "when scanning files" do it "should parse puppet files with the puppet parser" do @parser.stubs(:scan_top_level) parser = stub 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once parser.expects(:file=).with("module/manifests/init.pp") parser.expects(:file=).with(File.expand_path("/dev/null/manifests/site.pp")) @parser.scan end it "should scan the ast for Puppet files" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan end it "should return a PuppetTopLevel to RDoc" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan.should be_a(RDoc::PuppetTopLevel) end it "should scan the top level even if the file has already parsed" do known_type = stub 'known_types' env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(env) env.stubs(:known_resource_types).returns(known_type) known_type.expects(:watching_file?).with("module/manifests/init.pp").returns(true) @parser.expects(:scan_top_level) @parser.scan end end describe "when scanning top level entities" do before :each do @resource_type_collection = resource_type_collection = stub_everything('resource_type_collection') @parser.instance_eval { @known_resource_types = resource_type_collection } @parser.stubs(:split_module).returns("module") @topcontainer = stub_everything 'topcontainer' @container = stub_everything 'container' @module = stub_everything 'module' @container.stubs(:add_module).returns(@module) @parser.stubs(:get_class_or_module).returns([@container, "module"]) end it "should read any present README as module documentation" do FileTest.stubs(:readable?).with("module/README").returns(true) FileTest.stubs(:readable?).with("module/README.rdoc").returns(false) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:comment=).with("readme") @parser.scan_top_level(@topcontainer) end it "should read any present README.rdoc as module documentation" do FileTest.stubs(:readable?).with("module/README.rdoc").returns(true) FileTest.stubs(:readable?).with("module/README").returns(false) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:comment=).with("readme") @parser.scan_top_level(@topcontainer) end it "should prefer README.rdoc over README as module documentation" do FileTest.stubs(:readable?).with("module/README.rdoc").returns(true) FileTest.stubs(:readable?).with("module/README").returns(true) File.stubs(:open).with("module/README", "r").returns("readme") File.stubs(:open).with("module/README.rdoc", "r").returns("readme.rdoc") @parser.stubs(:parse_elements) @module.expects(:comment=).with("readme.rdoc") @parser.scan_top_level(@topcontainer) end it "should tell the container its module name" do @parser.stubs(:parse_elements) @topcontainer.expects(:module_name=).with("module") @parser.scan_top_level(@topcontainer) end it "should not document our toplevel if it isn't a valid module" do @parser.stubs(:split_module).returns(nil) @topcontainer.expects(:document_self=).with(false) @parser.expects(:parse_elements).never @parser.scan_top_level(@topcontainer) end it "should set the module as global if we parse the global manifests (ie __site__ module)" do @parser.stubs(:split_module).returns(RDoc::Parser::SITE) @parser.stubs(:parse_elements) @topcontainer.expects(:global=).with(true) @parser.scan_top_level(@topcontainer) end it "should attach this module container to the toplevel container" do @parser.stubs(:parse_elements) @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) @parser.scan_top_level(@topcontainer) end it "should defer ast parsing to parse_elements for this module" do @parser.expects(:parse_elements).with(@module) @parser.scan_top_level(@topcontainer) end it "should defer plugins parsing to parse_plugins for this module" do @parser.input_file_name = "module/lib/puppet/parser/function.rb" @parser.expects(:parse_plugins).with(@module) @parser.scan_top_level(@topcontainer) end end describe "when finding modules from filepath" do before :each do Puppet::Node::Environment.any_instance.stubs(:modulepath).returns("/path/to/modules") end it "should return the module name for modulized puppet manifests" do File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" end it "should return for manifests not under module path" do File.stubs(:expand_path).returns("/path/to/manifests/init.pp") File.stubs(:identical?).returns(false) @parser.split_module("/path/to/manifests/init.pp").should == RDoc::Parser::SITE end it "should handle windows paths with drive letters", :if => Puppet.features.microsoft_windows? do @parser.split_module("C:/temp/init.pp").should == RDoc::Parser::SITE end end describe "when parsing AST elements" do before :each do @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef" @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode" @resource_type_collection = resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser.instance_eval { @known_resource_types = resource_type_collection } @container = stub_everything 'container' end it "should document classes in the parsed file" do @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container) @parser.parse_elements(@container) end it "should not document class parsed in an other file" do @klass.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container).never @parser.parse_elements(@container) end it "should document vardefs for the main class" do @klass.stubs(:name).returns :main @resource_type_collection.add_hostclass(@klass) code = stub 'code', :is_a? => false @klass.stubs(:name).returns("") @klass.stubs(:code).returns(code) @parser.expects(:scan_for_vardef).with(@container, code) @parser.parse_elements(@container) end it "should document definitions in the parsed file" do @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container) @parser.parse_elements(@container) end it "should not document definitions parsed in an other file" do @definition.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container).never @parser.parse_elements(@container) end it "should document nodes in the parsed file" do @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container) @parser.parse_elements(@container) end it "should not document node parsed in an other file" do @node.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container).never @parser.parse_elements(@container) end end describe "when documenting definition" do before(:each) do @define = stub_everything 'define', :arguments => [], :doc => "mydoc", :file => "file", :line => 42 @class = stub_everything 'class' @parser.stubs(:get_class_or_module).returns([@class, "mydef"]) end it "should register a RDoc method to the current container" do @class.expects(:add_method).with { |m| m.name == "mydef"} @parser.document_define("mydef", @define, @class) end it "should attach the documentation to this method" do @class.expects(:add_method).with { |m| m.comment = "mydoc" } @parser.document_define("mydef", @define, @class) end it "should produce a better error message on unhandled exception" do @class.expects(:add_method).raises(ArgumentError) lambda { @parser.document_define("mydef", @define, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end it "should convert all definition parameter to string" do arg = stub 'arg' val = stub 'val' @define.stubs(:arguments).returns({arg => val}) arg.expects(:to_s).returns("arg") val.expects(:to_s).returns("val") @parser.document_define("mydef", @define, @class) end end describe "when documenting nodes" do before :each do @code = stub_everything 'code' @node = stub_everything 'node', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_node = stub_everything 'rdocnode' @class = stub_everything 'class' @class.stubs(:add_node).returns(@rdoc_node) end it "should add a node to the current container" do @class.expects(:add_node).with("mynode", "parent").returns(@rdoc_node) @parser.document_node("mynode", @node, @class) end it "should associate the node documentation to the rdoc node" do @rdoc_node.expects(:comment=).with("mydoc") @parser.document_node("mynode", @node, @class) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for variable definition" do @parser.expects(:scan_for_vardef).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should produce a better error message on unhandled exception" do @class.stubs(:add_node).raises(ArgumentError) lambda { @parser.document_node("mynode", @node, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when documenting classes" do before :each do @code = stub_everything 'code' @class = stub_everything 'class', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_class = stub_everything 'rdoc-class' @module = stub_everything 'class' @module.stubs(:add_class).returns(@rdoc_class) @parser.stubs(:get_class_or_module).returns([@module, "myclass"]) end it "should add a class to the current container" do @module.expects(:add_class).with(RDoc::PuppetClass, "myclass", "parent").returns(@rdoc_class) @parser.document_class("mynode", @class, @module) end it "should set the superclass" do @rdoc_class.expects(:superclass=).with("parent") @parser.document_class("mynode", @class, @module) end it "should associate the node documentation to the rdoc class" do @rdoc_class.expects(:comment=).with("mydoc") @parser.document_class("mynode", @class, @module) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should produce a better error message on unhandled exception" do @module.stubs(:add_class).raises(ArgumentError) lambda { @parser.document_class("mynode", @class, @module) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when scanning for includes and requires" do def create_stmt(name) stmt_value = stub "#{name}_value", :to_s => "myclass" Puppet::Parser::AST::Function.new( :name => name, :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, create_stmt("include")) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt("include") ]) @class.expects(:add_include)#.with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end it "should register requires to the current container" do @code.stubs(:children).returns([ create_stmt("require") ]) @class.expects(:add_require).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end end describe "when scanning for realized virtual resources" do def create_stmt stmt_value = stub "resource_ref", :to_s => "File[\"/tmp/a\"]" Puppet::Parser::AST::Function.new( :name => 'realize', :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class,create_stmt) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt ]) @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class, [@code]) end end describe "when scanning for variable definition" do before :each do @class = stub_everything 'class' @stmt = stub_everything 'stmt', :name => "myvar", :value => "myvalue", :doc => "mydoc" @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) @stmt.stubs(:is_a?).with(Puppet::Parser::AST::VarDef).returns(true) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should recursively register variables to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, @stmt) end end describe "when scanning for resources" do before :each do @class = stub_everything 'class' @stmt = Puppet::Parser::AST::Resource.new( :type => "File", :instances => Puppet::Parser::AST::ASTArray.new(:children => [ Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::Name.new(:value => "myfile"), :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) ) ]), :doc => 'mydoc' ) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should register a PuppetResource to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, @stmt) end end describe "when parsing plugins" do before :each do @container = stub 'container' end it "should delegate parsing custom facts to parse_facts" do @parser = RDoc::Parser.new(@top_level, "module/manifests/lib/puppet/facter/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_fact).with(@container) @parser.parse_plugins(@container) end it "should delegate parsing plugins to parse_plugins" do @parser = RDoc::Parser.new(@top_level, "module/manifests/lib/puppet/functions/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_puppet_plugin).with(@container) @parser.parse_plugins(@container) end end describe "when parsing plugins" do before :each do @container = stub_everything 'container' end it "should add custom functions to the container" do File.stubs(:open).yields("# documentation module Puppet::Parser::Functions newfunction(:myfunc, :type => :rvalue) do |args| File.dirname(args[0]) end end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "myfunc" end @parser.parse_puppet_plugin(@container) end it "should add custom types to the container" do File.stubs(:open).yields("# documentation Puppet::Type.newtype(:mytype) do end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "mytype" end @parser.parse_puppet_plugin(@container) end end describe "when parsing facts" do before :each do @container = stub_everything 'container' File.stubs(:open).yields(["# documentation", "Facter.add('myfact') do", "confine :kernel => :linux", "end"]) end it "should add facts to the container" do @container.expects(:add_fact).with do |fact| fact.comment == "documentation\n" and fact.name == "myfact" end @parser.parse_fact(@container) end it "should add confine to the parsed facts" do ourfact = nil @container.expects(:add_fact).with do |fact| ourfact = fact true end @parser.parse_fact(@container) ourfact.confine.should == { :type => "kernel", :value => ":linux" } end end end diff --git a/spec/unit/util/rdoc_spec.rb b/spec/unit/util/rdoc_spec.rb index 93e5d0ee1..e224766ac 100755 --- a/spec/unit/util/rdoc_spec.rb +++ b/spec/unit/util/rdoc_spec.rb @@ -1,152 +1,152 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/rdoc' require 'rdoc/rdoc' describe Puppet::Util::RDoc do it "should fail with a clear error without RDoc 1.*" do Puppet.features.stubs(:rdoc1?).returns(false) expect { Puppet::Util::RDoc.rdoc("output", []) }. should raise_error(/the version of RDoc .* is not supported/) end describe "when generating RDoc HTML documentation", :if => Puppet.features.rdoc1? do before :each do @rdoc = stub_everything 'rdoc' RDoc::RDoc.stubs(:new).returns(@rdoc) end it "should tell the parser to ignore import" do Puppet.expects(:[]=).with(:ignoreimport, true) Puppet::Util::RDoc.rdoc("output", []) end it "should install the Puppet HTML Generator into RDoc generators" do Puppet::Util::RDoc.rdoc("output", []) RDoc::RDoc::GENERATORS["puppet"].file_name.should == "puppet/util/rdoc/generators/puppet_generator.rb" end it "should tell RDoc to generate documentation using the Puppet generator" do @rdoc.expects(:document).with { |args| args.include?("--fmt") and args.include?("puppet") } Puppet::Util::RDoc.rdoc("output", []) end it "should tell RDoc to be quiet" do @rdoc.expects(:document).with { |args| args.include?("--quiet") } Puppet::Util::RDoc.rdoc("output", []) end it "should pass charset to RDoc" do @rdoc.expects(:document).with { |args| args.include?("--charset") and args.include?("utf-8") } Puppet::Util::RDoc.rdoc("output", [], "utf-8") end it "should tell RDoc to force updates of indices when RDoc supports it" do Options::OptionList.stubs(:options).returns([["--force-update", "-U", 0 ]]) @rdoc.expects(:document).with { |args| args.include?("--force-update") } Puppet::Util::RDoc.rdoc("output", []) end it "should not tell RDoc to force updates of indices when RDoc doesn't support it" do Options::OptionList.stubs(:options).returns([]) @rdoc.expects(:document).never.with { |args| args.include?("--force-update") } Puppet::Util::RDoc.rdoc("output", []) end it "should tell RDoc to use the given outputdir" do @rdoc.expects(:document).with { |args| args.include?("--op") and args.include?("myoutputdir") } Puppet::Util::RDoc.rdoc("myoutputdir", []) end it "should tell RDoc to exclude .pp files under any modules//files section" do @rdoc.expects(:document).with { |args| args.include?("--exclude") and args.include?("/modules/[^/]*/files/.*\.pp$") } Puppet::Util::RDoc.rdoc("myoutputdir", []) end it "should give all the source directories to RDoc" do @rdoc.expects(:document).with { |args| args.include?("sourcedir") } Puppet::Util::RDoc.rdoc("output", ["sourcedir"]) end end describe "when running a manifest documentation" do it "should tell the parser to ignore import" do Puppet.expects(:[]=).with(:ignoreimport, true) Puppet::Util::RDoc.manifestdoc([]) end it "should use a parser with the correct environment" do FileTest.stubs(:file?).returns(true) Puppet::Util::RDoc.stubs(:output) parser = stub_everything Puppet::Parser::Parser.stubs(:new).with{ |env| env.is_a?(Puppet::Node::Environment) }.returns(parser) parser.expects(:file=).with("file") parser.expects(:parse) Puppet::Util::RDoc.manifestdoc(["file"]) end it "should puppet parse all given files" do FileTest.stubs(:file?).returns(true) Puppet::Util::RDoc.stubs(:output) parser = stub_everything Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:file=).with("file") parser.expects(:parse) Puppet::Util::RDoc.manifestdoc(["file"]) end it "should call output for each parsed file" do FileTest.stubs(:file?).returns(true) ast = stub_everything parser = stub_everything Puppet::Parser::Parser.stubs(:new).returns(parser) parser.stubs(:parse).returns(ast) Puppet::Util::RDoc.expects(:output).with("file", ast) Puppet::Util::RDoc.manifestdoc(["file"]) end describe "when outputing documentation" do it "should output doc for ast classes, nodes and definitions in order of increasing line number" do byline = sequence('documentation outputs in line order') Puppet::Util::RDoc.expects(:puts).with("im a class\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a node\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a define\n").in_sequence(byline) # any other output must fail Puppet::Util::RDoc.manifestdoc([my_fixture('basic.pp')]) end it "should output resource documentation if needed" do pending "#6634 being fixed" Puppet.settings[:document_all] = true byline = sequence('documentation outputs in line order') Puppet::Util::RDoc.expects(:puts).with("im a class\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a node\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a define\n").in_sequence(byline) Puppet::Util::RDoc.expects(:puts).with("im a resource\n").in_sequence(byline) # any other output must fail Puppet::Util::RDoc.manifestdoc([my_fixture('basic.pp')]) end end end end diff --git a/spec/unit/util/reference_serializer_spec.rb b/spec/unit/util/reference_serializer_spec.rb index de53ab9bb..6b4b8e0c9 100755 --- a/spec/unit/util/reference_serializer_spec.rb +++ b/spec/unit/util/reference_serializer_spec.rb @@ -1,51 +1,51 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/rails/reference_serializer' class SerializeTester include Puppet::Util::ReferenceSerializer end describe Puppet::Util::ReferenceSerializer do before do @tester = SerializeTester.new end describe "when serializing" do it "should yaml-dump resource references" do ref = Puppet::Resource.new("file", "/foo") @tester.serialize_value(ref).should =~ /^---/ end it "should convert the boolean 'true' into the string 'true'" do @tester.serialize_value(true).should == "true" end it "should convert the boolean 'false' into the string 'false'" do @tester.serialize_value(false).should == "false" end it "should return all other values" do @tester.serialize_value("foo").should == "foo" end end describe "when unserializing" do it "should yaml-load values that look like yaml" do yaml = YAML.dump(%w{a b c}) @tester.unserialize_value(yaml).should == %w{a b c} end it "should convert the string 'true' into the boolean 'true'" do @tester.unserialize_value("true").should == true end it "should convert the string 'false' into the boolean 'false'" do @tester.unserialize_value("false").should == false end it "should return all other values" do @tester.unserialize_value("foo").should == "foo" end end end diff --git a/spec/unit/util/reference_spec.rb b/spec/unit/util/reference_spec.rb index 60aa4eee1..7597c1c71 100644 --- a/spec/unit/util/reference_spec.rb +++ b/spec/unit/util/reference_spec.rb @@ -1,39 +1,39 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/reference' describe Puppet::Util::Reference do it "should create valid Markdown extension definition lists" do my_fragment = nil Puppet::Util::Reference.newreference :testreference, :doc => "A peer of the type and configuration references, but with no useful information" do my_term = "A term" my_definition = <<-EOT The definition of this term, marked by a colon and a space. We should be able to handle multi-line definitions. Each subsequent line should left-align with the first word character after the colon used as the definition marker. We should be able to handle multi-paragraph definitions. Leading indentation should be stripped from the definition, which allows us to indent the source string for cosmetic purposes. EOT my_fragment = markdown_definitionlist(my_term, my_definition) end Puppet::Util::Reference.reference(:testreference).send(:to_markdown, true) my_fragment.should == <<-EOT A term : The definition of this term, marked by a colon and a space. We should be able to handle multi-line definitions. Each subsequent line should left-align with the first word character after the colon used as the definition marker. We should be able to handle multi-paragraph definitions. Leading indentation should be stripped from the definition, which allows us to indent the source string for cosmetic purposes. EOT end end diff --git a/spec/unit/util/resource_template_spec.rb b/spec/unit/util/resource_template_spec.rb index 4f7cafdb7..1582a1c97 100755 --- a/spec/unit/util/resource_template_spec.rb +++ b/spec/unit/util/resource_template_spec.rb @@ -1,57 +1,57 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/resource_template' describe Puppet::Util::ResourceTemplate do describe "when initializing" do it "should fail if the template does not exist" do FileTest.expects(:exist?).with("/my/template").returns false lambda { Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) }.should raise_error(ArgumentError) end it "should not create the ERB template" do ERB.expects(:new).never FileTest.expects(:exist?).with("/my/template").returns true Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) end end describe "when evaluating" do before do FileTest.stubs(:exist?).returns true File.stubs(:read).returns "eh" @template = stub 'template', :result => nil ERB.stubs(:new).returns @template @resource = mock 'resource' @wrapper = Puppet::Util::ResourceTemplate.new("/my/template", @resource) end it "should set all of the resource's parameters as instance variables" do @resource.expects(:to_hash).returns(:one => "uno", :two => "dos") @template.expects(:result).with do |bind| eval("@one", bind) == "uno" and eval("@two", bind) == "dos" end @wrapper.evaluate end it "should create a template instance with the contents of the file" do File.expects(:read).with("/my/template").returns "yay" ERB.expects(:new).with("yay", 0, "-").returns(@template) @wrapper.stubs :set_resource_variables @wrapper.evaluate end it "should return the result of the template" do @wrapper.stubs :set_resource_variables @wrapper.expects(:binding).returns "mybinding" @template.expects(:result).with("mybinding").returns "myresult" @wrapper.evaluate.should == "myresult" end end end diff --git a/spec/unit/util/retryaction_spec.rb b/spec/unit/util/retryaction_spec.rb index 90f8e8eb2..a348986e8 100644 --- a/spec/unit/util/retryaction_spec.rb +++ b/spec/unit/util/retryaction_spec.rb @@ -1,62 +1,62 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/retryaction' describe Puppet::Util::RetryAction do let (:exceptions) {{ Puppet::Error => 'Puppet Error Exception' }} it 'should retry on any exception if no acceptable exceptions given' do Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 1) -1) * 0.1) ) Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 2) -1) * 0.1) ) expect do Puppet::Util::RetryAction.retry_action( :retries => 2 ) do raise ArgumentError, 'Fake Failure' end end.to raise_exception(Puppet::Util::RetryAction::RetryException::RetriesExceeded) end it 'should retry on acceptable exceptions' do Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 1) -1) * 0.1) ) Puppet::Util::RetryAction.expects(:sleep).with( (((2 ** 2) -1) * 0.1) ) expect do Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do raise Puppet::Error, 'Fake Failure' end end.to raise_exception(Puppet::Util::RetryAction::RetryException::RetriesExceeded) end it 'should not retry on unacceptable exceptions' do Puppet::Util::RetryAction.expects(:sleep).never expect do Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do raise ArgumentError end end.to raise_exception(ArgumentError) end it 'should succeed if nothing is raised' do Puppet::Util::RetryAction.expects(:sleep).never Puppet::Util::RetryAction.retry_action( :retries => 2) do true end end it 'should succeed if an expected exception is raised retried and succeeds' do should_retry = nil Puppet::Util::RetryAction.expects(:sleep).once Puppet::Util::RetryAction.retry_action( :retries => 2, :retry_exceptions => exceptions) do if should_retry true else should_retry = true raise Puppet::Error, 'Fake error' end end end end diff --git a/spec/unit/util/run_mode_spec.rb b/spec/unit/util/run_mode_spec.rb index c146aa022..9acc13279 100755 --- a/spec/unit/util/run_mode_spec.rb +++ b/spec/unit/util/run_mode_spec.rb @@ -1,49 +1,49 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Util::RunMode do before do @run_mode = Puppet::Util::RunMode.new('fake') end it "should have confdir /etc/puppet when run as root" do Puppet.features.stubs(:root?).returns(true) etcdir = Puppet.features.microsoft_windows? ? File.join(Dir::COMMON_APPDATA, "PuppetLabs", "puppet", "etc") : '/etc/puppet' # REMIND: issue with windows backslashes @run_mode.conf_dir.should == File.expand_path(etcdir) end it "should have confdir ~/.puppet when run as non-root" do Puppet.features.stubs(:root?).returns(false) @run_mode.conf_dir.should == File.expand_path("~/.puppet") end it "should have vardir /var/lib/puppet when run as root" do Puppet.features.stubs(:root?).returns(true) vardir = Puppet.features.microsoft_windows? ? File.join(Dir::COMMON_APPDATA, "PuppetLabs", "puppet", "var") : '/var/lib/puppet' # REMIND: issue with windows backslashes @run_mode.var_dir.should == File.expand_path(vardir) end it "should have vardir ~/.puppet/var when run as non-root" do Puppet.features.stubs(:root?).returns(false) @run_mode.var_dir.should == File.expand_path("~/.puppet/var") end it "should have rundir depend on vardir" do @run_mode.run_dir.should == '$vardir/run' end it "should have logopts return a hash with $vardir/log and other metadata if runmode is master" do pending("runmode.logopts functionality is being moved") @run_mode.expects(:master?).returns true @run_mode.logopts.should == { :default => "$vardir/log", :mode => 0750, :owner => "service", :group => "service", :desc => "The Puppet log directory.", } end end diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb index 0eaf43cbb..becbfc861 100755 --- a/spec/unit/util/selinux_spec.rb +++ b/spec/unit/util/selinux_spec.rb @@ -1,277 +1,277 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/selinux' include Puppet::Util::SELinux unless defined?(Selinux) module Selinux def self.is_selinux_enabled false end end end describe Puppet::Util::SELinux do describe "selinux_support?" do before do end it "should return :true if this system has SELinux enabled" do Selinux.expects(:is_selinux_enabled).returns 1 selinux_support?.should be_true end it "should return :false if this system lacks SELinux" do Selinux.expects(:is_selinux_enabled).returns 0 selinux_support?.should be_false end it "should return nil if /proc/mounts does not exist" do File.stubs(:open).with("/proc/mounts").raises("No such file or directory - /proc/mounts") read_mounts.should == nil end end describe "filesystem detection" do before :each do fh = stub 'fh', :close => nil File.stubs(:open).with("/proc/mounts").returns fh fh.expects(:read_nonblock).times(2).returns("rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n/sys /sys sysfs rw,relatime 0 0\n192.168.1.1:/var/export /mnt/nfs nfs rw,relatime,vers=3,rsize=32768,wsize=32768,namlen=255,hard,nointr,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=192.168.1.1,mountvers=3,mountproto=udp,addr=192.168.1.1 0 0\n").then.raises EOFError end it "should parse the contents of /proc/mounts" do read_mounts.should == { '/' => 'ext3', '/sys' => 'sysfs', '/mnt/nfs' => 'nfs', '/proc' => 'proc', '/dev' => 'tmpfs' } end it "should match a path on / to ext3" do find_fs('/etc/puppet/testfile').should == "ext3" end it "should match a path on /mnt/nfs to nfs" do find_fs('/mnt/nfs/testfile/foobar').should == "nfs" end it "should reture true for a capable filesystem" do selinux_label_support?('/etc/puppet/testfile').should be_true end it "should return false for a noncapable filesystem" do selinux_label_support?('/mnt/nfs/testfile').should be_false end it "should follow symlinks when determining file systems" do self.stubs(:realpath).with('/mnt/symlink/testfile').returns('/mnt/nfs/dest/testfile') selinux_label_support?('/mnt/symlink/testfile').should be_false end end describe "realpath" do it "should handle files that don't exist" do # Since I'm stubbing Pathname.new for this test, # I need to also stub the internal calls to Pathname.new, # which happen in Pathname.dirname and Parthname.basename # I want those to return real Pathname objects, # so I'm creating them before the stub is in place. realpaths = Hash.new {|hash, path| hash[path] = Pathname.new(path) } paths = ['symlink', '/mnt'] paths.each { |path| realpaths[path] } realpaths['/mnt/symlink'] = stubs "Pathname" realpaths['/mnt/symlink'].stubs(:realpath).returns(realpaths['/mnt/nfs/dest']) realpaths['/mnt/symlink'].stubs(:exist?).returns(true) realpaths['/mnt/symlink/nonexistant'] = stubs "Pathname" realpaths['/mnt/symlink/nonexistant'].stubs(:realpath).raises(Errno::ENOENT) realpaths['/mnt/symlink/nonexistant'].stubs(:exist?).returns(false) realpaths['/mnt/symlink/nonexistant'].stubs(:dirname).returns(realpaths['/mnt/symlink']) realpaths['/mnt/symlink/nonexistant'].stubs(:basename).returns(realpaths['nonexistant']) realpaths.each do |path, value| Pathname.stubs(:new).with(path).returns(value) end realpath('/mnt/symlink/nonexistant').should == '/mnt/nfs/dest/nonexistant' end end describe "get_selinux_current_context" do it "should return nil if no SELinux support" do self.expects(:selinux_support?).returns false get_selinux_current_context("/foo").should be_nil end it "should return a context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] get_selinux_current_context("/foo").should == "user_u:role_r:type_t:s0" end it "should return nil if lgetfilecon fails" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns -1 get_selinux_current_context("/foo").should be_nil end end describe "get_selinux_default_context" do it "should return nil if no SELinux support" do self.expects(:selinux_support?).returns false get_selinux_default_context("/foo").should be_nil end it "should return a context if a default context exists" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 File.expects(:lstat).with("/foo").returns fstat self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"] get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0" end it "should return nil if matchpathcon returns failure" do self.expects(:selinux_support?).returns true fstat = stub 'File::Stat', :mode => 0 File.expects(:lstat).with("/foo").returns fstat self.expects(:find_fs).with("/foo").returns "ext3" Selinux.expects(:matchpathcon).with("/foo", 0).returns -1 get_selinux_default_context("/foo").should be_nil end it "should return nil if selinux_label_support returns false" do self.expects(:selinux_support?).returns true self.expects(:find_fs).with("/foo").returns "nfs" get_selinux_default_context("/foo").should be_nil end end describe "parse_selinux_context" do it "should return nil if no context is passed" do parse_selinux_context(:seluser, nil).should be_nil end it "should return nil if the context is 'unlabeled'" do parse_selinux_context(:seluser, "unlabeled").should be_nil end it "should return the user type when called with :seluser" do parse_selinux_context(:seluser, "user_u:role_r:type_t:s0").should == "user_u" end it "should return the role type when called with :selrole" do parse_selinux_context(:selrole, "user_u:role_r:type_t:s0").should == "role_r" end it "should return the type type when called with :seltype" do parse_selinux_context(:seltype, "user_u:role_r:type_t:s0").should == "type_t" end it "should return nil for :selrange when no range is returned" do parse_selinux_context(:selrange, "user_u:role_r:type_t").should be_nil end it "should return the range type when called with :selrange" do parse_selinux_context(:selrange, "user_u:role_r:type_t:s0").should == "s0" end describe "with a variety of SELinux range formats" do ['s0', 's0:c3', 's0:c3.c123', 's0:c3,c5,c8', 'TopSecret', 'TopSecret,Classified', 'Patient_Record'].each do |range| it "should parse range '#{range}'" do parse_selinux_context(:selrange, "user_u:role_r:type_t:#{range}").should == range end end end end describe "set_selinux_context" do before :each do fh = stub 'fh', :close => nil File.stubs(:open).with("/proc/mounts").returns fh fh.stubs(:read_nonblock).returns( "rootfs / rootfs rw 0 0\n/dev/root / ext3 rw,relatime,errors=continue,user_xattr,acl,data=ordered 0 0\n"+ "/dev /dev tmpfs rw,relatime,mode=755 0 0\n/proc /proc proc rw,relatime 0 0\n"+ "/sys /sys sysfs rw,relatime 0 0\n" ).then.raises EOFError end it "should return nil if there is no SELinux support" do self.expects(:selinux_support?).returns false set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil end it "should return nil if selinux_label_support returns false" do self.expects(:selinux_support?).returns true self.expects(:selinux_label_support?).with("/foo").returns false set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_nil end it "should use lsetfilecon to set a context" do self.expects(:selinux_support?).returns true Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "user_u:role_r:type_t:s0").should be_true end it "should use lsetfilecon to set user_u user context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "foo:role_r:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "user_u", :seluser).should be_true end it "should use lsetfilecon to set role_r role context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:foo:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "role_r", :selrole).should be_true end it "should use lsetfilecon to set type_t type context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:foo:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0").returns 0 set_selinux_context("/foo", "type_t", :seltype).should be_true end it "should use lsetfilecon to set s0:c3,c5 range context" do self.expects(:selinux_support?).returns true Selinux.expects(:lgetfilecon).with("/foo").returns [0, "user_u:role_r:type_t:s0"] Selinux.expects(:lsetfilecon).with("/foo", "user_u:role_r:type_t:s0:c3,c5").returns 0 set_selinux_context("/foo", "s0:c3,c5", :selrange).should be_true end end describe "set_selinux_default_context" do it "should return nil if there is no SELinux support" do self.expects(:selinux_support?).returns false set_selinux_default_context("/foo").should be_nil end it "should return nil if no default context exists" do self.expects(:get_selinux_default_context).with("/foo").returns nil set_selinux_default_context("/foo").should be_nil end it "should do nothing and return nil if the current context matches the default context" do self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" self.expects(:get_selinux_current_context).with("/foo").returns "user_u:role_r:type_t" set_selinux_default_context("/foo").should be_nil end it "should set and return the default context if current and default do not match" do self.expects(:get_selinux_default_context).with("/foo").returns "user_u:role_r:type_t" self.expects(:get_selinux_current_context).with("/foo").returns "olduser_u:role_r:type_t" self.expects(:set_selinux_context).with("/foo", "user_u:role_r:type_t").returns true set_selinux_default_context("/foo").should == "user_u:role_r:type_t" end end end diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb index c8a477d5f..00eadd6e6 100755 --- a/spec/unit/util/storage_spec.rb +++ b/spec/unit/util/storage_spec.rb @@ -1,210 +1,210 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'yaml' require 'puppet/util/storage' describe Puppet::Util::Storage do include PuppetSpec::Files before(:all) do @basepath = make_absolute("/somepath") Puppet[:statedir] = tmpdir("statedir") end after(:all) do Puppet.settings.clear end before(:each) do Puppet::Util::Storage.clear end describe "when caching a symbol" do it "should return an empty hash" do Puppet::Util::Storage.cache(:yayness).should == {} Puppet::Util::Storage.cache(:more_yayness).should == {} end it "should add the symbol to its internal state" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} end it "should not clobber existing state when caching additional objects" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet::Util::Storage.cache(:bubblyness) Puppet::Util::Storage.state.should == {:yayness=>{},:bubblyness=>{}} end end describe "when caching a Puppet::Type" do before(:all) do @file_test = Puppet::Type.type(:file).new(:name => @basepath+"/yayness", :check => %w{checksum type}) @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+"/bin/ls /yayness") end it "should return an empty hash" do Puppet::Util::Storage.cache(@file_test).should == {} Puppet::Util::Storage.cache(@exec_test).should == {} end it "should add the resource ref to its internal state" do Puppet::Util::Storage.state.should == {} Puppet::Util::Storage.cache(@file_test) Puppet::Util::Storage.state.should == {"File[#{@basepath}/yayness]"=>{}} Puppet::Util::Storage.cache(@exec_test) Puppet::Util::Storage.state.should == {"File[#{@basepath}/yayness]"=>{}, "Exec[#{@basepath}/bin/ls /yayness]"=>{}} end end describe "when caching something other than a resource or symbol" do it "should cache by converting to a string" do data = Puppet::Util::Storage.cache(42) data[:yay] = true Puppet::Util::Storage.cache("42")[:yay].should be_true end end it "should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet::Util::Storage.clear Puppet::Util::Storage.state.should == {} end describe "when loading from the state file" do before do Puppet.settings.stubs(:use).returns(true) end describe "when the state file/directory does not exist" do before(:each) do transient = Tempfile.new('storage_test') @path = transient.path() transient.close!() end it "should not fail to load()" do FileTest.exists?(@path).should be_false Puppet[:statedir] = @path proc { Puppet::Util::Storage.load }.should_not raise_error Puppet[:statefile] = @path proc { Puppet::Util::Storage.load }.should_not raise_error end it "should not lose its internal state when load() is called" do FileTest.exists?(@path).should be_false Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet[:statefile] = @path proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {:yayness=>{}} end end describe "when the state file/directory exists" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path end it "should overwrite its internal state if load() is called" do # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should restore its internal state if the state file contains valid YAML" do test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == test_yaml end it "should initialize with a clear internal state if the state file does not contain valid YAML" do @state_file.write(:booness) @state_file.flush proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do @state_file.write(:booness) @state_file.flush YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).raises(SystemCallError) proc { Puppet::Util::Storage.load }.should raise_error end it "should attempt to rename the state file if the file is corrupted" do # We fake corruption by causing YAML.load to raise an exception YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).at_least_once proc { Puppet::Util::Storage.load }.should_not raise_error end it "should fail gracefully on load() if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) proc { Puppet::Util::Storage.load }.should_not raise_error Dir.rmdir(Puppet[:statefile]) end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end describe "when storing to the state file" do before(:each) do @state_file = tmpfile('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file end it "should create the state file if it does not exist" do FileTest.exists?(Puppet[:statefile]).should be_false Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should_not raise_error FileTest.exists?(Puppet[:statefile]).should be_true end it "should raise an exception if the state file is not a regular file" do Dir.mkdir(Puppet[:statefile]) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should raise_error Dir.rmdir(Puppet[:statefile]) end it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} proc { Puppet::Util::Storage.store }.should_not raise_error Puppet::Util::Storage.clear Puppet::Util::Storage.state.should == {} proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {:yayness=>{}} end end end diff --git a/spec/unit/util/suidmanager_spec.rb b/spec/unit/util/suidmanager_spec.rb index 19d7bfb32..044635212 100755 --- a/spec/unit/util/suidmanager_spec.rb +++ b/spec/unit/util/suidmanager_spec.rb @@ -1,290 +1,290 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Util::SUIDManager do let :user do Puppet::Type.type(:user).new(:name => 'name', :uid => 42, :gid => 42) end let :xids do Hash.new {|h,k| 0} end before :each do Puppet::Util::SUIDManager.stubs(:convert_xid).returns(42) pwent = stub('pwent', :name => 'fred', :uid => 42, :gid => 42) Etc.stubs(:getpwuid).with(42).returns(pwent) [:euid, :egid, :uid, :gid, :groups].each do |id| Process.stubs("#{id}=").with {|value| xids[id] = value } end end describe "#initgroups" do it "should use the primary group of the user as the 'basegid'" do Process.expects(:initgroups).with('fred', 42) described_class.initgroups(42) end end describe "#uid" do it "should allow setting euid/egid" do Puppet::Util::SUIDManager.egid = user[:gid] Puppet::Util::SUIDManager.euid = user[:uid] xids[:egid].should == user[:gid] xids[:euid].should == user[:uid] end end describe "#asuser" do it "should not get or set euid/egid when not root" do Puppet.features.stubs(:microsoft_windows?).returns(false) Process.stubs(:uid).returns(1) Process.stubs(:egid).returns(51) Process.stubs(:euid).returns(50) Puppet::Util::SUIDManager.asuser(user[:uid], user[:gid]) {} xids.should be_empty end context "when root and not windows" do before :each do Process.stubs(:uid).returns(0) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should set euid/egid when root" do Process.stubs(:uid).returns(0) Process.stubs(:egid).returns(51) Process.stubs(:euid).returns(50) Puppet::Util::SUIDManager.stubs(:convert_xid).with(:gid, 51).returns(51) Puppet::Util::SUIDManager.stubs(:convert_xid).with(:uid, 50).returns(50) Puppet::Util::SUIDManager.stubs(:initgroups).returns([]) yielded = false Puppet::Util::SUIDManager.asuser(user[:uid], user[:gid]) do xids[:egid].should == user[:gid] xids[:euid].should == user[:uid] yielded = true end xids[:egid].should == 51 xids[:euid].should == 50 # It's possible asuser could simply not yield, so the assertions in the # block wouldn't fail. So verify those actually got checked. yielded.should be_true end it "should just yield if user and group are nil" do yielded = false Puppet::Util::SUIDManager.asuser(nil, nil) { yielded = true } yielded.should be_true xids.should == {} end it "should just change group if only group is given" do yielded = false Puppet::Util::SUIDManager.asuser(nil, 42) { yielded = true } yielded.should be_true xids.should == { :egid => 42 } end it "should change gid to the primary group of uid by default" do Process.stubs(:initgroups) yielded = false Puppet::Util::SUIDManager.asuser(42) { yielded = true } yielded.should be_true xids.should == { :euid => 42, :egid => 42 } end it "should change both uid and gid if given" do # I don't like the sequence, but it is the only way to assert on the # internal behaviour in a reliable fashion, given we need multiple # sequenced calls to the same methods. --daniel 2012-02-05 horror = sequence('of user and group changes') Puppet::Util::SUIDManager.expects(:change_group).with(43, false).in_sequence(horror) Puppet::Util::SUIDManager.expects(:change_user).with(42, false).in_sequence(horror) Puppet::Util::SUIDManager.expects(:change_group). with(Puppet::Util::SUIDManager.egid, false).in_sequence(horror) Puppet::Util::SUIDManager.expects(:change_user). with(Puppet::Util::SUIDManager.euid, false).in_sequence(horror) yielded = false Puppet::Util::SUIDManager.asuser(42, 43) { yielded = true } yielded.should be_true end end it "should not get or set euid/egid on Windows" do Puppet.features.stubs(:microsoft_windows?).returns true Puppet::Util::SUIDManager.asuser(user[:uid], user[:gid]) {} xids.should be_empty end end describe "#change_group" do describe "when changing permanently" do it "should change_privilege" do Process::GID.expects(:change_privilege).with do |gid| Process.gid = gid Process.egid = gid end Puppet::Util::SUIDManager.change_group(42, true) xids[:egid].should == 42 xids[:gid].should == 42 end end describe "when changing temporarily" do it "should change only egid" do Puppet::Util::SUIDManager.change_group(42, false) xids[:egid].should == 42 xids[:gid].should == 0 end end end describe "#change_user" do describe "when changing permanently" do it "should change_privilege" do Process::UID.expects(:change_privilege).with do |uid| Process.uid = uid Process.euid = uid end Puppet::Util::SUIDManager.expects(:initgroups).with(42) Puppet::Util::SUIDManager.change_user(42, true) xids[:euid].should == 42 xids[:uid].should == 42 end end describe "when changing temporarily" do it "should change only euid and groups" do Puppet::Util::SUIDManager.stubs(:initgroups).returns([]) Puppet::Util::SUIDManager.change_user(42, false) xids[:euid].should == 42 xids[:uid].should == 0 end it "should set euid before groups if changing to root" do Process.stubs(:euid).returns 50 when_not_root = sequence 'when_not_root' Process.expects(:euid=).in_sequence(when_not_root) Puppet::Util::SUIDManager.expects(:initgroups).in_sequence(when_not_root) Puppet::Util::SUIDManager.change_user(0, false) end it "should set groups before euid if changing from root" do Process.stubs(:euid).returns 0 when_root = sequence 'when_root' Puppet::Util::SUIDManager.expects(:initgroups).in_sequence(when_root) Process.expects(:euid=).in_sequence(when_root) Puppet::Util::SUIDManager.change_user(50, false) end end end describe "when running commands" do before :each do # We want to make sure $CHILD_STATUS is set Kernel.system '' if $CHILD_STATUS.nil? end describe "with #run_and_capture" do it "should capture the output and return process status" do Puppet::Util::Execution. expects(:execute).with() do |*args| args[0] == 'yay' && args[1][:combine] == true && args[1][:failonfail] == false && args[1][:uid] == user[:uid] && args[1][:gid] == user[:gid] && args[1][:override_locale] == true && args[1].has_key?(:custom_environment) end . returns('output') output = Puppet::Util::SUIDManager.run_and_capture 'yay', user[:uid], user[:gid] output.first.should == 'output' output.last.should be_a(Process::Status) end end end describe "#root?" do describe "on POSIX systems" do before :each do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should be root if uid is 0" do Process.stubs(:uid).returns(0) Puppet::Util::SUIDManager.should be_root end it "should not be root if uid is not 0" do Process.stubs(:uid).returns(1) Puppet::Util::SUIDManager.should_not be_root end end describe "on Microsoft Windows", :if => Puppet.features.microsoft_windows? do it "should be root if user is privileged" do Puppet::Util::Windows::User.stubs(:admin?).returns true Puppet::Util::SUIDManager.should be_root end it "should not be root if user is not privileged" do Puppet::Util::Windows::User.stubs(:admin?).returns false Puppet::Util::SUIDManager.should_not be_root end end end end describe 'Puppet::Util::SUIDManager#groups=' do subject do Puppet::Util::SUIDManager end it "(#3419) should rescue Errno::EINVAL on OS X" do Process.expects(:groups=).raises(Errno::EINVAL, 'blew up') subject.expects(:osx_maj_ver).returns('10.7').twice subject.groups = ['list', 'of', 'groups'] end it "(#3419) should fail if an Errno::EINVAL is raised NOT on OS X" do Process.expects(:groups=).raises(Errno::EINVAL, 'blew up') subject.expects(:osx_maj_ver).returns(false) expect { subject.groups = ['list', 'of', 'groups'] }.should raise_error(Errno::EINVAL) end end diff --git a/spec/unit/util/symbolic_file_mode_spec.rb b/spec/unit/util/symbolic_file_mode_spec.rb index a6e9509f7..9583aec80 100755 --- a/spec/unit/util/symbolic_file_mode_spec.rb +++ b/spec/unit/util/symbolic_file_mode_spec.rb @@ -1,182 +1,182 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/symbolic_file_mode' describe Puppet::Util::SymbolicFileMode do include Puppet::Util::SymbolicFileMode describe "#valid_symbolic_mode?" do %w{ 0 0000 1 1 7 11 77 111 777 11 0 00000 01 01 07 011 077 0111 0777 011 = - + u= g= o= a= u+ g+ o+ a+ u- g- o- a- ugo= ugoa= ugugug= a=,u=,g= a=,g+ =rwx +rwx -rwx 644 go-w =rw,+X +X 755 u=rwx,go=rx u=rwx,go=u-w go= g=u-w 755 0755 }.each do |input| it "should treat #{input.inspect} as valid" do valid_symbolic_mode?(input).should be_true end end [0000, 0111, 0640, 0755, 0777].each do |input| it "should treat the int #{input.to_s(8)} as value" do valid_symbolic_mode?(input).should be_true end end %w{ -1 -8 8 9 18 19 91 81 000000 11111 77777 0-1 0-8 08 09 018 019 091 081 0000000 011111 077777 u g o a ug uo ua ag }.each do |input| it "should treat #{input.inspect} as invalid" do valid_symbolic_mode?(input).should be_false end end end describe "#normalize_symbolic_mode" do it "should turn an int into a string" do normalize_symbolic_mode(12).should be_an_instance_of String end it "should not add a leading zero to an int" do normalize_symbolic_mode(12).should_not =~ /^0/ end it "should not add a leading zero to a string with a number" do normalize_symbolic_mode("12").should_not =~ /^0/ end it "should string a leading zero from a number" do normalize_symbolic_mode("012").should == '12' end it "should pass through any other string" do normalize_symbolic_mode("u=rwx").should == 'u=rwx' end end describe "#symbolic_mode_to_int" do { "0654" => 00654, "u+r" => 00400, "g+r" => 00040, "a+r" => 00444, "a+x" => 00111, "o+t" => 01000, "o+t" => 01000, ["o-t", 07777] => 06777, ["a-x", 07777] => 07666, ["a-rwx", 07777] => 07000, ["ug-rwx", 07777] => 07007, "a+x,ug-rwx" => 00001, # My experimentation on debian suggests that +g ignores the sgid flag ["a+g", 02060] => 02666, # My experimentation on debian suggests that -g ignores the sgid flag ["a-g", 02666] => 02000, "g+x,a+g" => 00111, # +X without exec set in the original should not set anything "u+x,g+X" => 00100, "g+X" => 00000, # +X only refers to the original, *unmodified* file mode! ["u+x,a+X", 0600] => 00700, # Examples from the MacOS chmod(1) manpage "0644" => 00644, ["go-w", 07777] => 07755, ["=rw,+X", 07777] => 07777, ["=rw,+X", 07766] => 07777, ["=rw,+X", 07676] => 07777, ["=rw,+X", 07667] => 07777, ["=rw,+X", 07666] => 07666, "0755" => 00755, "u=rwx,go=rx" => 00755, "u=rwx,go=u-w" => 00755, ["go=", 07777] => 07700, ["g=u-w", 07777] => 07757, ["g=u-w", 00700] => 00750, ["g=u-w", 00600] => 00640, ["g=u-w", 00500] => 00550, ["g=u-w", 00400] => 00440, ["g=u-w", 00300] => 00310, ["g=u-w", 00200] => 00200, ["g=u-w", 00100] => 00110, ["g=u-w", 00000] => 00000, # Cruel, but legal, use of the action set. ["g=u+r-w", 0300] => 00350, # Empty assignments. ["u=", 00000] => 00000, ["u=", 00600] => 00000, ["ug=", 00000] => 00000, ["ug=", 00600] => 00000, ["ug=", 00660] => 00000, ["ug=", 00666] => 00006, ["=", 00000] => 00000, ["=", 00666] => 00000, ["+", 00000] => 00000, ["+", 00124] => 00124, ["-", 00000] => 00000, ["-", 00124] => 00124, }.each do |input, result| from = input.is_a?(Array) ? "#{input[0]}, 0#{input[1].to_s(8)}" : input it "should map #{from.inspect} to #{result.inspect}" do symbolic_mode_to_int(*input).should == result end end # Now, test some failure modes. it "should fail if no mode is given" do expect { symbolic_mode_to_int('') }. to raise_error Puppet::Error, /empty mode string/ end %w{u g o ug uo go ugo a uu u/x u!x u=r,,g=r}.each do |input| it "should fail if no (valid) action is given: #{input.inspect}" do expect { symbolic_mode_to_int(input) }. to raise_error Puppet::Error, /Missing action/ end end %w{u+q u-rwF u+rw,g+rw,o+RW}.each do |input| it "should fail with unknown op #{input.inspect}" do expect { symbolic_mode_to_int(input) }. to raise_error Puppet::Error, /Unknown operation/ end end it "should refuse to subtract the conditional execute op" do expect { symbolic_mode_to_int("o-rwX") }. to raise_error Puppet::Error, /only works with/ end it "should refuse to set to the conditional execute op" do expect { symbolic_mode_to_int("o=rwX") }. to raise_error Puppet::Error, /only works with/ end %w{8 08 9 09 118 119}.each do |input| it "should fail for decimal modes: #{input.inspect}" do expect { symbolic_mode_to_int(input) }. to raise_error Puppet::Error, /octal/ end end it "should set the execute bit on a directory, without exec in original" do symbolic_mode_to_int("u+X", 0444, true).to_s(8).should == "544" symbolic_mode_to_int("g+X", 0444, true).to_s(8).should == "454" symbolic_mode_to_int("o+X", 0444, true).to_s(8).should == "445" symbolic_mode_to_int("+X", 0444, true).to_s(8).should == "555" end it "should set the execute bit on a file with exec in the original" do symbolic_mode_to_int("+X", 0544).to_s(8).should == "555" end it "should not set the execute bit on a file without exec on the original even if set by earlier DSL" do symbolic_mode_to_int("u+x,go+X", 0444).to_s(8).should == "544" end end end diff --git a/spec/unit/util/tagging_spec.rb b/spec/unit/util/tagging_spec.rb index 1e5abdadd..5e4708492 100755 --- a/spec/unit/util/tagging_spec.rb +++ b/spec/unit/util/tagging_spec.rb @@ -1,98 +1,98 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/tagging' describe Puppet::Util::Tagging, "when adding tags" do before do @tagger = Object.new @tagger.extend(Puppet::Util::Tagging) end it "should have a method for adding tags" do @tagger.should be_respond_to(:tag) end it "should have a method for returning all tags" do @tagger.should be_respond_to(:tags) end it "should add tags to the returned tag list" do @tagger.tag("one") @tagger.tags.should be_include("one") end it "should not add duplicate tags to the returned tag list" do @tagger.tag("one") @tagger.tag("one") @tagger.tags.should == ["one"] end it "should return a duplicate of the tag list, rather than the original" do @tagger.tag("one") tags = @tagger.tags tags << "two" @tagger.tags.should_not be_include("two") end it "should add all provided tags to the tag list" do @tagger.tag("one", "two") @tagger.tags.should be_include("one") @tagger.tags.should be_include("two") end it "should fail on tags containing '*' characters" do lambda { @tagger.tag("bad*tag") }.should raise_error(Puppet::ParseError) end it "should fail on tags starting with '-' characters" do lambda { @tagger.tag("-badtag") }.should raise_error(Puppet::ParseError) end it "should fail on tags containing ' ' characters" do lambda { @tagger.tag("bad tag") }.should raise_error(Puppet::ParseError) end it "should allow alpha tags" do lambda { @tagger.tag("good_tag") }.should_not raise_error(Puppet::ParseError) end it "should allow tags containing '.' characters" do lambda { @tagger.tag("good.tag") }.should_not raise_error(Puppet::ParseError) end it "should provide a method for testing tag validity" do @tagger.singleton_class.publicize_methods(:valid_tag?) { @tagger.should be_respond_to(:valid_tag?) } end it "should add qualified classes as tags" do @tagger.tag("one::two") @tagger.tags.should be_include("one::two") end it "should add each part of qualified classes as tags" do @tagger.tag("one::two::three") @tagger.tags.should be_include("one") @tagger.tags.should be_include("two") @tagger.tags.should be_include("three") end it "should indicate when the object is tagged with a provided tag" do @tagger.tag("one") @tagger.should be_tagged("one") end it "should indicate when the object is not tagged with a provided tag" do @tagger.should_not be_tagged("one") end it "should indicate when the object is tagged with any tag in an array" do @tagger.tag("one") @tagger.should be_tagged("one","two","three") end it "should indicate when the object is not tagged with any tag in an array" do @tagger.tag("one") @tagger.should_not be_tagged("two","three") end end diff --git a/spec/unit/util/terminal_spec.rb b/spec/unit/util/terminal_spec.rb index d70bb94a9..769c82b71 100644 --- a/spec/unit/util/terminal_spec.rb +++ b/spec/unit/util/terminal_spec.rb @@ -1,42 +1,42 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/terminal' describe Puppet::Util::Terminal do describe '.width' do before { Puppet.features.stubs(:posix?).returns(true) } it 'should invoke `stty` and return the width' do height, width = 100, 200 subject.expects(:`).with('stty size 2>/dev/null').returns("#{height} #{width}\n") subject.width.should == width end it 'should use `tput` if `stty` is unavailable' do width = 200 subject.expects(:`).with('stty size 2>/dev/null').returns("\n") subject.expects(:`).with('tput cols 2>/dev/null').returns("#{width}\n") subject.width.should == width end it 'should default to 80 columns if `tput` and `stty` are unavailable' do width = 80 subject.expects(:`).with('stty size 2>/dev/null').returns("\n") subject.expects(:`).with('tput cols 2>/dev/null').returns("\n") subject.width.should == width end it 'should default to 80 columns if `tput` or `stty` raise exceptions' do width = 80 subject.expects(:`).with('stty size 2>/dev/null').raises() subject.stubs(:`).with('tput cols 2>/dev/null').returns("#{width + 1000}\n") subject.width.should == width end it 'should default to 80 columns if not in a POSIX environment' do width = 80 Puppet.features.stubs(:posix?).returns(false) subject.width.should == width end end end diff --git a/spec/unit/util/user_attr_spec.rb b/spec/unit/util/user_attr_spec.rb index 2d6ba8b5a..48c77f07f 100755 --- a/spec/unit/util/user_attr_spec.rb +++ b/spec/unit/util/user_attr_spec.rb @@ -1,46 +1,46 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' require 'puppet/util/user_attr' describe UserAttr do before do user_attr = ["foo::::type=role", "bar::::type=normal;profile=foobar"] File.stubs(:readlines).returns(user_attr) end describe "when getting attributes by name" do it "should return nil if there is no entry for that name" do UserAttr.get_attributes_by_name('baz').should == nil end it "should return a hash if there is an entry in /etc/user_attr" do UserAttr.get_attributes_by_name('foo').class.should == Hash end it "should return a hash with the name value from /etc/user_attr" do UserAttr.get_attributes_by_name('foo')[:name].should == 'foo' end #this test is contrived #there are a bunch of possible parameters that could be in the hash #the role/normal is just a the convention of the file describe "when the name is a role" do it "should contain :type = role" do UserAttr.get_attributes_by_name('foo')[:type].should == 'role' end end describe "when the name is not a role" do it "should contain :type = normal" do UserAttr.get_attributes_by_name('bar')[:type].should == 'normal' end end describe "when the name has more attributes" do it "should contain all the attributes" do UserAttr.get_attributes_by_name('bar')[:profile].should == 'foobar' end end end end diff --git a/spec/unit/util/warnings_spec.rb b/spec/unit/util/warnings_spec.rb index cc2c44711..ee4540247 100755 --- a/spec/unit/util/warnings_spec.rb +++ b/spec/unit/util/warnings_spec.rb @@ -1,38 +1,38 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec require 'spec_helper' describe Puppet::Util::Warnings do before(:all) do @msg1 = "booness" @msg2 = "more booness" end {:notice => "notice_once", :warning => "warnonce"}.each do |log, method| describe "when registring '#{log}' messages" do it "should always return nil" do Puppet::Util::Warnings.send(method, @msg1).should be(nil) end it "should issue a warning" do Puppet.expects(log).with(@msg1) Puppet::Util::Warnings.send(method, @msg1) end it "should issue a warning exactly once per unique message" do Puppet.expects(log).with(@msg1).once Puppet::Util::Warnings.send(method, @msg1) Puppet::Util::Warnings.send(method, @msg1) end it "should issue multiple warnings for multiple unique messages" do Puppet.expects(log).times(2) Puppet::Util::Warnings.send(method, @msg1) Puppet::Util::Warnings.send(method, @msg2) end end end after(:each) do Puppet::Util::Warnings.clear_warnings end end diff --git a/spec/unit/util/zaml_spec.rb b/spec/unit/util/zaml_spec.rb index ba5d6b259..c32e8b3aa 100755 --- a/spec/unit/util/zaml_spec.rb +++ b/spec/unit/util/zaml_spec.rb @@ -1,130 +1,130 @@ -#!/usr/bin/env rspec +#! /usr/bin/env ruby -S rspec # encoding: UTF-8 # # The above encoding line is a magic comment to set the default source encoding # of this file for the Ruby interpreter. It must be on the first or second # line of the file if an interpreter is in use. In Ruby 1.9 and later, the # source encoding determines the encoding of String and Regexp objects created # from this source file. This explicit encoding is important becuase otherwise # Ruby will pick an encoding based on LANG or LC_CTYPE environment variables. # These may be different from site to site so it's important for us to # establish a consistent behavior. For more information on M17n please see: # http://links.puppetlabs.com/understanding_m17n require 'spec_helper' require 'puppet/util/monkey_patches' describe "Pure ruby yaml implementation" do { 7 => "--- 7", 3.14159 => "--- 3.14159", "3.14159" => '--- "3.14159"', "+3.14159" => '--- "+3.14159"', "0x123abc" => '--- "0x123abc"', "-0x123abc" => '--- "-0x123abc"', "-0x123" => '--- "-0x123"', "+0x123" => '--- "+0x123"', "0x123.456" => "--- 0x123.456", 'test' => "--- test", [] => "--- []", :symbol => "--- !ruby/sym symbol", {:a => "A"} => "--- \n !ruby/sym a: A", {:a => "x\ny"} => "--- \n !ruby/sym a: |-\n x\n y" }.each { |o,y| it "should convert the #{o.class} #{o.inspect} to yaml" do o.to_yaml.should == y end it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do YAML.load(o.to_yaml).should == o end } # # Can't test for equality on raw objects { Object.new => "--- !ruby/object {}", [Object.new] => "--- \n - !ruby/object {}", {Object.new => Object.new} => "--- \n ? !ruby/object {}\n : !ruby/object {}" }.each { |o,y| it "should convert the #{o.class} #{o.inspect} to yaml" do o.to_yaml.should == y end it "should produce yaml for the #{o.class} #{o.inspect} that can be reconstituted" do lambda { YAML.load(o.to_yaml) }.should_not raise_error end } it "should emit proper labels and backreferences for common objects" do # Note: this test makes assumptions about the names ZAML chooses # for labels. x = [1, 2] y = [3, 4] z = [x, y, x, y] z.to_yaml.should == "--- \n - &id001\n - 1\n - 2\n - &id002\n - 3\n - 4\n - *id001\n - *id002" z2 = YAML.load(z.to_yaml) z2.should == z z2[0].should equal(z2[2]) z2[1].should equal(z2[3]) end it "should emit proper labels and backreferences for recursive objects" do x = [1, 2] x << x x.to_yaml.should == "--- &id001\n \n - 1\n - 2\n - *id001" x2 = YAML.load(x.to_yaml) x2.should be_a(Array) x2.length.should == 3 x2[0].should == 1 x2[1].should == 2 x2[2].should equal(x2) end end # Note, many of these tests will pass on Ruby 1.8 but fail on 1.9 if the patch # fix is not applied to Puppet or there's a regression. These version # dependant failures are intentional since the string encoding behavior changed # significantly in 1.9. describe "UTF-8 encoded String#to_yaml (Bug #11246)" do # JJM All of these snowmen are different representations of the same # UTF-8 encoded string. let(:snowman) { 'Snowman: [☃]' } let(:snowman_escaped) { "Snowman: [\xE2\x98\x83]" } describe "UTF-8 String Literal" do subject { snowman } it "should serialize to YAML" do subject.to_yaml end it "should serialize and deserialize to the same thing" do YAML.load(subject.to_yaml).should == subject end it "should serialize and deserialize to a String compatible with a UTF-8 encoded Regexp" do YAML.load(subject.to_yaml).should =~ /☃/u end end end describe "binary data" do subject { "M\xC0\xDF\xE5tt\xF6" } it "should not explode encoding binary data" do expect { subject.to_yaml }.not_to raise_error end it "should mark the binary data as binary" do subject.to_yaml.should =~ /!binary/ end it "should round-trip the data" do yaml = subject.to_yaml read = YAML.load(yaml) if read.respond_to? :force_encoding read.force_encoding('binary') subject.force_encoding('binary') end read.should == subject end end