summaryrefslogtreecommitdiff
path: root/spec/unit
diff options
context:
space:
mode:
authorStig Sandbeck Mathisen <ssm@debian.org>2014-09-07 10:14:36 +0200
committerStig Sandbeck Mathisen <ssm@debian.org>2014-09-07 10:14:36 +0200
commitd4b83be375ac1dead058e091191ee7c7b7c24c8a (patch)
treedc825687392ae3068de5b764be60c53122d9e02a /spec/unit
parent229cbb976fe0f70f5f30548b83517b415840f9bb (diff)
parent1681684857c6e39d60d87b0b3520d8783977ceff (diff)
downloadpuppet-upstream/3.7.0.tar.gz
Imported Upstream version 3.7.0upstream/3.7.0
Diffstat (limited to 'spec/unit')
-rwxr-xr-xspec/unit/application/apply_spec.rb6
-rwxr-xr-xspec/unit/application/doc_spec.rb30
-rwxr-xr-xspec/unit/application/master_spec.rb62
-rwxr-xr-xspec/unit/application/resource_spec.rb5
-rwxr-xr-xspec/unit/configurer/downloader_factory_spec.rb96
-rwxr-xr-xspec/unit/configurer/downloader_spec.rb129
-rwxr-xr-xspec/unit/configurer/plugin_handler_spec.rb44
-rwxr-xr-xspec/unit/configurer_spec.rb4
-rw-r--r--spec/unit/defaults_spec.rb30
-rwxr-xr-xspec/unit/face/certificate_request_spec.rb7
-rwxr-xr-xspec/unit/face/certificate_revocation_list_spec.rb7
-rwxr-xr-xspec/unit/face/config_spec.rb3
-rwxr-xr-xspec/unit/face/key_spec.rb7
-rw-r--r--spec/unit/face/module/build_spec.rb4
-rw-r--r--spec/unit/face/module/install_spec.rb16
-rw-r--r--spec/unit/face/parser_spec.rb104
-rwxr-xr-xspec/unit/face/report_spec.rb7
-rwxr-xr-xspec/unit/face/resource_spec.rb7
-rwxr-xr-xspec/unit/face/resource_type_spec.rb7
-rwxr-xr-xspec/unit/file_bucket/file_spec.rb4
-rw-r--r--spec/unit/file_system/tempfile_spec.rb48
-rw-r--r--spec/unit/file_system/uniquefile_spec.rb184
-rw-r--r--spec/unit/forge/errors_spec.rb10
-rw-r--r--spec/unit/forge/module_release_spec.rb267
-rw-r--r--spec/unit/forge/repository_spec.rb112
-rw-r--r--spec/unit/forge_spec.rb42
-rw-r--r--spec/unit/functions/assert_type_spec.rb25
-rw-r--r--spec/unit/functions/each_spec.rb (renamed from spec/unit/parser/methods/each_spec.rb)22
-rw-r--r--spec/unit/functions/epp_spec.rb (renamed from spec/unit/parser/functions/epp_spec.rb)70
-rw-r--r--spec/unit/functions/filter_spec.rb (renamed from spec/unit/parser/methods/filter_spec.rb)72
-rw-r--r--spec/unit/functions/inline_epp_spec.rb (renamed from spec/unit/parser/functions/inline_epp_spec.rb)21
-rw-r--r--spec/unit/functions/map_spec.rb169
-rw-r--r--spec/unit/functions/match_spec.rb57
-rw-r--r--spec/unit/functions/reduce_spec.rb (renamed from spec/unit/parser/methods/reduce_spec.rb)28
-rw-r--r--spec/unit/functions/slice_spec.rb (renamed from spec/unit/parser/methods/slice_spec.rb)55
-rw-r--r--spec/unit/functions/with_spec.rb35
-rw-r--r--spec/unit/functions4_spec.rb15
-rwxr-xr-xspec/unit/indirector/catalog/compiler_spec.rb2
-rw-r--r--spec/unit/indirector/catalog/static_compiler_spec.rb11
-rw-r--r--spec/unit/indirector/data_binding/hiera_spec.rb97
-rwxr-xr-xspec/unit/indirector/facts/facter_spec.rb171
-rw-r--r--spec/unit/indirector/hiera_spec.rb17
-rwxr-xr-xspec/unit/indirector/request_spec.rb6
-rwxr-xr-xspec/unit/indirector/resource/ral_spec.rb5
-rwxr-xr-xspec/unit/indirector/resource_type/parser_spec.rb29
-rwxr-xr-xspec/unit/indirector/rest_spec.rb42
-rwxr-xr-xspec/unit/interface/face_collection_spec.rb4
-rw-r--r--spec/unit/module_tool/applications/builder_spec.rb378
-rw-r--r--spec/unit/module_tool/applications/uninstaller_spec.rb22
-rw-r--r--spec/unit/module_tool/applications/unpacker_spec.rb40
-rw-r--r--spec/unit/module_tool/applications/upgrader_spec.rb22
-rw-r--r--spec/unit/module_tool/installed_modules_spec.rb49
-rw-r--r--spec/unit/module_tool/metadata_spec.rb76
-rw-r--r--spec/unit/module_tool/tar/mini_spec.rb3
-rwxr-xr-xspec/unit/network/authentication_spec.rb4
-rw-r--r--spec/unit/network/http/api/v2/environments_spec.rb27
-rwxr-xr-x[-rw-r--r--]spec/unit/network/http/connection_spec.rb219
-rwxr-xr-xspec/unit/network/http/factory_spec.rb82
-rwxr-xr-xspec/unit/network/http/handler_spec.rb36
-rwxr-xr-xspec/unit/network/http/nocache_pool_spec.rb43
-rwxr-xr-xspec/unit/network/http/pool_spec.rb269
-rwxr-xr-xspec/unit/network/http/rack/rest_spec.rb2
-rwxr-xr-xspec/unit/network/http/session_spec.rb43
-rwxr-xr-xspec/unit/network/http/site_spec.rb90
-rwxr-xr-xspec/unit/network/http/webrick_spec.rb2
-rwxr-xr-xspec/unit/network/http_pool_spec.rb15
-rwxr-xr-xspec/unit/network/http_spec.rb10
-rwxr-xr-xspec/unit/node/environment_spec.rb90
-rwxr-xr-xspec/unit/node_spec.rb8
-rwxr-xr-xspec/unit/parser/compiler_spec.rb11
-rw-r--r--spec/unit/parser/eparser_adapter_spec.rb407
-rwxr-xr-xspec/unit/parser/files_spec.rb19
-rw-r--r--spec/unit/parser/functions/contain_spec.rb51
-rwxr-xr-xspec/unit/parser/functions/create_resources_spec.rb9
-rwxr-xr-xspec/unit/parser/functions/digest_spec.rb31
-rwxr-xr-xspec/unit/parser/functions/file_spec.rb53
-rwxr-xr-xspec/unit/parser/functions/include_spec.rb16
-rwxr-xr-xspec/unit/parser/functions/realize_spec.rb78
-rwxr-xr-xspec/unit/parser/functions/require_spec.rb24
-rwxr-xr-xspec/unit/parser/functions/search_spec.rb5
-rw-r--r--spec/unit/parser/functions/shared.rb82
-rwxr-xr-xspec/unit/parser/functions_spec.rb7
-rwxr-xr-xspec/unit/parser/lexer_spec.rb11
-rw-r--r--spec/unit/parser/methods/map_spec.rb184
-rw-r--r--spec/unit/parser/methods/shared.rb45
-rwxr-xr-xspec/unit/parser/type_loader_spec.rb1
-rw-r--r--spec/unit/pops/benchmark_spec.rb2
-rw-r--r--spec/unit/pops/binder/bindings_composer_spec.rb46
-rw-r--r--spec/unit/pops/binder/injector_spec.rb14
-rw-r--r--spec/unit/pops/evaluator/access_ops_spec.rb6
-rw-r--r--spec/unit/pops/evaluator/comparison_ops_spec.rb11
-rw-r--r--spec/unit/pops/evaluator/evaluating_parser_spec.rb280
-rw-r--r--spec/unit/pops/evaluator/logical_ops_spec.rb4
-rw-r--r--spec/unit/pops/evaluator/variables_spec.rb105
-rw-r--r--spec/unit/pops/issues_spec.rb170
-rw-r--r--spec/unit/pops/loaders/dependency_loader_spec.rb17
-rw-r--r--spec/unit/pops/loaders/loader_paths_spec.rb19
-rw-r--r--spec/unit/pops/loaders/loaders_spec.rb42
-rw-r--r--spec/unit/pops/loaders/module_loaders_spec.rb29
-rw-r--r--spec/unit/pops/loaders/static_loader_spec.rb6
-rw-r--r--spec/unit/pops/parser/epp_parser_spec.rb47
-rw-r--r--spec/unit/pops/parser/evaluating_parser_spec.rb1
-rw-r--r--spec/unit/pops/parser/lexer2_spec.rb25
-rwxr-xr-xspec/unit/pops/parser/lexer_spec.rb840
-rw-r--r--spec/unit/pops/parser/parse_basic_expressions_spec.rb5
-rw-r--r--spec/unit/pops/parser/parse_calls_spec.rb9
-rw-r--r--spec/unit/pops/parser/parse_conditionals_spec.rb17
-rw-r--r--spec/unit/pops/parser/parse_containers_spec.rb69
-rw-r--r--spec/unit/pops/parser/parse_resource_spec.rb228
-rw-r--r--spec/unit/pops/parser/parser_spec.rb16
-rw-r--r--spec/unit/pops/parser/parsing_typed_parameters_spec.rb72
-rw-r--r--spec/unit/pops/transformer/transform_calls_spec.rb2
-rw-r--r--spec/unit/pops/transformer/transform_resource_spec.rb185
-rw-r--r--spec/unit/pops/types/type_calculator_spec.rb311
-rw-r--r--spec/unit/pops/types/type_factory_spec.rb11
-rw-r--r--spec/unit/pops/types/type_parser_spec.rb31
-rw-r--r--spec/unit/pops/validator/validator_spec.rb170
-rwxr-xr-xspec/unit/provider/exec/posix_spec.rb36
-rwxr-xr-xspec/unit/provider/exec/shell_spec.rb4
-rwxr-xr-xspec/unit/provider/file/windows_spec.rb14
-rw-r--r--spec/unit/provider/group/windows_adsi_spec.rb34
-rwxr-xr-xspec/unit/provider/package/gem_spec.rb10
-rwxr-xr-xspec/unit/provider/package/openbsd_spec.rb75
-rwxr-xr-xspec/unit/provider/package/pacman_spec.rb161
-rwxr-xr-xspec/unit/provider/package/windows/package_spec.rb27
-rwxr-xr-xspec/unit/provider/package/yum_spec.rb1
-rwxr-xr-xspec/unit/provider/parsedfile_spec.rb2
-rw-r--r--spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb18
-rwxr-xr-x[-rw-r--r--]spec/unit/provider/service/openbsd_spec.rb28
-rwxr-xr-xspec/unit/provider/service/upstart_spec.rb13
-rwxr-xr-xspec/unit/provider/ssh_authorized_key/parsed_spec.rb6
-rwxr-xr-xspec/unit/provider/user/user_role_add_spec.rb24
-rwxr-xr-xspec/unit/provider/user/windows_adsi_spec.rb34
-rwxr-xr-xspec/unit/reports/store_spec.rb16
-rwxr-xr-xspec/unit/resource/catalog_spec.rb5
-rwxr-xr-xspec/unit/resource_spec.rb4
-rw-r--r--spec/unit/settings/array_setting_spec.rb39
-rw-r--r--spec/unit/settings/autosign_setting_spec.rb4
-rw-r--r--spec/unit/settings/environment_conf_spec.rb87
-rwxr-xr-xspec/unit/settings/file_setting_spec.rb9
-rwxr-xr-xspec/unit/settings/priority_setting_spec.rb8
-rwxr-xr-xspec/unit/settings_spec.rb189
-rwxr-xr-xspec/unit/ssl/certificate_authority_spec.rb27
-rwxr-xr-xspec/unit/ssl/inventory_spec.rb13
-rw-r--r--spec/unit/ssl/validator_spec.rb1
-rwxr-xr-xspec/unit/transaction/resource_harness_spec.rb64
-rwxr-xr-xspec/unit/transaction_spec.rb145
-rwxr-xr-xspec/unit/type/cron_spec.rb6
-rwxr-xr-xspec/unit/type/exec_spec.rb9
-rwxr-xr-xspec/unit/type/file/content_spec.rb125
-rwxr-xr-xspec/unit/type/file/mode_spec.rb27
-rwxr-xr-xspec/unit/type/file/source_spec.rb30
-rwxr-xr-xspec/unit/type/file_spec.rb6
-rwxr-xr-xspec/unit/type/nagios_spec.rb15
-rwxr-xr-xspec/unit/type/resources_spec.rb94
-rwxr-xr-xspec/unit/type/user_spec.rb21
-rwxr-xr-x[-rw-r--r--]spec/unit/type/yumrepo_spec.rb136
-rwxr-xr-xspec/unit/type/zone_spec.rb45
-rwxr-xr-xspec/unit/type_spec.rb20
-rwxr-xr-xspec/unit/util/colors_spec.rb22
-rwxr-xr-xspec/unit/util/command_line_spec.rb18
-rwxr-xr-xspec/unit/util/execution_spec.rb77
-rwxr-xr-xspec/unit/util/feature_spec.rb12
-rw-r--r--spec/unit/util/http_proxy_spec.rb44
-rwxr-xr-xspec/unit/util/log/destinations_spec.rb46
-rwxr-xr-xspec/unit/util/logging_spec.rb44
-rw-r--r--spec/unit/util/pidlock_spec.rb38
-rw-r--r--spec/unit/util/profiler/aggregate_spec.rb59
-rw-r--r--spec/unit/util/profiler/around_profiler_spec.rb61
-rw-r--r--spec/unit/util/profiler/logging_spec.rb47
-rw-r--r--spec/unit/util/profiler/none_spec.rb12
-rw-r--r--spec/unit/util/profiler/wall_clock_spec.rb2
-rw-r--r--spec/unit/util/profiler_spec.rb55
-rwxr-xr-xspec/unit/util/queue_spec.rb1
-rwxr-xr-xspec/unit/util/rdoc/parser_spec.rb20
-rwxr-xr-xspec/unit/util/tagging_spec.rb33
-rw-r--r--spec/unit/util/windows/access_control_entry_spec.rb2
-rwxr-xr-xspec/unit/util/windows/adsi_spec.rb (renamed from spec/unit/util/adsi_spec.rb)219
-rw-r--r--spec/unit/util/windows/api_types_spec.rb28
-rwxr-xr-xspec/unit/util/windows/registry_spec.rb13
-rwxr-xr-xspec/unit/util/windows/sid_spec.rb9
-rw-r--r--spec/unit/util/windows/string_spec.rb4
-rwxr-xr-xspec/unit/util/zaml_spec.rb6
183 files changed, 6409 insertions, 3644 deletions
diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb
index de4784fbe..ec6afee66 100755
--- a/spec/unit/application/apply_spec.rb
+++ b/spec/unit/application/apply_spec.rb
@@ -131,7 +131,9 @@ describe Puppet::Application::Apply do
@apply.setup
- expect(Puppet::Util::Profiler.current).to be_a(Puppet::Util::Profiler::WallClock)
+ expect(Puppet::Util::Profiler.current).to satisfy do |ps|
+ ps.any? {|p| p.is_a? Puppet::Util::Profiler::WallClock }
+ end
end
it "does not have a profiler if profiling is disabled" do
@@ -139,7 +141,7 @@ describe Puppet::Application::Apply do
@apply.setup
- expect(Puppet::Util::Profiler.current).to eq(Puppet::Util::Profiler::NONE)
+ expect(Puppet::Util::Profiler.current.length).to be 0
end
it "should set default_file_terminus to `file_server` to be local" do
diff --git a/spec/unit/application/doc_spec.rb b/spec/unit/application/doc_spec.rb
index 8e43907ca..2f8624abd 100755
--- a/spec/unit/application/doc_spec.rb
+++ b/spec/unit/application/doc_spec.rb
@@ -259,6 +259,7 @@ describe Puppet::Application::Doc do
let(:envdir) { tmpdir('env') }
let(:modules) { File.join(envdir, "modules") }
+ let(:modules2) { File.join(envdir, "modules2") }
let(:manifests) { File.join(envdir, "manifests") }
before :each do
@@ -277,8 +278,8 @@ describe Puppet::Application::Doc do
around(:each) do |example|
FileUtils.mkdir_p(modules)
- @env = Puppet::Node::Environment.create(Puppet[:environment].to_sym, [modules], "#{manifests}/site.pp")
- Puppet.override(:environments => Puppet::Environments::Static.new(@env)) do
+ env = Puppet::Node::Environment.create(Puppet[:environment].to_sym, [modules], "#{manifests}/site.pp")
+ Puppet.override({:environments => Puppet::Environments::Static.new(env), :current_environment => env}) do
example.run
end
end
@@ -287,39 +288,42 @@ describe Puppet::Application::Doc do
@doc.options[:all] = true
Puppet.settings.expects(:[]=).with(:document_all, true)
- expect { @doc.rdoc }.to exit_with 0
+ 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
+ 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[:charset] = 'utf-8'
Puppet::Util::RDoc.expects(:rdoc).with('doc', [modules, manifests], "utf-8")
- expect { @doc.rdoc }.to exit_with 0
+ 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[:outputdir] = false
Puppet::Util::RDoc.expects(:rdoc).with('doc', [modules, manifests], nil)
- expect { @doc.rdoc }.to exit_with 0
+ 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
+ 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(:manifest).returns('envmanifests/site.pp')
-
- Puppet::Util::RDoc.expects(:rdoc).with('doc', ['envmodules1','envmodules2','envmanifests'], nil)
-
- expect { @doc.rdoc }.to exit_with 0
+ FileUtils.mkdir_p(modules)
+ FileUtils.mkdir_p(modules2)
+ env = Puppet::Node::Environment.create(Puppet[:environment].to_sym,
+ [modules, modules2],
+ "envmanifests/site.pp")
+ Puppet.override({:environments => Puppet::Environments::Static.new(env), :current_environment => env}) do
+ Puppet::Util::RDoc.stubs(:rdoc).with('doc', [modules.to_s, modules2.to_s, env.manifest.to_s], nil)
+ expect { @doc.rdoc }.to exit_with(0)
+ end
end
end
diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb
index 4d3167ce8..ef9c1b174 100755
--- a/spec/unit/application/master_spec.rb
+++ b/spec/unit/application/master_spec.rb
@@ -114,38 +114,46 @@ describe Puppet::Application::Master, :unless => Puppet.features.microsoft_windo
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
+ describe "setting up logging" do
+ it "sets the log level" do
+ @master.expects(:set_log_level)
+ @master.setup
+ end
- it "should set syslog as the log destination if no --logdest and not --daemonize" do
- Puppet::Log.expects(:newdestination).with(:syslog)
+ describe "when the log destination is not explicitly configured" do
+ before do
+ @master.options.stubs(:[]).with(:setdest).returns false
+ end
- @master.setup
- end
+ it "logs to the console when --compile is given" do
+ @master.options.stubs(:[]).with(:node).returns "default"
+ Puppet::Util::Log.expects(:newdestination).with(:console)
+ @master.setup
+ end
- it "should set syslog as the log destination if --rack" do
- @master.options.stubs(:[]).with(:rack).returns(:true)
+ it "logs to the console when the master is not daemonized or run with rack" do
+ Puppet::Util::Log.expects(:newdestination).with(:console)
+ Puppet[:daemonize] = false
+ @master.options.stubs(:[]).with(:rack).returns(false)
+ @master.setup
+ end
- Puppet::Log.expects(:newdestination).with(:syslog)
+ it "logs to syslog when the master is daemonized" do
+ Puppet::Util::Log.expects(:newdestination).with(:console).never
+ Puppet::Util::Log.expects(:newdestination).with(:syslog)
+ Puppet[:daemonize] = true
+ @master.options.stubs(:[]).with(:rack).returns(false)
+ @master.setup
+ end
- @master.setup
+ it "logs to syslog when the master is run with rack" do
+ Puppet::Util::Log.expects(:newdestination).with(:console).never
+ Puppet::Util::Log.expects(:newdestination).with(:syslog)
+ Puppet[:daemonize] = false
+ @master.options.stubs(:[]).with(:rack).returns(true)
+ @master.setup
+ end
+ end
end
it "should print puppet config if asked to in Puppet config" do
diff --git a/spec/unit/application/resource_spec.rb b/spec/unit/application/resource_spec.rb
index f2e2596ca..029aa466d 100755
--- a/spec/unit/application/resource_spec.rb
+++ b/spec/unit/application/resource_spec.rb
@@ -16,11 +16,6 @@ describe Puppet::Application::Resource 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
diff --git a/spec/unit/configurer/downloader_factory_spec.rb b/spec/unit/configurer/downloader_factory_spec.rb
new file mode 100755
index 000000000..fae919918
--- /dev/null
+++ b/spec/unit/configurer/downloader_factory_spec.rb
@@ -0,0 +1,96 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/configurer'
+
+describe Puppet::Configurer::DownloaderFactory do
+ let(:factory) { Puppet::Configurer::DownloaderFactory.new }
+ let(:environment) { Puppet::Node::Environment.create(:myenv, []) }
+
+ let(:plugin_downloader) do
+ factory.create_plugin_downloader(environment)
+ end
+
+ let(:facts_downloader) do
+ factory.create_plugin_facts_downloader(environment)
+ end
+
+ def ignores_source_permissions(downloader)
+ expect(downloader.file[:source_permissions]).to eq(:ignore)
+ end
+
+ def uses_source_permissions(downloader)
+ expect(downloader.file[:source_permissions]).to eq(:use)
+ end
+
+ context "when creating a plugin downloader for modules" do
+ it 'is named "plugin"' do
+ expect(plugin_downloader.name).to eq('plugin')
+ end
+
+ it 'downloads files into Puppet[:plugindest]' do
+ plugindest = File.expand_path("/tmp/pdest")
+ Puppet[:plugindest] = plugindest
+
+ expect(plugin_downloader.file[:path]).to eq(plugindest)
+ end
+
+ it 'downloads files from Puppet[:pluginsource]' do
+ Puppet[:pluginsource] = 'puppet:///myotherplugins'
+
+ expect(plugin_downloader.file[:source]).to eq([Puppet[:pluginsource]])
+ end
+
+ it 'ignores files from Puppet[:pluginsignore]' do
+ Puppet[:pluginsignore] = 'pignore'
+
+ expect(plugin_downloader.file[:ignore]).to eq(['pignore'])
+ end
+
+ it 'splits Puppet[:pluginsignore] on whitespace' do
+ Puppet[:pluginsignore] = ".svn CVS .git"
+
+ expect(plugin_downloader.file[:ignore]).to eq(%w[.svn CVS .git])
+ end
+
+ it "ignores source permissions" do
+ ignores_source_permissions(plugin_downloader)
+ end
+ end
+
+ context "when creating a plugin downloader for external facts" do
+ it 'is named "pluginfacts"' do
+ expect(facts_downloader.name).to eq('pluginfacts')
+ end
+
+ it 'downloads files into Puppet[:pluginfactdest]' do
+ plugindest = File.expand_path("/tmp/pdest")
+ Puppet[:pluginfactdest] = plugindest
+
+ expect(facts_downloader.file[:path]).to eq(plugindest)
+ end
+
+ it 'downloads files from Puppet[:pluginfactsource]' do
+ Puppet[:pluginfactsource] = 'puppet:///myotherfacts'
+
+ expect(facts_downloader.file[:source]).to eq([Puppet[:pluginfactsource]])
+ end
+
+ it 'ignores files from Puppet[:pluginsignore]' do
+ Puppet[:pluginsignore] = 'pignore'
+
+ expect(facts_downloader.file[:ignore]).to eq(['pignore'])
+ end
+
+ context "on POSIX", :if => Puppet.features.posix? do
+ it "uses source permissions" do
+ uses_source_permissions(facts_downloader)
+ end
+ end
+
+ context "on Windows", :if => Puppet.features.microsoft_windows? do
+ it "ignores source permissions during external fact pluginsync" do
+ ignores_source_permissions(facts_downloader)
+ end
+ end
+ end
+end
diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb
index 9e1d3a29b..71b88bb51 100755
--- a/spec/unit/configurer/downloader_spec.rb
+++ b/spec/unit/configurer/downloader_spec.rb
@@ -6,6 +6,10 @@ require 'puppet/configurer/downloader'
describe Puppet::Configurer::Downloader do
require 'puppet_spec/files'
include PuppetSpec::Files
+
+ let(:path) { Puppet[:plugindest] }
+ let(:source) { 'puppet://puppet/plugins' }
+
it "should require a name" do
lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError)
end
@@ -21,87 +25,115 @@ describe Puppet::Configurer::Downloader do
dler.source.should == "source"
end
- describe "when creating the file that does the downloading" do
- before do
- @dler = Puppet::Configurer::Downloader.new("foo", "path", "source")
- end
+ def downloader(options = {})
+ options[:name] ||= "facts"
+ options[:path] ||= path
+ options[:source_permissions] ||= :ignore
+ Puppet::Configurer::Downloader.new(options[:name], options[:path], source, options[:ignore], options[:environment], options[:source_permissions])
+ end
+
+ def generate_file_resource(options = {})
+ dler = downloader(options)
+ dler.file
+ end
+ describe "when creating the file that does the downloading" do
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
+ file = generate_file_resource(:path => path, :source => source)
+
+ expect(file[:path]).to eq(path)
+ expect(file[:source]).to eq([source])
end
it "should tag the file with the downloader name" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" }
- @dler.file
+ name = "mydownloader"
+ file = generate_file_resource(:name => name)
+
+ expect(file[:tag]).to eq([name])
end
it "should always recurse" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true }
- @dler.file
+ file = generate_file_resource
+
+ expect(file[:recurse]).to be_true
end
it "should always purge" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true }
- @dler.file
+ file = generate_file_resource
+
+ expect(file[:purge]).to be_true
end
it "should never be in noop" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false }
- @dler.file
+ file = generate_file_resource
+
+ expect(file[:noop]).to be_false
+ end
+
+ it "should set source_permissions to ignore by default" do
+ file = generate_file_resource
+
+ expect(file[:source_permissions]).to eq(:ignore)
end
- it "should set source_permissions to ignore" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:source_permissions] == :ignore }
- @dler.file
+ it "should allow source_permissions to be overridden" do
+ file = generate_file_resource(:source_permissions => :use)
+
+ expect(file[:source_permissions]).to eq(:use)
end
describe "on POSIX", :as_platform => :posix do
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
+
+ file = generate_file_resource(:path => '/path')
+ expect(file[:owner]).to eq(51)
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
+
+ file = generate_file_resource(:path => '/path')
+ expect(file[:group]).to eq(61)
end
end
describe "on Windows", :as_platform => :windows do
it "should omit the owner" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == nil }
- @dler.file
+ file = generate_file_resource(:path => 'C:/path')
+
+ expect(file[:owner]).to be_nil
end
it "should omit the group" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == nil }
- @dler.file
+ file = generate_file_resource(:path => 'C:/path')
+
+ expect(file[:group]).to be_nil
end
end
it "should always force the download" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true }
- @dler.file
+ file = generate_file_resource
+
+ expect(file[:force]).to be_true
end
it "should never back up when downloading" do
- Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false }
- @dler.file
+ file = generate_file_resource
+
+ expect(file[:backup]).to be_false
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
+ file = generate_file_resource(:ignore => '.svn')
+
+ expect(file[:ignore]).to eq(['.svn'])
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
+ file = generate_file_resource(:ignore => '.svn CVS')
+
+ expect(file[:ignore]).to eq(['.svn', 'CVS'])
end
end
@@ -142,25 +174,6 @@ describe Puppet::Configurer::Downloader do
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 using the `configtimeout` setting" do
- Puppet[:configtimeout] = 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
@@ -172,8 +185,6 @@ describe Puppet::Configurer::Downloader do
@dler.expects(:catalog).returns(catalog)
catalog.expects(:apply).yields(trans)
- Timeout.expects(:timeout).yields
-
resource = mock 'resource'
resource.expects(:[]).with(:path).returns "/changed/file"
@@ -189,8 +200,6 @@ describe Puppet::Configurer::Downloader do
@dler.expects(:catalog).returns(catalog)
catalog.expects(:apply).yields(trans)
- Timeout.expects(:timeout).yields
-
resource = mock 'resource'
resource.expects(:[]).with(:path).returns "/changed/file"
@@ -203,7 +212,9 @@ describe Puppet::Configurer::Downloader do
it "should catch and log exceptions" do
Puppet.expects(:err)
- Timeout.stubs(:timeout).raises(Puppet::Error, "testing")
+ # The downloader creates a new catalog for each apply, and really the only object
+ # that it is possible to stub for the purpose of generating a puppet error
+ Puppet::Resource::Catalog.any_instance.stubs(:apply).raises(Puppet::Error, "testing")
lambda { @dler.evaluate }.should_not raise_error
end
diff --git a/spec/unit/configurer/plugin_handler_spec.rb b/spec/unit/configurer/plugin_handler_spec.rb
index 295629481..a80236888 100755
--- a/spec/unit/configurer/plugin_handler_spec.rb
+++ b/spec/unit/configurer/plugin_handler_spec.rb
@@ -3,37 +3,37 @@ 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
+ let(:factory) { Puppet::Configurer::DownloaderFactory.new }
+ let(:pluginhandler) { Puppet::Configurer::PluginHandler.new(factory) }
+ let(:environment) { Puppet::Node::Environment.create(:myenv, []) }
+ before :each do
# 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 use an Agent Downloader, with the name, source, destination, ignore, and environment set correctly, to download plugins when downloading is enabled" do
- environment = Puppet::Node::Environment.create(:myenv, [])
- Puppet.features.stubs(:external_facts?).returns(:true)
- plugindest = File.expand_path("/tmp/pdest")
- Puppet[:pluginsource] = "psource"
- Puppet[:plugindest] = plugindest
- Puppet[:pluginsignore] = "pignore"
- Puppet[:pluginfactsource] = "psource"
- Puppet[:pluginfactdest] = plugindest
+ it "downloads plugins and facts" do
+ Puppet.features.stubs(:external_facts?).returns(true)
+
+ plugin_downloader = stub('plugin-downloader', :evaluate => [])
+ facts_downloader = stub('facts-downloader', :evaluate => [])
+
+ factory.expects(:create_plugin_downloader).returns(plugin_downloader)
+ factory.expects(:create_plugin_facts_downloader).returns(facts_downloader)
+
+ pluginhandler.download_plugins(environment)
+ end
+
+ it "skips facts if not enabled" do
+ Puppet.features.stubs(:external_facts?).returns(false)
- downloader = mock 'downloader'
- Puppet::Configurer::Downloader.expects(:new).with("pluginfacts", plugindest, "psource", "pignore", environment).returns downloader
- Puppet::Configurer::Downloader.expects(:new).with("plugin", plugindest, "psource", "pignore", environment).returns downloader
+ plugin_downloader = stub('plugin-downloader', :evaluate => [])
- downloader.stubs(:evaluate).returns([])
- downloader.expects(:evaluate).twice
+ factory.expects(:create_plugin_downloader).returns(plugin_downloader)
+ factory.expects(:create_plugin_facts_downloader).never
- @pluginhandler.download_plugins(environment)
+ pluginhandler.download_plugins(environment)
end
end
diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb
index ecbedec28..093990591 100755
--- a/spec/unit/configurer_spec.rb
+++ b/spec/unit/configurer_spec.rb
@@ -12,10 +12,6 @@ describe Puppet::Configurer do
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
diff --git a/spec/unit/defaults_spec.rb b/spec/unit/defaults_spec.rb
index f86283a64..0d141d91c 100644
--- a/spec/unit/defaults_spec.rb
+++ b/spec/unit/defaults_spec.rb
@@ -41,4 +41,34 @@ describe "Defaults" do
end
end
end
+
+ describe 'cfacter' do
+
+ before :each do
+ Facter.reset
+ end
+
+ it 'should default to false' do
+ Puppet.settings[:cfacter].should be_false
+ end
+
+ it 'should raise an error if cfacter is not installed' do
+ Puppet.features.stubs(:cfacter?).returns false
+ lambda { Puppet.settings[:cfacter] = true }.should raise_exception ArgumentError, 'cfacter version 0.2.0 or later is not installed.'
+ end
+
+ it 'should raise an error if facter has already evaluated facts' do
+ Facter[:facterversion]
+ Puppet.features.stubs(:cfacter?).returns true
+ lambda { Puppet.settings[:cfacter] = true }.should raise_exception ArgumentError, 'facter has already evaluated facts.'
+ end
+
+ it 'should initialize cfacter when set to true' do
+ Puppet.features.stubs(:cfacter?).returns true
+ CFacter = mock
+ CFacter.stubs(:initialize)
+ Puppet.settings[:cfacter] = true
+ end
+
+ end
end
diff --git a/spec/unit/face/certificate_request_spec.rb b/spec/unit/face/certificate_request_spec.rb
deleted file mode 100755
index 60917254f..000000000
--- a/spec/unit/face/certificate_request_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /usr/bin/env ruby
-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
deleted file mode 100755
index b833fb718..000000000
--- a/spec/unit/face/certificate_revocation_list_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /usr/bin/env ruby
-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/config_spec.rb b/spec/unit/face/config_spec.rb
index f42844827..2c43b33b7 100755
--- a/spec/unit/face/config_spec.rb
+++ b/spec/unit/face/config_spec.rb
@@ -92,6 +92,7 @@ basemodulepath = #{File.expand_path("/some/base")}
end
it "prints the default configured env settings for an env that does not exist" do
+ pending "This case no longer exists because Application will through an error before we even get here because of the non-existent environment"
Puppet[:environment] = 'doesnotexist'
FS.overlay(
@@ -125,7 +126,7 @@ basemodulepath = #{File.expand_path("/some/base")}
CONF
end
- it_behaves_like :config_printing_a_section
+ it_behaves_like :config_printing_a_section, nil
end
context "from master section" do
diff --git a/spec/unit/face/key_spec.rb b/spec/unit/face/key_spec.rb
deleted file mode 100755
index 6d9519825..000000000
--- a/spec/unit/face/key_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /usr/bin/env ruby
-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/module/build_spec.rb b/spec/unit/face/module/build_spec.rb
index 02bbb7e9d..72ba5e1f1 100644
--- a/spec/unit/face/module/build_spec.rb
+++ b/spec/unit/face/module/build_spec.rb
@@ -25,7 +25,7 @@ describe "puppet module build" do
it "if current directory or parents contain no module root, should return exception" do
Dir.expects(:pwd).returns('/a/b/c')
Puppet::ModuleTool.expects(:find_module_root).returns(nil)
- expect { subject.build }.to raise_error RuntimeError, "Unable to find module root at /a/b/c or parent directories"
+ expect { subject.build }.to raise_error RuntimeError, "Unable to find metadata.json or Modulefile in module root /a/b/c or parent directories. See <http://links.puppetlabs.com/modulefile> for required file format."
end
end
@@ -39,7 +39,7 @@ describe "puppet module build" do
it "if path is not a module root should raise exception" do
Puppet::ModuleTool.expects(:is_module_root?).with('/a/b/c').returns(false)
- expect { subject.build('/a/b/c') }.to raise_error RuntimeError, "Unable to find module root at /a/b/c"
+ expect { subject.build('/a/b/c') }.to raise_error RuntimeError, "Unable to find metadata.json or Modulefile in module root /a/b/c. See <http://links.puppetlabs.com/modulefile> for required file format."
end
end
diff --git a/spec/unit/face/module/install_spec.rb b/spec/unit/face/module/install_spec.rb
index 528c7822a..3f0b27684 100644
--- a/spec/unit/face/module/install_spec.rb
+++ b/spec/unit/face/module/install_spec.rb
@@ -7,19 +7,19 @@ describe "puppet module install" do
describe "action" do
let(:name) { stub(:name) }
- let(:target_dir) { stub(:target_dir) }
- let(:target_path) { stub(:target_path) }
- let(:install_dir) { stub(:install_dir) }
+ let(:target_dir) { tmpdir('module install face action') }
let(:options) { { :target_dir => target_dir } }
it 'should invoke the Installer app' do
- args = [ name, install_dir, options ]
-
Puppet::ModuleTool.expects(:set_option_defaults).with(options)
+ Puppet::ModuleTool::Applications::Installer.expects(:run).with do |*args|
+ mod, target, opts = args
- Pathname.expects(:new).with(target_dir).returns(target_path)
- Puppet::ModuleTool::InstallDirectory.expects(:new).with(target_path).returns(install_dir)
- Puppet::ModuleTool::Applications::Installer.expects(:run).with(*args)
+ expect(mod).to eql(name)
+ expect(opts).to eql(options)
+ expect(target).to be_a(Puppet::ModuleTool::InstallDirectory)
+ expect(target.target).to eql(Pathname.new(target_dir))
+ end
Puppet::Face[:module, :current].install(name, options)
end
diff --git a/spec/unit/face/parser_spec.rb b/spec/unit/face/parser_spec.rb
index 9b1b07a47..e9ba07a2d 100644
--- a/spec/unit/face/parser_spec.rb
+++ b/spec/unit/face/parser_spec.rb
@@ -8,58 +8,96 @@ describe Puppet::Face[:parser, :current] do
let(:parser) { Puppet::Face[:parser, :current] }
- context "from an interactive terminal" do
- before :each do
- from_an_interactive_terminal
+ context "validate" do
+ context "from an interactive terminal" do
+ before :each do
+ from_an_interactive_terminal
+ end
+
+ it "validates the configured site manifest when no files are given" do
+ manifest = file_containing('site.pp', "{ invalid =>")
+
+ configured_environment = Puppet::Node::Environment.create(:default, [], manifest)
+ Puppet.override(:current_environment => configured_environment) do
+ expect { parser.validate() }.to exit_with(1)
+ end
+ end
+
+ it "validates the given file" do
+ manifest = file_containing('site.pp', "{ invalid =>")
+
+ expect { parser.validate(manifest) }.to exit_with(1)
+ end
+
+ it "runs error free when there are no validation errors" do
+ manifest = file_containing('site.pp', "notify { valid: }")
+
+ parser.validate(manifest)
+ end
+
+ it "reports missing files" do
+ expect do
+ parser.validate("missing.pp")
+ end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m)
+ end
+
+ it "parses supplied manifest files in the context of a directory environment" do
+ manifest = file_containing('test.pp', "{ invalid =>")
+
+ env = Puppet::Node::Environment.create(:special, [])
+ env_loader = Puppet::Environments::Static.new(env)
+ Puppet.override({:environments => env_loader, :current_environment => env}) do
+ expect { parser.validate(manifest) }.to exit_with(1)
+ end
+
+ expect(@logs.join).to match(/environment special.*Syntax error at '\{'/)
+ end
+
end
- it "validates the configured site manifest when no files are given" do
- manifest = file_containing('site.pp', "{ invalid =>")
+ it "validates the contents of STDIN when no files given and STDIN is not a tty" do
+ from_a_piped_input_of("{ invalid =>")
- configured_environment = Puppet::Node::Environment.create(:default, [], manifest)
- Puppet.override(:current_environment => configured_environment) do
+ Puppet.override(:current_environment => Puppet::Node::Environment.create(:special, [])) do
expect { parser.validate() }.to exit_with(1)
end
end
+ end
- it "validates the given file" do
- manifest = file_containing('site.pp', "{ invalid =>")
-
- expect { parser.validate(manifest) }.to exit_with(1)
+ context "dump" do
+ it "prints the AST of the passed expression" do
+ expect(parser.dump({ :e => 'notice hi' })).to eq("(invoke notice hi)\n")
end
- it "runs error free when there are no validation errors" do
- manifest = file_containing('site.pp', "notify { valid: }")
+ it "prints the AST of the code read from the passed files" do
+ first_manifest = file_containing('site.pp', "notice hi")
+ second_manifest = file_containing('site2.pp', "notice bye")
- parser.validate(manifest)
+ output = parser.dump(first_manifest, second_manifest)
+
+ expect(output).to match(/site\.pp.*\(invoke notice hi\)/)
+ expect(output).to match(/site2\.pp.*\(invoke notice bye\)/)
end
- it "reports missing files" do
- expect do
- parser.validate("missing.pp")
- end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m)
+ it "informs the user of files that don't exist" do
+ expect(parser.dump('does_not_exist_here.pp')).to match(/did not exist:\s*does_not_exist_here\.pp/m)
end
- it "parses supplied manifest files in the context of a directory environment" do
- manifest = file_containing('test.pp', "{ invalid =>")
+ it "prints the AST of STDIN when no files given and STDIN is not a tty" do
+ from_a_piped_input_of("notice hi")
- env_loader = Puppet::Environments::Static.new(
- Puppet::Node::Environment.create(:special, [])
- )
- Puppet.override(:environments => env_loader) do
- Puppet[:environment] = 'special'
- expect { parser.validate(manifest) }.to exit_with(1)
+ Puppet.override(:current_environment => Puppet::Node::Environment.create(:special, [])) do
+ expect(parser.dump()).to eq("(invoke notice hi)\n")
end
-
- expect(@logs.join).to match(/environment special.*Syntax error at '\{'/)
end
- end
-
- it "validates the contents of STDIN when no files given and STDIN is not a tty" do
- from_a_piped_input_of("{ invalid =>")
+ it "logs an error if the input cannot be parsed" do
+ output = parser.dump({ :e => '{ invalid =>' })
- expect { parser.validate() }.to exit_with(1)
+ expect(output).to eq("")
+ expect(@logs[0].message).to eq("Syntax error at end of file")
+ expect(@logs[0].level).to eq(:err)
+ end
end
def from_an_interactive_terminal
diff --git a/spec/unit/face/report_spec.rb b/spec/unit/face/report_spec.rb
deleted file mode 100755
index 6fcb49a64..000000000
--- a/spec/unit/face/report_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /usr/bin/env ruby
-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
deleted file mode 100755
index 031d78116..000000000
--- a/spec/unit/face/resource_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /usr/bin/env ruby
-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
deleted file mode 100755
index 5713a01fb..000000000
--- a/spec/unit/face/resource_type_spec.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-#! /usr/bin/env ruby
-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/file_bucket/file_spec.rb b/spec/unit/file_bucket/file_spec.rb
index 370ba1eeb..500e81685 100755
--- a/spec/unit/file_bucket/file_spec.rb
+++ b/spec/unit/file_bucket/file_spec.rb
@@ -14,7 +14,7 @@ describe Puppet::FileBucket::File, :uses_checksums => true do
end
it "accepts s and pson" do
- expect(Puppet::FileBucket::File.supported_formats).to include(:s, :pson)
+ expect(Puppet::FileBucket::File.supported_formats).to include(:s, :pson)
end
describe "making round trips through network formats" do
@@ -34,7 +34,7 @@ describe Puppet::FileBucket::File, :uses_checksums => true do
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$/)
+ expect { Puppet::FileBucket::File.new(5) }.to raise_error(ArgumentError, /contents must be a String or Pathname, got a Fixnum$/)
end
it "should complain about options other than :bucket_path" do
diff --git a/spec/unit/file_system/tempfile_spec.rb b/spec/unit/file_system/tempfile_spec.rb
deleted file mode 100644
index eb13b0406..000000000
--- a/spec/unit/file_system/tempfile_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require 'spec_helper'
-
-describe Puppet::FileSystem::Tempfile do
- it "makes the name of the file available" do
- Puppet::FileSystem::Tempfile.open('foo') do |file|
- expect(file.path).to match(/foo/)
- end
- end
-
- it "provides a writeable file" do
- Puppet::FileSystem::Tempfile.open('foo') do |file|
- file.write("stuff")
- file.flush
-
- expect(Puppet::FileSystem.read(file.path)).to eq("stuff")
- end
- end
-
- it "returns the value of the block" do
- the_value = Puppet::FileSystem::Tempfile.open('foo') do |file|
- "my value"
- end
-
- expect(the_value).to eq("my value")
- end
-
- it "unlinks the temporary file" do
- filename = Puppet::FileSystem::Tempfile.open('foo') do |file|
- file.path
- end
-
- expect(Puppet::FileSystem.exist?(filename)).to be_false
- end
-
- it "unlinks the temporary file even if the block raises an error" do
- filename = nil
-
- begin
- Puppet::FileSystem::Tempfile.open('foo') do |file|
- filename = file.path
- raise "error!"
- end
- rescue
- end
-
- expect(Puppet::FileSystem.exist?(filename)).to be_false
- end
-end
diff --git a/spec/unit/file_system/uniquefile_spec.rb b/spec/unit/file_system/uniquefile_spec.rb
new file mode 100644
index 000000000..1268581fa
--- /dev/null
+++ b/spec/unit/file_system/uniquefile_spec.rb
@@ -0,0 +1,184 @@
+require 'spec_helper'
+
+describe Puppet::FileSystem::Uniquefile do
+ it "makes the name of the file available" do
+ Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|
+ expect(file.path).to match(/foo/)
+ end
+ end
+
+ it "provides a writeable file" do
+ Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|
+ file.write("stuff")
+ file.flush
+
+ expect(Puppet::FileSystem.read(file.path)).to eq("stuff")
+ end
+ end
+
+ it "returns the value of the block" do
+ the_value = Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|
+ "my value"
+ end
+
+ expect(the_value).to eq("my value")
+ end
+
+ it "unlinks the temporary file" do
+ filename = Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|
+ file.path
+ end
+
+ expect(Puppet::FileSystem.exist?(filename)).to be_false
+ end
+
+ it "unlinks the temporary file even if the block raises an error" do
+ filename = nil
+
+ begin
+ Puppet::FileSystem::Uniquefile.open_tmp('foo') do |file|
+ filename = file.path
+ raise "error!"
+ end
+ rescue
+ end
+
+ expect(Puppet::FileSystem.exist?(filename)).to be_false
+ end
+
+
+ context "Ruby 1.9.3 Tempfile tests" do
+ # the remaining tests in this file are ported directly from the ruby 1.9.3 source,
+ # since most of this file was ported from there
+ # see: https://github.com/ruby/ruby/blob/v1_9_3_547/test/test_tempfile.rb
+
+ def tempfile(*args, &block)
+ t = Puppet::FileSystem::Uniquefile.new(*args, &block)
+ @tempfile = (t unless block)
+ end
+
+ after(:each) do
+ if @tempfile
+ @tempfile.close!
+ end
+ end
+
+ it "creates tempfiles" do
+ t = tempfile("foo")
+ path = t.path
+ t.write("hello world")
+ t.close
+ expect(File.read(path)).to eq("hello world")
+ end
+
+ it "saves in tmpdir by default" do
+ t = tempfile("foo")
+ expect(Dir.tmpdir).to eq(File.dirname(t.path))
+ end
+
+ it "saves in given directory" do
+ subdir = File.join(Dir.tmpdir, "tempfile-test-#{rand}")
+ Dir.mkdir(subdir)
+ begin
+ tempfile = Tempfile.new("foo", subdir)
+ tempfile.close
+ begin
+ expect(subdir).to eq(File.dirname(tempfile.path))
+ ensure
+ tempfile.unlink
+ end
+ ensure
+ Dir.rmdir(subdir)
+ end
+ end
+
+ it "supports basename" do
+ t = tempfile("foo")
+ expect(File.basename(t.path)).to match(/^foo/)
+ end
+
+ it "supports basename with suffix" do
+ t = tempfile(["foo", ".txt"])
+ expect(File.basename(t.path)).to match(/^foo/)
+ expect(File.basename(t.path)).to match(/\.txt$/)
+ end
+
+ it "supports unlink" do
+ t = tempfile("foo")
+ path = t.path
+ t.close
+ expect(File.exist?(path)).to eq(true)
+ t.unlink
+ expect(File.exist?(path)).to eq(false)
+ expect(t.path).to eq(nil)
+ end
+
+ it "supports closing" do
+ t = tempfile("foo")
+ expect(t.closed?).to eq(false)
+ t.close
+ expect(t.closed?).to eq(true)
+ end
+
+ it "supports closing and unlinking via boolean argument" do
+ t = tempfile("foo")
+ path = t.path
+ t.close(true)
+ expect(t.closed?).to eq(true)
+ expect(t.path).to eq(nil)
+ expect(File.exist?(path)).to eq(false)
+ end
+
+ context "on unix platforms", :unless => Puppet.features.microsoft_windows? do
+ it "close doesn't unlink if already unlinked" do
+ t = tempfile("foo")
+ path = t.path
+ t.unlink
+ File.open(path, "w").close
+ begin
+ t.close(true)
+ expect(File.exist?(path)).to eq(true)
+ ensure
+ File.unlink(path) rescue nil
+ end
+ end
+ end
+
+ it "supports close!" do
+ t = tempfile("foo")
+ path = t.path
+ t.close!
+ expect(t.closed?).to eq(true)
+ expect(t.path).to eq(nil)
+ expect(File.exist?(path)).to eq(false)
+ end
+
+ context "on unix platforms", :unless => Puppet.features.microsoft_windows? do
+ it "close! doesn't unlink if already unlinked" do
+ t = tempfile("foo")
+ path = t.path
+ t.unlink
+ File.open(path, "w").close
+ begin
+ t.close!
+ expect(File.exist?(path)).to eq(true)
+ ensure
+ File.unlink(path) rescue nil
+ end
+ end
+ end
+
+ it "close does not make path nil" do
+ t = tempfile("foo")
+ t.close
+ expect(t.path.nil?).to eq(false)
+ end
+
+ it "close flushes buffer" do
+ t = tempfile("foo")
+ t.write("hello")
+ t.close
+ expect(File.size(t.path)).to eq(5)
+ end
+ end
+end
diff --git a/spec/unit/forge/errors_spec.rb b/spec/unit/forge/errors_spec.rb
index 057bf014a..fc1ac1a0e 100644
--- a/spec/unit/forge/errors_spec.rb
+++ b/spec/unit/forge/errors_spec.rb
@@ -47,15 +47,14 @@ Could not connect to http://fake.com:1111
let(:exception) { subject.new(:uri => 'http://fake.com:1111', :response => response, :input => 'user/module') }
it 'should return a valid single line error' do
- exception.message.should == 'Could not execute operation for \'user/module\'. Detail: 404 not found.'
+ exception.message.should == 'Request to Puppet Forge failed. Detail: 404 not found.'
end
it 'should return a valid multiline error' do
exception.multiline.should == <<-eos.chomp
-Could not execute operation for 'user/module'
+Request to Puppet Forge failed.
The server being queried was http://fake.com:1111
The HTTP response we received was '404 not found'
- Check the author and module names are correct.
eos
end
end
@@ -64,16 +63,15 @@ Could not execute operation for 'user/module'
let(:exception) { subject.new(:uri => 'http://fake.com:1111', :response => response, :input => 'user/module', :message => 'no such module') }
it 'should return a valid single line error' do
- exception.message.should == 'Could not execute operation for \'user/module\'. Detail: no such module / 404 not found.'
+ exception.message.should == 'Request to Puppet Forge failed. Detail: no such module / 404 not found.'
end
it 'should return a valid multiline error' do
exception.multiline.should == <<-eos.chomp
-Could not execute operation for 'user/module'
+Request to Puppet Forge failed.
The server being queried was http://fake.com:1111
The HTTP response we received was '404 not found'
The message we received said 'no such module'
- Check the author and module names are correct.
eos
end
end
diff --git a/spec/unit/forge/module_release_spec.rb b/spec/unit/forge/module_release_spec.rb
index fbf5e157a..b763f0833 100644
--- a/spec/unit/forge/module_release_spec.rb
+++ b/spec/unit/forge/module_release_spec.rb
@@ -8,57 +8,14 @@ describe Puppet::Forge::ModuleRelease do
let(:agent) { "Test/1.0" }
let(:repository) { Puppet::Forge::Repository.new('http://fake.com', agent) }
let(:ssl_repository) { Puppet::Forge::Repository.new('https://fake.com', agent) }
-
- let(:release_json) do
- <<-EOF
- {
- "uri": "/v3/releases/puppetlabs-stdlib-4.1.0",
- "module": {
- "uri": "/v3/modules/puppetlabs-stdlib",
- "name": "stdlib",
- "owner": {
- "uri": "/v3/users/puppetlabs",
- "username": "puppetlabs",
- "gravatar_id": "fdd009b7c1ec96e088b389f773e87aec"
- }
- },
- "version": "4.1.0",
- "metadata": {
- "types": [ ],
- "license": "Apache 2.0",
- "checksums": { },
- "version": "4.1.0",
- "description": "Standard Library for Puppet Modules",
- "source": "git://github.com/puppetlabs/puppetlabs-stdlib.git",
- "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib",
- "summary": "Puppet Module Standard Library",
- "dependencies": [
-
- ],
- "author": "puppetlabs",
- "name": "puppetlabs-stdlib"
- },
- "tags": [
- "puppetlabs",
- "library",
- "stdlib",
- "standard",
- "stages"
- ],
- "file_uri": "/v3/files/puppetlabs-stdlib-4.1.0.tar.gz",
- "file_size": 67586,
- "file_md5": "bbf919d7ee9d278d2facf39c25578bf8",
- "downloads": 610751,
- "readme": "",
- "changelog": "",
- "license": "",
- "created_at": "2013-05-13 08:31:19 -0700",
- "updated_at": "2013-05-13 08:31:19 -0700",
- "deleted_at": null
- }
- EOF
- end
-
+ let(:api_version) { "v3" }
+ let(:module_author) { "puppetlabs" }
+ let(:module_name) { "stdlib" }
+ let(:module_version) { "4.1.0" }
+ let(:module_full_name) { "#{module_author}-#{module_name}" }
+ let(:module_full_name_versioned) { "#{module_full_name}-#{module_version}" }
+ let(:module_md5) { "bbf919d7ee9d278d2facf39c25578bf8" }
+ let(:uri) { " "}
let(:release) { Puppet::Forge::ModuleRelease.new(ssl_repository, JSON.parse(release_json)) }
let(:mock_file) {
@@ -69,63 +26,195 @@ describe Puppet::Forge::ModuleRelease do
let(:mock_dir) { '/tmp' }
- def mock_digest_file_with_md5(md5)
- Digest::MD5.stubs(:file).returns(stub(:hexdigest => md5))
- end
-
- describe '#prepare' do
- before :each do
- release.stubs(:tmpfile).returns(mock_file)
- release.stubs(:tmpdir).returns(mock_dir)
+ shared_examples 'a module release' do
+ def mock_digest_file_with_md5(md5)
+ Digest::MD5.stubs(:file).returns(stub(:hexdigest => md5))
end
- it 'should call sub methods with correct params' do
- release.expects(:download).with('/v3/files/puppetlabs-stdlib-4.1.0.tar.gz', mock_file)
- release.expects(:validate_checksum).with(mock_file, 'bbf919d7ee9d278d2facf39c25578bf8')
- release.expects(:unpack).with(mock_file, mock_dir)
+ describe '#prepare' do
+ before :each do
+ release.stubs(:tmpfile).returns(mock_file)
+ release.stubs(:tmpdir).returns(mock_dir)
+ end
+
+ it 'should call sub methods with correct params' do
+ release.expects(:download).with("/#{api_version}/files/#{module_full_name_versioned}.tar.gz", mock_file)
+ release.expects(:validate_checksum).with(mock_file, module_md5)
+ release.expects(:unpack).with(mock_file, mock_dir)
- release.prepare
+ release.prepare
+ end
end
- end
- describe '#tmpfile' do
+ describe '#tmpfile' do
- # This is impossible to test under Ruby 1.8.x, but should also occur there.
- it 'should be opened in binary mode', :unless => RUBY_VERSION >= '1.8.7' do
- Puppet::Forge::Cache.stubs(:base_path).returns(Dir.tmpdir)
- release.send(:tmpfile).binmode?.should be_true
+ # This is impossible to test under Ruby 1.8.x, but should also occur there.
+ it 'should be opened in binary mode', :unless => RUBY_VERSION >= '1.8.7' do
+ Puppet::Forge::Cache.stubs(:base_path).returns(Dir.tmpdir)
+ release.send(:tmpfile).binmode?.should be_true
+ end
end
- end
- describe '#download' do
- it 'should call make_http_request with correct params' do
- # valid URI comes from file_uri in JSON blob above
- ssl_repository.expects(:make_http_request).with('/v3/files/puppetlabs-stdlib-4.1.0.tar.gz', mock_file).returns(mock_file)
+ describe '#download' do
+ it 'should call make_http_request with correct params' do
+ # valid URI comes from file_uri in JSON blob above
+ ssl_repository.expects(:make_http_request).with("/#{api_version}/files/#{module_full_name_versioned}.tar.gz", mock_file).returns(stub(:body => '{}', :code => '200'))
+
+ release.send(:download, "/#{api_version}/files/#{module_full_name_versioned}.tar.gz", mock_file)
+ end
- release.send(:download, '/v3/files/puppetlabs-stdlib-4.1.0.tar.gz', mock_file)
+ it 'should raise a response error when it receives an error from forge' do
+ ssl_repository.stubs(:make_http_request).returns(stub(:body => '{"errors": ["error"]}', :code => '500', :message => 'server error'))
+ expect { release.send(:download, "/some/path", mock_file)}. to raise_error Puppet::Forge::Errors::ResponseError
+ end
end
- end
- describe '#verify_checksum' do
- it 'passes md5 check when valid' do
- # valid hash comes from file_md5 in JSON blob above
- mock_digest_file_with_md5('bbf919d7ee9d278d2facf39c25578bf8')
+ describe '#verify_checksum' do
+ it 'passes md5 check when valid' do
+ # valid hash comes from file_md5 in JSON blob above
+ mock_digest_file_with_md5(module_md5)
- release.send(:validate_checksum, mock_file, 'bbf919d7ee9d278d2facf39c25578bf8')
+ release.send(:validate_checksum, mock_file, module_md5)
+ end
+
+ it 'fails md5 check when invalid' do
+ mock_digest_file_with_md5('ffffffffffffffffffffffffffffffff')
+
+ expect { release.send(:validate_checksum, mock_file, module_md5) }.to raise_error(RuntimeError, /did not match expected checksum/)
+ end
end
- it 'fails md5 check when invalid' do
- mock_digest_file_with_md5('ffffffffffffffffffffffffffffffff')
+ describe '#unpack' do
+ it 'should call unpacker with correct params' do
+ Puppet::ModuleTool::Applications::Unpacker.expects(:unpack).with(mock_file.path, mock_dir).returns(true)
+
+ release.send(:unpack, mock_file, mock_dir)
+ end
+ end
+ end
- expect { release.send(:validate_checksum, mock_file, 'bbf919d7ee9d278d2facf39c25578bf8') }.to raise_error(RuntimeError, /did not match expected checksum/)
+ context 'standard forge module' do
+ let(:release_json) do %Q{
+ {
+ "uri": "/#{api_version}/releases/#{module_full_name_versioned}",
+ "module": {
+ "uri": "/#{api_version}/modules/#{module_full_name}",
+ "name": "#{module_name}",
+ "owner": {
+ "uri": "/#{api_version}/users/#{module_author}",
+ "username": "#{module_author}",
+ "gravatar_id": "fdd009b7c1ec96e088b389f773e87aec"
+ }
+ },
+ "version": "#{module_version}",
+ "metadata": {
+ "types": [ ],
+ "license": "Apache 2.0",
+ "checksums": { },
+ "version": "#{module_version}",
+ "description": "Standard Library for Puppet Modules",
+ "source": "git://github.com/puppetlabs/puppetlabs-stdlib.git",
+ "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib",
+ "summary": "Puppet Module Standard Library",
+ "dependencies": [
+
+ ],
+ "author": "#{module_author}",
+ "name": "#{module_full_name}"
+ },
+ "tags": [
+ "puppetlabs",
+ "library",
+ "stdlib",
+ "standard",
+ "stages"
+ ],
+ "file_uri": "/#{api_version}/files/#{module_full_name_versioned}.tar.gz",
+ "file_size": 67586,
+ "file_md5": "#{module_md5}",
+ "downloads": 610751,
+ "readme": "",
+ "changelog": "",
+ "license": "",
+ "created_at": "2013-05-13 08:31:19 -0700",
+ "updated_at": "2013-05-13 08:31:19 -0700",
+ "deleted_at": null
+ }
+ }
end
+
+ it_behaves_like 'a module release'
end
- describe '#unpack' do
- it 'should call unpacker with correct params' do
- Puppet::ModuleTool::Applications::Unpacker.expects(:unpack).with(mock_file.path, mock_dir).returns(true)
+ context 'forge module with no dependencies field' do
+ let(:release_json) do %Q{
+ {
+ "uri": "/#{api_version}/releases/#{module_full_name_versioned}",
+ "module": {
+ "uri": "/#{api_version}/modules/#{module_full_name}",
+ "name": "#{module_name}",
+ "owner": {
+ "uri": "/#{api_version}/users/#{module_author}",
+ "username": "#{module_author}",
+ "gravatar_id": "fdd009b7c1ec96e088b389f773e87aec"
+ }
+ },
+ "version": "#{module_version}",
+ "metadata": {
+ "types": [ ],
+ "license": "Apache 2.0",
+ "checksums": { },
+ "version": "#{module_version}",
+ "description": "Standard Library for Puppet Modules",
+ "source": "git://github.com/puppetlabs/puppetlabs-stdlib.git",
+ "project_page": "https://github.com/puppetlabs/puppetlabs-stdlib",
+ "summary": "Puppet Module Standard Library",
+ "author": "#{module_author}",
+ "name": "#{module_full_name}"
+ },
+ "tags": [
+ "puppetlabs",
+ "library",
+ "stdlib",
+ "standard",
+ "stages"
+ ],
+ "file_uri": "/#{api_version}/files/#{module_full_name_versioned}.tar.gz",
+ "file_size": 67586,
+ "file_md5": "#{module_md5}",
+ "downloads": 610751,
+ "readme": "",
+ "changelog": "",
+ "license": "",
+ "created_at": "2013-05-13 08:31:19 -0700",
+ "updated_at": "2013-05-13 08:31:19 -0700",
+ "deleted_at": null
+ }
+ }
+ end
+
+ it_behaves_like 'a module release'
+ end
- release.send(:unpack, mock_file, mock_dir)
+ context 'forge module with the minimal set of fields' do
+ let(:release_json) do %Q{
+ {
+ "uri": "/#{api_version}/releases/#{module_full_name_versioned}",
+ "module": {
+ "uri": "/#{api_version}/modules/#{module_full_name}",
+ "name": "#{module_name}"
+ },
+ "metadata": {
+ "version": "#{module_version}",
+ "name": "#{module_full_name}"
+ },
+ "file_uri": "/#{api_version}/files/#{module_full_name_versioned}.tar.gz",
+ "file_size": 67586,
+ "file_md5": "#{module_md5}"
+ }
+ }
end
+
+ it_behaves_like 'a module release'
end
end
diff --git a/spec/unit/forge/repository_spec.rb b/spec/unit/forge/repository_spec.rb
index 04b10b166..4ac77ecb8 100644
--- a/spec/unit/forge/repository_spec.rb
+++ b/spec/unit/forge/repository_spec.rb
@@ -80,6 +80,20 @@ describe Puppet::Forge::Repository do
request['User-Agent'].should =~ /\bRuby\b/
end
+ it "Does not set Authorization header by default" do
+ Puppet.features.stubs(:pe_license?).returns(false)
+ Puppet[:forge_authorization] = nil
+ request = repository.get_request_object("the_path")
+ request['Authorization'].should == nil
+ end
+
+ it "Sets Authorization header from config" do
+ token = 'bearer some token'
+ Puppet[:forge_authorization] = token
+ request = repository.get_request_object("the_path")
+ request['Authorization'].should == token
+ end
+
it "escapes the received URI" do
unescaped_uri = "héllo world !! ç à"
performs_an_http_request do |http|
@@ -97,7 +111,7 @@ describe Puppet::Forge::Repository do
proxy = mock("http proxy")
proxy_class.expects(:new).with("fake.com", 80).returns(proxy)
proxy.expects(:start).yields(http).returns(result)
- Net::HTTP.expects(:Proxy).with("proxy", 1234).returns(proxy_class)
+ Net::HTTP.expects(:Proxy).with("proxy", 1234, nil, nil).returns(proxy_class)
end
def performs_an_https_request(result = nil, &block)
@@ -111,7 +125,94 @@ describe Puppet::Forge::Repository do
proxy.expects(:use_ssl=).with(true)
proxy.expects(:cert_store=)
proxy.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
- Net::HTTP.expects(:Proxy).with("proxy", 1234).returns(proxy_class)
+ Net::HTTP.expects(:Proxy).with("proxy", 1234, nil, nil).returns(proxy_class)
+ end
+ end
+
+ describe "making a request against an authentiated proxy" do
+ before :each do
+ authenticated_proxy_settings_of("proxy", 1234, 'user1', 'password')
+ end
+
+ it "returns the result object from the request" do
+ result = "#{Object.new}"
+
+ performs_an_authenticated_http_request result do |http|
+ http.expects(:request).with(responds_with(:path, "the_path"))
+ end
+
+ repository.make_http_request("the_path").should == result
+ end
+
+ it 'returns the result object from a request with ssl' do
+ result = "#{Object.new}"
+ performs_an_authenticated_https_request result do |http|
+ http.expects(:request).with(responds_with(:path, "the_path"))
+ end
+
+ ssl_repository.make_http_request("the_path").should == result
+ end
+
+ it 'return a valid exception when there is an SSL verification problem' do
+ performs_an_authenticated_https_request "#{Object.new}" do |http|
+ http.expects(:request).with(responds_with(:path, "the_path")).raises OpenSSL::SSL::SSLError.new("certificate verify failed")
+ end
+
+ expect { ssl_repository.make_http_request("the_path") }.to raise_error Puppet::Forge::Errors::SSLVerifyError, 'Unable to verify the SSL certificate at https://fake.com'
+ end
+
+ it 'return a valid exception when there is a communication problem' do
+ performs_an_authenticated_http_request "#{Object.new}" do |http|
+ http.expects(:request).with(responds_with(:path, "the_path")).raises SocketError
+ end
+
+ expect { repository.make_http_request("the_path") }.
+ to raise_error Puppet::Forge::Errors::CommunicationError,
+ 'Unable to connect to the server at http://fake.com. Detail: SocketError.'
+ end
+
+ it "sets the user agent for the request" do
+ path = 'the_path'
+
+ request = repository.get_request_object(path)
+
+ request['User-Agent'].should =~ /\b#{agent}\b/
+ request['User-Agent'].should =~ /\bPuppet\b/
+ request['User-Agent'].should =~ /\bRuby\b/
+ end
+
+ it "escapes the received URI" do
+ unescaped_uri = "héllo world !! ç à"
+ performs_an_authenticated_http_request do |http|
+ http.expects(:request).with(responds_with(:path, URI.escape(unescaped_uri)))
+ end
+
+ repository.make_http_request(unescaped_uri)
+ end
+
+ def performs_an_authenticated_http_request(result = nil, &block)
+ http = mock("http client")
+ yield http
+
+ proxy_class = mock("http proxy class")
+ proxy = mock("http proxy")
+ proxy_class.expects(:new).with("fake.com", 80).returns(proxy)
+ proxy.expects(:start).yields(http).returns(result)
+ Net::HTTP.expects(:Proxy).with("proxy", 1234, "user1", "password").returns(proxy_class)
+ end
+
+ def performs_an_authenticated_https_request(result = nil, &block)
+ http = mock("http client")
+ yield http
+
+ proxy_class = mock("http proxy class")
+ proxy = mock("http proxy")
+ proxy_class.expects(:new).with("fake.com", 443).returns(proxy)
+ proxy.expects(:start).yields(http).returns(result)
+ proxy.expects(:use_ssl=).with(true)
+ proxy.expects(:cert_store=)
+ proxy.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
+ Net::HTTP.expects(:Proxy).with("proxy", 1234, "user1", "password").returns(proxy_class)
end
end
@@ -119,4 +220,11 @@ describe Puppet::Forge::Repository do
Puppet[:http_proxy_host] = host
Puppet[:http_proxy_port] = port
end
+
+ def authenticated_proxy_settings_of(host, port, user, password)
+ Puppet[:http_proxy_host] = host
+ Puppet[:http_proxy_port] = port
+ Puppet[:http_proxy_user] = user
+ Puppet[:http_proxy_password] = password
+ end
end
diff --git a/spec/unit/forge_spec.rb b/spec/unit/forge_spec.rb
index 96bf8d3be..eb7c56a3e 100644
--- a/spec/unit/forge_spec.rb
+++ b/spec/unit/forge_spec.rb
@@ -110,17 +110,38 @@ describe Puppet::Forge do
forge.search('bacula').should == search_results
end
+ context "when module_groups are defined" do
+ let(:release_response) do
+ releases = JSON.parse(http_response)
+ releases['results'] = []
+ JSON.dump(releases)
+ end
+
+ before :each do
+ repository_responds_with(stub(:body => release_response, :code => '200')).with {|uri| uri =~ /module_groups=foo/}
+ Puppet[:module_groups] = "foo"
+ end
+
+ it "passes module_groups with search" do
+ forge.search('bacula')
+ end
+
+ it "passes module_groups with fetch" do
+ forge.fetch('puppetlabs-bacula')
+ end
+ end
+
context "when the connection to the forge fails" do
before :each do
repository_responds_with(stub(:body => '{}', :code => '404', :message => "not found"))
end
it "raises an error for search" do
- expect { forge.search('bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, "Could not execute operation for 'bacula'. Detail: 404 not found."
+ expect { forge.search('bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, "Request to Puppet Forge failed. Detail: 404 not found."
end
it "raises an error for fetch" do
- expect { forge.fetch('puppetlabs/bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, "Could not execute operation for 'puppetlabs/bacula'. Detail: 404 not found."
+ expect { forge.fetch('puppetlabs/bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, "Request to Puppet Forge failed. Detail: 404 not found."
end
end
@@ -130,7 +151,22 @@ describe Puppet::Forge do
end
it "raises an error for fetch" do
- expect { forge.fetch('puppetlabs/bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, "Could not execute operation for 'puppetlabs/bacula'. Detail: 410 Gone."
+ expect { forge.fetch('puppetlabs/bacula') }.to raise_error Puppet::Forge::Errors::ResponseError, "Request to Puppet Forge failed. Detail: 410 Gone."
+ end
+ end
+
+ context "when the forge returns a module with unparseable dependencies" do
+ before :each do
+ response = JSON.parse(http_response)
+ release = response['results'][0]['current_release']
+ release['metadata']['dependencies'] = [{'name' => 'broken-garbage >= 1.0.0', 'version_requirement' => 'banana'}]
+ response['results'] = [release]
+ repository_responds_with(stub(:body => JSON.dump(response), :code => '200'))
+ end
+
+ it "ignores modules with unparseable dependencies" do
+ expect { result = forge.fetch('puppetlabs/bacula') }.to_not raise_error
+ expect { result.to be_empty }
end
end
end
diff --git a/spec/unit/functions/assert_type_spec.rb b/spec/unit/functions/assert_type_spec.rb
index d47f47e30..13b353401 100644
--- a/spec/unit/functions/assert_type_spec.rb
+++ b/spec/unit/functions/assert_type_spec.rb
@@ -37,8 +37,8 @@ describe 'the assert_type function' do
end.to raise_error(ArgumentError, Regexp.new(Regexp.escape(
"function 'assert_type' called with mis-matched arguments
expected one of:
- assert_type(Type type, Optional[Object] value) - arg count {2}
- assert_type(String type_string, Optional[Object] value) - arg count {2}
+ assert_type(Type type, Any value, Callable[Type, Type] block {0,1}) - arg count {2,3}
+ assert_type(String type_string, Any value, Callable[Type, Type] block {0,1}) - arg count {2,3}
actual:
assert_type(Integer, Integer) - arg count {2}")))
end
@@ -46,7 +46,13 @@ actual:
it 'allows the second arg to be undef/nil)' do
expect do
func.call({}, optional(String), nil)
- end.to_not raise_error(ArgumentError)
+ end.to_not raise_error
+ end
+
+ it 'can be called with a callable that receives a specific type' do
+ expected, actual = func.call({}, optional(String), 1, create_callable_2_args_unit)
+ expect(expected.to_s).to eql('Optional[String]')
+ expect(actual.to_s).to eql('Integer[1, 1]')
end
def optional(type_ref)
@@ -56,4 +62,17 @@ actual:
def type(type_ref)
Puppet::Pops::Types::TypeFactory.type_of(type_ref)
end
+
+ def create_callable_2_args_unit()
+ Puppet::Functions.create_function(:func) do
+ dispatch :func do
+ param 'Type', 'expected'
+ param 'Type', 'actual'
+ end
+
+ def func(expected, actual)
+ [expected, actual]
+ end
+ end.new({}, nil)
+ end
end
diff --git a/spec/unit/parser/methods/each_spec.rb b/spec/unit/functions/each_spec.rb
index 5e9ce4e0c..d6651cf5a 100644
--- a/spec/unit/parser/methods/each_spec.rb
+++ b/spec/unit/functions/each_spec.rb
@@ -1,7 +1,8 @@
require 'puppet'
require 'spec_helper'
require 'puppet_spec/compiler'
-require 'rubygems'
+
+require 'shared_behaviours/iterative_functions'
describe 'the each method' do
include PuppetSpec::Compiler
@@ -23,6 +24,7 @@ describe 'the each method' do
catalog.resource(:file, "/file_2")['ensure'].should == 'present'
catalog.resource(:file, "/file_3")['ensure'].should == 'present'
end
+
it 'each on an array selecting each value - function call style' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1,2,3]
@@ -61,6 +63,7 @@ describe 'the each method' do
catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
catalog.resource(:file, "/file_c")['ensure'].should == 'present'
end
+
it 'each on a hash selecting key and value' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {'a'=>present,'b'=>absent,'c'=>present}
@@ -73,7 +76,21 @@ describe 'the each method' do
catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
catalog.resource(:file, "/file_c")['ensure'].should == 'present'
end
+
+ it 'each on a hash selecting key and value (using captures-last parameter)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>present,'b'=>absent,'c'=>present}
+ $a.each |*$kv| {
+ file { "/file_${kv[0]}": ensure => $kv[1] }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
+ catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+ end
end
+
context "should produce receiver" do
it 'each checking produced value using single expression' do
catalog = compile_to_catalog(<<-MANIFEST)
@@ -88,4 +105,7 @@ describe 'the each method' do
end
end
+ it_should_behave_like 'all iterative functions argument checks', 'each'
+ it_should_behave_like 'all iterative functions hash handling', 'each'
+
end
diff --git a/spec/unit/parser/functions/epp_spec.rb b/spec/unit/functions/epp_spec.rb
index b88d3da8f..382fd9548 100644
--- a/spec/unit/parser/functions/epp_spec.rb
+++ b/spec/unit/functions/epp_spec.rb
@@ -27,7 +27,7 @@ describe "the epp function" do
end
it "get nil accessing a variable that is undef" do
- scope['undef_var'] = :undef
+ scope['undef_var'] = nil
eval_template("<%= $undef_var == undef %>").should == "true"
end
@@ -36,26 +36,74 @@ describe "the epp function" do
eval_template_with_args("<%= $phantom == dragos %>", 'phantom' => 'dragos').should == "true"
end
+ it "can use values from the enclosing scope for defaults" do
+ scope['phantom'] = 'of the opera'
+ eval_template("<%- |$phantom = $phantom| -%><%= $phantom %>").should == "of the opera"
+ end
+
+ it "uses the default value if the given value is undef/nil" do
+ eval_template_with_args("<%- |$phantom = 'inside your mind'| -%><%= $phantom %>", 'phantom' => nil).should == "inside your mind"
+ end
+
it "gets shadowed variable if args are given and parameters are specified" do
scope['x'] = 'wrong one'
- eval_template_with_args("<%-( $x )-%><%= $x == correct %>", 'x' => 'correct').should == "true"
+ eval_template_with_args("<%- |$x| -%><%= $x == correct %>", 'x' => 'correct').should == "true"
end
it "raises an error if required variable is not given" do
scope['x'] = 'wrong one'
- expect {
+ expect do
eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'y' => 'correct')
- }.to raise_error(/no value given for required parameters x/)
+ end.to raise_error(/no value given for required parameters x/)
end
it "raises an error if too many arguments are given" do
scope['x'] = 'wrong one'
- expect {
+ expect do
eval_template_with_args("<%-| $x |-%><%= $x == correct %>", 'x' => 'correct', 'y' => 'surplus')
- }.to raise_error(/Too many arguments: 2 for 1/)
+ end.to raise_error(/Too many arguments: 2 for 1/)
+ end
+ end
+
+ context "when given an empty template" do
+ it "allows the template file to be empty" do
+ expect(eval_template("")).to eq("")
+ end
+
+ it "allows the template to have empty body after parameters" do
+ expect(eval_template_with_args("<%-|$x|%>", 'x'=>1)).to eq("")
end
end
+ context "when using typed parameters" do
+ it "allows a passed value that matches the parameter's type" do
+ expect(eval_template_with_args("<%-|String $x|-%><%= $x == correct %>", 'x' => 'correct')).to eq("true")
+ end
+
+ it "does not allow slurped parameters" do
+ expect do
+ eval_template_with_args("<%-|*$x|-%><%= $x %>", 'x' => 'incorrect')
+ end.to raise_error(/'captures rest' - not supported in an Epp Template/)
+ end
+
+ it "raises an error when the passed value does not match the parameter's type" do
+ expect do
+ eval_template_with_args("<%-|Integer $x|-%><%= $x %>", 'x' => 'incorrect')
+ end.to raise_error(/expected.*Integer.*actual.*String/m)
+ end
+
+ it "raises an error when the default value does not match the parameter's type" do
+ expect do
+ eval_template("<%-|Integer $x = 'nope'|-%><%= $x %>")
+ end.to raise_error(/expected.*Integer.*actual.*String/m)
+ end
+
+ it "allows an parameter to default to undef" do
+ expect(eval_template("<%-|Optional[Integer] $x = undef|-%><%= $x == undef %>")).to eq("true")
+ end
+ end
+
+
# although never a problem with epp
it "is not interfered with by having a variable named 'string' (#14093)" do
scope['string'] = "this output should not be seen"
@@ -78,7 +126,7 @@ describe "the epp function" do
}})
Puppet.override({:current_environment => (env = Puppet::Node::Environment.create(:testload, [ modules_dir ]))}, "test") do
node.environment = env
- expect(scope.function_epp([ 'testmodule/the_x.epp', { 'x' => '3'} ])).to eql("The x is 3")
+ expect(epp_function.call(scope, 'testmodule/the_x.epp', { 'x' => '3'} )).to eql("The x is 3")
end
end
end
@@ -89,7 +137,7 @@ describe "the epp function" do
File.open(filename, "w+") { |f| f.write(content) }
Puppet::Parser::Files.stubs(:find_template).returns(filename)
- scope.function_epp(['template', args_hash])
+ epp_function.call(scope, 'template', args_hash)
end
def eval_template(content)
@@ -98,6 +146,10 @@ describe "the epp function" do
File.open(filename, "w+") { |f| f.write(content) }
Puppet::Parser::Files.stubs(:find_template).returns(filename)
- scope.function_epp(['template'])
+ epp_function.call(scope, 'template')
+ end
+
+ def epp_function()
+ epp_func = scope.compiler.loaders.public_environment_loader.load(:function, 'epp')
end
end
diff --git a/spec/unit/parser/methods/filter_spec.rb b/spec/unit/functions/filter_spec.rb
index c9fed31d2..e904c6751 100644
--- a/spec/unit/parser/methods/filter_spec.rb
+++ b/spec/unit/functions/filter_spec.rb
@@ -1,11 +1,13 @@
require 'puppet'
require 'spec_helper'
require 'puppet_spec/compiler'
+require 'matchers/resource'
-require 'unit/parser/methods/shared'
+require 'shared_behaviours/iterative_functions'
describe 'the filter method' do
include PuppetSpec::Compiler
+ include Matchers::Resource
before :each do
Puppet[:parser] = 'future'
@@ -19,8 +21,8 @@ describe 'the filter method' do
}
MANIFEST
- catalog.resource(:file, "/file_strawberry")['ensure'].should == 'present'
- catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present')
end
it 'should filter on enumerable type (Integer)' do
@@ -31,9 +33,9 @@ describe 'the filter method' do
}
MANIFEST
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- catalog.resource(:file, "/file_9")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_9]").with_parameter(:ensure, 'present')
end
it 'should filter on enumerable type (Integer) using two args index/value' do
@@ -44,9 +46,9 @@ describe 'the filter method' do
}
MANIFEST
- catalog.resource(:file, "/file_10")['ensure'].should == 'present'
- catalog.resource(:file, "/file_13")['ensure'].should == 'present'
- catalog.resource(:file, "/file_16")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_10]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_13]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_16]").with_parameter(:ensure, 'present')
end
it 'should produce an array when acting on an array' do
@@ -57,8 +59,8 @@ describe 'the filter method' do
file { "/file_${b[1]}": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_strawberry")['ensure'].should == 'present'
- catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present')
end
it 'can filter array using index and value' do
@@ -69,8 +71,20 @@ describe 'the filter method' do
file { "/file_${b[1]}": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_strawberry")['ensure'].should == 'present'
- catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_orange]").with_parameter(:ensure, 'present')
+ end
+
+ it 'can filter array using index and value (using captures-rest)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = ['strawberry','blueberry','orange']
+ $b = $a.filter |*$ix|{ $ix[0] == 0 or $ix[0] ==2}
+ file { "/file_${b[0]}": ensure => present }
+ file { "/file_${b[1]}": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_orange]").with_parameter(:ensure, 'present')
end
it 'filters on a hash (all berries) by key' do
@@ -81,8 +95,8 @@ describe 'the filter method' do
}
MANIFEST
- catalog.resource(:file, "/file_strawberry")['ensure'].should == 'present'
- catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_strawberry]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueberry]").with_parameter(:ensure, 'present')
end
it 'should produce a hash when acting on a hash' do
@@ -95,9 +109,9 @@ describe 'the filter method' do
MANIFEST
- catalog.resource(:file, "/file_red")['ensure'].should == 'present'
- catalog.resource(:file, "/file_blue")['ensure'].should == 'present'
- catalog.resource(:file, "/file_")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_red]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blue]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_]").with_parameter(:ensure, 'present')
end
it 'filters on a hash (all berries) by value' do
@@ -108,26 +122,8 @@ describe 'the filter method' do
}
MANIFEST
- catalog.resource(:file, "/file_strawb")['ensure'].should == 'present'
- catalog.resource(:file, "/file_blueb")['ensure'].should == 'present'
- end
-
- context 'filter checks arguments and' do
- it 'raises an error when block has more than 2 argument' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].filter |$indexm, $x, $yikes|{ }
- MANIFEST
- end.to raise_error(Puppet::Error, /block must define at most two parameters/)
- end
-
- it 'raises an error when block has fewer than 1 argument' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].filter || { }
- MANIFEST
- end.to raise_error(Puppet::Error, /block must define at least one parameter/)
- end
+ expect(catalog).to have_resource("File[/file_strawb]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_blueb]").with_parameter(:ensure, 'present')
end
it_should_behave_like 'all iterative functions argument checks', 'filter'
diff --git a/spec/unit/parser/functions/inline_epp_spec.rb b/spec/unit/functions/inline_epp_spec.rb
index 44b24528b..36328c2dc 100644
--- a/spec/unit/parser/functions/inline_epp_spec.rb
+++ b/spec/unit/functions/inline_epp_spec.rb
@@ -56,8 +56,18 @@ describe "the inline_epp function" do
end
end
+ context "when given an empty template" do
+ it "allows the template file to be empty" do
+ expect(eval_template("")).to eq("")
+ end
+
+ it "allows the template to have empty body after parameters" do
+ expect(eval_template_with_args("<%-|$x|%>", 'x'=>1)).to eq("")
+ end
+ end
+
it "renders a block expression" do
- eval_template_with_args("<%= {($x) $x + 1} %>", 'x' => 2).should == "3"
+ eval_template_with_args("<%= { $y = $x $x + 1} %>", 'x' => 2).should == "3"
end
# although never a problem with epp
@@ -73,10 +83,15 @@ describe "the inline_epp function" do
def eval_template_with_args(content, args_hash)
- scope.function_inline_epp([content, args_hash])
+ epp_function.call(scope, content, args_hash)
end
def eval_template(content)
- scope.function_inline_epp([content])
+ epp_function.call(scope, content)
end
+
+ def epp_function()
+ epp_func = scope.compiler.loaders.public_environment_loader.load(:function, 'inline_epp')
+ end
+
end
diff --git a/spec/unit/functions/map_spec.rb b/spec/unit/functions/map_spec.rb
new file mode 100644
index 000000000..e1b09cf24
--- /dev/null
+++ b/spec/unit/functions/map_spec.rb
@@ -0,0 +1,169 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+require 'shared_behaviours/iterative_functions'
+
+describe 'the map method' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :each do
+ Puppet[:parser] = "future"
+ end
+
+ context "using future parser" do
+ it 'map on an array (multiplying each value by 2)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $a.map |$x|{ $x*2}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_4]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on an enumerable type (multiplying each value by 2)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = Integer[1,3]
+ $a.map |$x|{ $x*2}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_4]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on an integer (multiply each by 3)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ 3.map |$x|{ $x*3}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a string' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {a=>x, b=>y}
+ "ab".map |$x|{$a[$x]}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_x]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_y]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on an array (multiplying value by 10 in even index position)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $a.map |$i, $x|{ if $i % 2 == 0 {$x} else {$x*10}}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_20]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a hash selecting keys' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$x|{ $x[0]}.each |$k|{
+ file { "/file_$k": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a hash selecting keys - using two block parameters' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$k,$v|{ file { "/file_$k": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present')
+ end
+
+ it 'map on a hash using captures-last parameter' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>present,'b'=>absent,'c'=>present}
+ $a.map |*$kv|{ file { "/file_${kv[0]}": ensure => $kv[1] } }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_a]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_b]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_c]").with_parameter(:ensure, 'present')
+ end
+
+ it 'each on a hash selecting value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$x|{ $x[1]}.each |$k|{ file { "/file_$k": ensure => present } }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'each on a hash selecting value - using two block parameters' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$k,$v|{ file { "/file_$v": ensure => present } }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ context "handles data type corner cases" do
+ it "map gets values that are false" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [false,false]
+ $a.map |$x| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0.false]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_1.false]").with_parameter(:ensure, 'present')
+ end
+
+ it "map gets values that are nil" do
+ Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args|
+ [nil]
+ end
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = nil_array()
+ $a.map |$x| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_0.]").with_parameter(:ensure, 'present')
+ end
+ end
+
+ it_should_behave_like 'all iterative functions argument checks', 'map'
+ it_should_behave_like 'all iterative functions hash handling', 'map'
+ end
+end
diff --git a/spec/unit/functions/match_spec.rb b/spec/unit/functions/match_spec.rb
new file mode 100644
index 000000000..f4e2e383b
--- /dev/null
+++ b/spec/unit/functions/match_spec.rb
@@ -0,0 +1,57 @@
+require 'spec_helper'
+require 'puppet/pops'
+require 'puppet/loaders'
+
+describe 'the match function' do
+
+ before(:all) do
+ loaders = Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))
+ Puppet.push_context({:loaders => loaders}, "test-examples")
+ end
+
+ after(:all) do
+ Puppet::Pops::Loaders.clear
+ Puppet::pop_context()
+ end
+
+ let(:func) do
+ Puppet.lookup(:loaders).puppet_system_loader.load(:function, 'match')
+ end
+
+ let(:type_parser) { Puppet::Pops::Types::TypeParser.new }
+
+
+ it 'matches string and regular expression without captures' do
+ expect(func.call({}, 'abc123', /[a-z]+[1-9]+/)).to eql(['abc123'])
+ end
+
+ it 'matches string and regular expression with captures' do
+ expect(func.call({}, 'abc123', /([a-z]+)([1-9]+)/)).to eql(['abc123', 'abc', '123'])
+ end
+
+ it 'produces nil if match is not found' do
+ expect(func.call({}, 'abc123', /([x]+)([6]+)/)).to be_nil
+ end
+
+ [ 'Pattern[/([a-z]+)([1-9]+)/]', # regexp
+ 'Pattern["([a-z]+)([1-9]+)"]', # string
+ 'Regexp[/([a-z]+)([1-9]+)/]', # regexp type
+ 'Pattern[/x9/, /([a-z]+)([1-9]+)/]', # regexp, first found matches
+ ].each do |pattern|
+ it "matches string and type #{pattern} with captures" do
+ expect(func.call({}, 'abc123', type(pattern))).to eql(['abc123', 'abc', '123'])
+ end
+ end
+
+ it 'matches an array of strings and yields a map of the result' do
+ expect(func.call({}, ['abc123', '2a', 'xyz2'], /([a-z]+)[1-9]+/)).to eql([['abc123', 'abc'], nil, ['xyz2', 'xyz']])
+ end
+
+ it 'raises error if Regexp type without regexp is used' do
+ expect{func.call({}, 'abc123', type('Regexp'))}.to raise_error(ArgumentError, /Given Regexp Type has no regular expression/)
+ end
+
+ def type(s)
+ Puppet::Pops::Types::TypeParser.new.parse(s)
+ end
+end
diff --git a/spec/unit/parser/methods/reduce_spec.rb b/spec/unit/functions/reduce_spec.rb
index 4f0c14e5e..032f6ccc4 100644
--- a/spec/unit/parser/methods/reduce_spec.rb
+++ b/spec/unit/functions/reduce_spec.rb
@@ -1,9 +1,12 @@
require 'puppet'
require 'spec_helper'
require 'puppet_spec/compiler'
+require 'matchers/resource'
+require 'shared_behaviours/iterative_functions'
describe 'the reduce method' do
include PuppetSpec::Compiler
+ include Matchers::Resource
before :all do
# enable switching back
@@ -32,7 +35,17 @@ describe 'the reduce method' do
file { "/file_$b": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
+ end
+
+ it 'reduce on an array with captures rest in lambda' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $b = $a.reduce |*$mx| { $mx[0] + $mx[1] }
+ file { "/file_$b": ensure => present }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
end
it 'reduce on enumerable type' do
@@ -42,7 +55,7 @@ describe 'the reduce method' do
file { "/file_$b": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_6]").with_parameter(:ensure, 'present')
end
it 'reduce on an array with start value' do
@@ -52,8 +65,9 @@ describe 'the reduce method' do
file { "/file_$b": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_10")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_10]").with_parameter(:ensure, 'present')
end
+
it 'reduce on a hash' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {a=>1, b=>2, c=>3}
@@ -62,8 +76,9 @@ describe 'the reduce method' do
file { "/file_${$b[0]}_${$b[1]}": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_sum_6")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_sum_6]").with_parameter(:ensure, 'present')
end
+
it 'reduce on a hash with start value' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {a=>1, b=>2, c=>3}
@@ -72,7 +87,10 @@ describe 'the reduce method' do
file { "/file_${$b[0]}_${$b[1]}": ensure => present }
MANIFEST
- catalog.resource(:file, "/file_sum_10")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_sum_10]").with_parameter(:ensure, 'present')
end
end
+
+ it_should_behave_like 'all iterative functions argument checks', 'reduce'
+
end
diff --git a/spec/unit/parser/methods/slice_spec.rb b/spec/unit/functions/slice_spec.rb
index 1de1dd0f1..945cae5c7 100644
--- a/spec/unit/parser/methods/slice_spec.rb
+++ b/spec/unit/functions/slice_spec.rb
@@ -1,10 +1,11 @@
require 'puppet'
require 'spec_helper'
require 'puppet_spec/compiler'
-require 'rubygems'
+require 'matchers/resource'
describe 'methods' do
include PuppetSpec::Compiler
+ include Matchers::Resource
before :all do
# enable switching back
@@ -36,9 +37,22 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
+ end
+
+ it 'slice with captures last' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1, present, 2, absent, 3, present]
+ $a.slice(2) |*$kv| {
+ file { "/file_${$kv[0]}": ensure => $kv[1] }
+ }
+ MANIFEST
+
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
end
it 'slice with one parameter' do
@@ -49,9 +63,9 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
end
it 'slice with shorter last slice' do
@@ -62,8 +76,8 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3.")['ensure'].should == 'absent'
+ expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3.]").with_parameter(:ensure, 'absent')
end
end
@@ -76,8 +90,8 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3.")['ensure'].should == 'absent'
+ expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3.]").with_parameter(:ensure, 'absent')
end
end
@@ -90,8 +104,8 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3.4")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_1.2]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_3.4]").with_parameter(:ensure, 'present')
end
it 'slice with integer' do
@@ -101,8 +115,8 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_0.1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2.3")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_0.1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2.3]").with_parameter(:ensure, 'present')
end
it 'slice with string' do
@@ -112,8 +126,8 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_a.b")['ensure'].should == 'present'
- catalog.resource(:file, "/file_c.d")['ensure'].should == 'present'
+ expect(catalog).to have_resource("File[/file_a.b]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_c.d]").with_parameter(:ensure, 'present')
end
end
@@ -126,10 +140,9 @@ describe 'methods' do
}
MANIFEST
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
-
+ expect(catalog).to have_resource("File[/file_1]").with_parameter(:ensure, 'present')
+ expect(catalog).to have_resource("File[/file_2]").with_parameter(:ensure, 'absent')
+ expect(catalog).to have_resource("File[/file_3]").with_parameter(:ensure, 'present')
end
end
end
diff --git a/spec/unit/functions/with_spec.rb b/spec/unit/functions/with_spec.rb
new file mode 100644
index 000000000..952b14412
--- /dev/null
+++ b/spec/unit/functions/with_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+require 'puppet_spec/compiler'
+require 'matchers/resource'
+
+describe 'the with function' do
+ include PuppetSpec::Compiler
+ include Matchers::Resource
+
+ before :each do
+ Puppet[:parser] = 'future'
+ end
+
+ it 'calls a lambda passing no arguments' do
+ expect(compile_to_catalog("with() || { notify { testing: } }")).to have_resource('Notify[testing]')
+ end
+
+ it 'calls a lambda passing a single argument' do
+ expect(compile_to_catalog('with(1) |$x| { notify { "testing$x": } }')).to have_resource('Notify[testing1]')
+ end
+
+ it 'calls a lambda passing more than one argument' do
+ expect(compile_to_catalog('with(1, 2) |*$x| { notify { "testing${x[0]}, ${x[1]}": } }')).to have_resource('Notify[testing1, 2]')
+ end
+
+ it 'passes a type reference to a lambda' do
+ expect(compile_to_catalog('notify { test: message => "data" } with(Notify[test]) |$x| { notify { "${x[message]}": } }')).to have_resource('Notify[data]')
+ end
+
+ it 'errors when not given enough arguments for the lambda' do
+ expect do
+ compile_to_catalog('with(1) |$x, $y| { }')
+ end.to raise_error(/Parameter \$y is required but no value was given/m)
+ end
+end
diff --git a/spec/unit/functions4_spec.rb b/spec/unit/functions4_spec.rb
index a5404cc1e..6c31b75c2 100644
--- a/spec/unit/functions4_spec.rb
+++ b/spec/unit/functions4_spec.rb
@@ -83,9 +83,9 @@ actual:
func = f.new(:closure_scope, :loader)
expect(func.is_a?(Puppet::Functions::Function)).to be_true
signature = if RUBY_VERSION =~ /^1\.8/
- 'Object{2}'
+ 'Any{2}'
else
- 'Object x, Object y'
+ 'Any x, Any y'
end
expect do
func.call({}, 10)
@@ -102,9 +102,9 @@ actual:
func = f.new(:closure_scope, :loader)
expect(func.is_a?(Puppet::Functions::Function)).to be_true
signature = if RUBY_VERSION =~ /^1\.8/
- 'Object{2}'
+ 'Any{2}'
else
- 'Object x, Object y'
+ 'Any x, Any y'
end
expect do
func.call({}, 10, 10, 10)
@@ -162,9 +162,9 @@ actual:
func = f.new(:closure_scope, :loader)
expect(func.is_a?(Puppet::Functions::Function)).to be_true
signature = if RUBY_VERSION =~ /^1\.8/
- 'Object{2,}'
+ 'Any{2,}'
else
- 'Object x, Object y, Object a?, Object b?, Object c{0,}'
+ 'Any x, Any y, Any a?, Any b?, Any c{0,}'
end
expect do
func.call({}, 10)
@@ -439,12 +439,11 @@ actual:
# about selection of parser and evaluator
#
Puppet[:parser] = 'future'
- Puppet[:evaluator] = 'future'
# Puppetx cannot be loaded until the correct parser has been set (injector is turned off otherwise)
require 'puppetx'
end
- let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new }
+ let(:parser) { Puppet::Pops::Parser::EvaluatingParser.new }
let(:node) { 'node.example.com' }
let(:scope) { s = create_test_scope_for_node(node); s }
diff --git a/spec/unit/indirector/catalog/compiler_spec.rb b/spec/unit/indirector/catalog/compiler_spec.rb
index 7d92aa80f..4aaecd664 100755
--- a/spec/unit/indirector/catalog/compiler_spec.rb
+++ b/spec/unit/indirector/catalog/compiler_spec.rb
@@ -6,10 +6,8 @@ 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
diff --git a/spec/unit/indirector/catalog/static_compiler_spec.rb b/spec/unit/indirector/catalog/static_compiler_spec.rb
index a3d13e804..556bc7943 100644
--- a/spec/unit/indirector/catalog/static_compiler_spec.rb
+++ b/spec/unit/indirector/catalog/static_compiler_spec.rb
@@ -11,10 +11,21 @@ describe Puppet::Resource::Catalog::StaticCompiler do
end
before :each do
+ Facter.stubs(:loadfacts)
Facter.stubs(:to_hash).returns({})
Facter.stubs(:value)
end
+ around(:each) do |example|
+ Puppet.override({
+ :current_environment => Puppet::Node::Environment.create(:app, []),
+ },
+ "Ensure we are using an environment other than root"
+ ) do
+ example.run
+ end
+ end
+
let(:request) do
Puppet::Indirector::Request.new(:the_indirection_named_foo,
:find,
diff --git a/spec/unit/indirector/data_binding/hiera_spec.rb b/spec/unit/indirector/data_binding/hiera_spec.rb
index 7ab3b27fc..12572b174 100644
--- a/spec/unit/indirector/data_binding/hiera_spec.rb
+++ b/spec/unit/indirector/data_binding/hiera_spec.rb
@@ -2,34 +2,6 @@ require 'spec_helper'
require 'puppet/indirector/data_binding/hiera'
describe Puppet::DataBinding::Hiera do
- include PuppetSpec::Files
-
- def write_hiera_config(config_file, datadir)
- File.open(config_file, 'w') do |f|
- f.write("---
- :yaml:
- :datadir: #{datadir}
- :hierarchy: ['global', 'invalid']
- :logger: 'noop'
- :backends: ['yaml']
- ")
- end
- end
-
- def request(key)
- Puppet::Indirector::Request.new(:hiera, :find, key, nil)
- end
-
- before do
- hiera_config_file = tmpfile("hiera.yaml")
- Puppet.settings[:hiera_config] = hiera_config_file
- write_hiera_config(hiera_config_file, my_fixture_dir)
- end
-
- after do
- Puppet::DataBinding::Hiera.instance_variable_set(:@hiera, nil)
- end
-
it "should have documentation" do
Puppet::DataBinding::Hiera.doc.should_not be_nil
end
@@ -42,73 +14,6 @@ describe Puppet::DataBinding::Hiera do
it "should have its name set to :hiera" do
Puppet::DataBinding::Hiera.name.should == :hiera
end
- it "should be the default data_binding terminus" do
- Puppet.settings[:data_binding_terminus].should == :hiera
- end
-
- it "should raise an error if we don't have the hiera feature" do
- Puppet.features.expects(:hiera?).returns(false)
- lambda { Puppet::DataBinding::Hiera.new }.should raise_error RuntimeError,
- "Hiera terminus not supported without hiera library"
- end
-
- describe "the behavior of the hiera_config method", :if => Puppet.features.hiera? do
- it "should override the logger and set it to puppet" do
- Puppet::DataBinding::Hiera.hiera_config[:logger].should == "puppet"
- end
- context "when the Hiera configuration file does not exist" do
- let(:path) { File.expand_path('/doesnotexist') }
-
- before do
- Puppet.settings[:hiera_config] = path
- end
-
- it "should log a warning" do
- Puppet.expects(:warning).with(
- "Config file #{path} not found, using Hiera defaults")
- Puppet::DataBinding::Hiera.hiera_config
- end
-
- it "should only configure the logger and set it to puppet" do
- Puppet.expects(:warning).with(
- "Config file #{path} not found, using Hiera defaults")
- Puppet::DataBinding::Hiera.hiera_config.should == { :logger => 'puppet' }
- end
- end
- end
-
- describe "the behavior of the find method", :if => Puppet.features.hiera? do
-
- let(:data_binder) { Puppet::DataBinding::Hiera.new }
-
- it "should support looking up an integer" do
- data_binder.find(request("integer")).should == 3000
- end
-
- it "should support looking up a string" do
- data_binder.find(request("string")).should == 'apache'
- end
-
- it "should support looking up an array" do
- data_binder.find(request("array")).should == [
- '0.ntp.puppetlabs.com',
- '1.ntp.puppetlabs.com',
- ]
- end
-
- it "should support looking up a hash" do
- data_binder.find(request("hash")).should == {
- 'user' => 'Hightower',
- 'group' => 'admin',
- 'mode' => '0644'
- }
- end
-
- it "raises a data binding error if hiera cannot parse the yaml data" do
- expect do
- data_binder.find(request('invalid'))
- end.to raise_error(Puppet::DataBinding::LookupError)
- end
- end
+ it_should_behave_like "Hiera indirection", Puppet::DataBinding::Hiera, my_fixture_dir
end
diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb
index cb90fb7c3..4417ede54 100755
--- a/spec/unit/indirector/facts/facter_spec.rb
+++ b/spec/unit/indirector/facts/facter_spec.rb
@@ -1,9 +1,8 @@
#! /usr/bin/env ruby
require 'spec_helper'
-
require 'puppet/indirector/facts/facter'
-module PuppetNodeFactsFacter
+module NodeFactsFacterSpec
describe Puppet::Node::Facts::Facter do
FS = Puppet::FileSystem
@@ -24,27 +23,6 @@ describe Puppet::Node::Facts::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
@@ -54,22 +32,31 @@ describe Puppet::Node::Facts::Facter do
@environment = stub 'environment'
@request.stubs(:environment).returns(@environment)
@request.environment.stubs(:modules).returns([])
+ @request.environment.stubs(:modulepath).returns([])
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)
+ describe 'when finding facts' do
+ it 'should reset facts' do
+ reset = sequence 'reset'
+ Facter.expects(:reset).in_sequence(reset)
+ Puppet::Node::Facts::Facter.expects(:setup_search_paths).in_sequence(reset)
+ @facter.find(@request)
+ end
+
+ it 'should include external facts when feature is present' do
+ reset = sequence 'reset'
+ Puppet.features.stubs(:external_facts?).returns true
+ Facter.expects(:reset).in_sequence(reset)
+ Puppet::Node::Facts::Facter.expects(:setup_external_search_paths).in_sequence(reset)
+ Puppet::Node::Facts::Facter.expects(:setup_search_paths).in_sequence(reset)
@facter.find(@request)
end
- it "should include external facts when feature is present" do
- clear = sequence 'clear'
- Puppet.features.stubs(:external_facts?).returns(:true)
- Puppet::Node::Facts::Facter.expects(:setup_external_facts).in_sequence(clear)
- Puppet::Node::Facts::Facter.expects(:reload_facter).in_sequence(clear)
- Puppet::Node::Facts::Facter.expects(:load_fact_plugins).in_sequence(clear)
+ it 'should not include external facts when feature is not present' do
+ reset = sequence 'reset'
+ Puppet.features.stubs(:external_facts?).returns false
+ Facter.expects(:reset).in_sequence(reset)
+ Puppet::Node::Facts::Facter.expects(:setup_search_paths).in_sequence(reset)
@facter.find(@request)
end
@@ -114,89 +101,69 @@ describe Puppet::Node::Facts::Facter do
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
+ it 'should fail when saving facts' do
+ proc { @facter.save(@facts) }.should raise_error(Puppet::DevError)
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")
+ it 'should fail when destroying facts' do
+ proc { @facter.destroy(@facts) }.should raise_error(Puppet::DevError)
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
+ describe 'when setting up search paths' do
+ let(:factpath1) { File.expand_path 'one' }
+ let(:factpath2) { File.expand_path 'two' }
+ let(:factpath) { [factpath1, factpath2].join(File::PATH_SEPARATOR) }
+ let(:modulepath) { File.expand_path 'module/foo' }
+ let(:modulelibfacter) { File.expand_path 'module/foo/lib/facter' }
+ let(:modulepluginsfacter) { File.expand_path 'module/foo/plugins/facter' }
- Dir.expects(:glob).with("*.rb").returns %w{a.rb b.rb}
+ before :each do
+ FileTest.expects(:directory?).with(factpath1).returns true
+ FileTest.expects(:directory?).with(factpath2).returns true
+ @request.environment.stubs(:modulepath).returns [modulepath]
+ Dir.expects(:glob).with("#{modulepath}/*/lib/facter").returns [modulelibfacter]
+ Dir.expects(:glob).with("#{modulepath}/*/plugins/facter").returns [modulepluginsfacter]
- 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
+ Puppet[:factpath] = factpath
+ end
- it "should include pluginfactdest when loading external facts",
- :if => (Puppet.features.external_facts? and not Puppet.features.microsoft_windows?) do
- Puppet[:pluginfactdest] = "/plugin/dest"
- @facter.find(@request)
- Facter.search_external_path.include?("/plugin/dest")
- end
+ it 'should skip files' do
+ FileTest.expects(:directory?).with(modulelibfacter).returns false
+ FileTest.expects(:directory?).with(modulepluginsfacter).returns false
+ Facter.expects(:search).with(factpath1, factpath2)
+ Puppet::Node::Facts::Facter.setup_search_paths @request
+ end
- it "should include pluginfactdest when loading external facts",
- :if => (Puppet.features.external_facts? and Puppet.features.microsoft_windows?) do
- Puppet[:pluginfactdest] = "/plugin/dest"
- @facter.find(@request)
- Facter.search_external_path.include?("C:/plugin/dest")
+ it 'should add directories' do
+ FileTest.expects(:directory?).with(modulelibfacter).returns true
+ FileTest.expects(:directory?).with(modulepluginsfacter).returns true
+ Facter.expects(:search).with(modulelibfacter, modulepluginsfacter, factpath1, factpath2)
+ Puppet::Node::Facts::Facter.setup_search_paths @request
+ end
end
- describe "when loading fact plugins from disk" do
- let(:one) { File.expand_path("one") }
- let(:two) { File.expand_path("two") }
-
- it "should load each directory in the Fact path" do
- Puppet[:factpath] = [one, two].join(File::PATH_SEPARATOR)
-
- Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with(one)
- Puppet::Node::Facts::Facter.expects(:load_facts_in_dir).with(two)
+ describe 'when setting up external search paths', :if => Puppet.features.external_facts? do
+ let(:pluginfactdest) { File.expand_path 'plugin/dest' }
+ let(:modulepath) { File.expand_path 'module/foo' }
+ let(:modulefactsd) { File.expand_path 'module/foo/facts.d' }
- Puppet::Node::Facts::Facter.load_fact_plugins
+ before :each do
+ FileTest.expects(:directory?).with(pluginfactdest).returns true
+ mod = Puppet::Module.new('foo', modulepath, @request.environment)
+ @request.environment.stubs(:modules).returns [mod]
+ Puppet[:pluginfactdest] = pluginfactdest
end
- it "should load all facts from the modules" do
- Puppet::Node::Facts::Facter.stubs(:load_facts_in_dir)
-
- 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")
-
- FS.overlay(FS::MemoryFile.a_directory(one), FS::MemoryFile.a_directory(two)) do
- Puppet.override(:current_environment => Puppet::Node::Environment.create(:testing, [one, two], "")) do
- Puppet::Node::Facts::Facter.load_fact_plugins
- end
- end
+ it 'should skip files' do
+ File.expects(:directory?).with(modulefactsd).returns false
+ Facter.expects(:search_external).with [pluginfactdest]
+ Puppet::Node::Facts::Facter.setup_external_search_paths @request
end
- it "should include module plugin facts when present", :if => Puppet.features.external_facts? do
- mod = Puppet::Module.new("mymodule", "#{one}/mymodule", @request.environment)
- @request.environment.stubs(:modules).returns([mod])
- @facter.find(@request)
- Facter.search_external_path.include?("#{one}/mymodule/facts.d")
+ it 'should add directories' do
+ File.expects(:directory?).with(modulefactsd).returns true
+ Facter.expects(:search_external).with [modulefactsd, pluginfactdest]
+ Puppet::Node::Facts::Facter.setup_external_search_paths @request
end
end
end
diff --git a/spec/unit/indirector/hiera_spec.rb b/spec/unit/indirector/hiera_spec.rb
new file mode 100644
index 000000000..f74fd29aa
--- /dev/null
+++ b/spec/unit/indirector/hiera_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+require 'puppet/data_binding'
+require 'puppet/indirector/hiera'
+require 'hiera/backend'
+
+describe Puppet::Indirector::Hiera do
+
+ module Testing
+ module DataBinding
+ class Hiera < Puppet::Indirector::Hiera
+ end
+ end
+ end
+
+ it_should_behave_like "Hiera indirection", Testing::DataBinding::Hiera, my_fixture_dir
+end
+
diff --git a/spec/unit/indirector/request_spec.rb b/spec/unit/indirector/request_spec.rb
index 3c11664d3..e3edfd670 100755
--- a/spec/unit/indirector/request_spec.rb
+++ b/spec/unit/indirector/request_spec.rb
@@ -232,14 +232,12 @@ describe Puppet::Indirector::Request do
Puppet::Indirector::Request.new(:myind, :find, "my key", nil, :environment => env).environment.should equal(env)
end
- it "should use the configured environment when none is provided" do
+ it "should use the current environment when none is provided" do
configured = Puppet::Node::Environment.create(:foo, [])
Puppet[:environment] = "foo"
- Puppet.override(:environments => Puppet::Environments::Static.new(configured)) do
- Puppet::Indirector::Request.new(:myind, :find, "my key", nil).environment.should == configured
- end
+ expect(Puppet::Indirector::Request.new(:myind, :find, "my key", nil).environment).to eq(Puppet.lookup(:current_environment))
end
it "should support converting its options to a hash" do
diff --git a/spec/unit/indirector/resource/ral_spec.rb b/spec/unit/indirector/resource/ral_spec.rb
index 8b42f6881..36ba5f1c2 100755
--- a/spec/unit/indirector/resource/ral_spec.rb
+++ b/spec/unit/indirector/resource/ral_spec.rb
@@ -25,6 +25,11 @@ describe "Puppet::Resource::Ral" do
Puppet::Resource::Ral.new.find(@request).should == my_resource
end
+ it "should produce Puppet::Error instead of ArgumentError" do
+ @bad_request = stub 'thiswillcauseanerror', :key => "thiswill/causeanerror"
+ expect{Puppet::Resource::Ral.new.find(@bad_request)}.to raise_error(Puppet::Error)
+ end
+
it "if there is no instance, it should create one" do
wrong_instance = stub "wrong user", :name => "bob"
root = mock "Root User"
diff --git a/spec/unit/indirector/resource_type/parser_spec.rb b/spec/unit/indirector/resource_type/parser_spec.rb
index cf81428e4..fdbab84e7 100755
--- a/spec/unit/indirector/resource_type/parser_spec.rb
+++ b/spec/unit/indirector/resource_type/parser_spec.rb
@@ -7,9 +7,13 @@ require 'puppet_spec/files'
describe Puppet::Indirector::ResourceType::Parser do
include PuppetSpec::Files
+ let(:environmentpath) { tmpdir("envs") }
+ let(:modulepath) { "#{environmentpath}/test/modules" }
+ let(:environment) { Puppet::Node::Environment.create(:test, [modulepath]) }
before do
@terminus = Puppet::Indirector::ResourceType::Parser.new
@request = Puppet::Indirector::Request.new(:resource_type, :find, "foo", nil)
+ @request.environment = environment
@krt = @request.environment.known_resource_types
end
@@ -26,19 +30,18 @@ describe Puppet::Indirector::ResourceType::Parser do
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
+ FileUtils.mkdir_p(modulepath)
# Make a new request, since we've reset the env
- @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo::bar", nil)
+ request = Puppet::Indirector::Request.new(:resource_type, :find, "foo::bar", nil)
+ request.environment = environment
- manifest_path = File.join(dir, "foo", "manifests")
+ manifest_path = File.join(modulepath, "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 = @terminus.find(request)
result.should be_instance_of(Puppet::Resource::Type)
result.name.should == "foo::bar"
end
@@ -108,10 +111,11 @@ describe Puppet::Indirector::ResourceType::Parser do
second = File.join(dir, "second")
FileUtils.mkdir_p(first)
FileUtils.mkdir_p(second)
- Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}"
+ environment = Puppet::Node::Environment.create(:test, [first, second])
# Make a new request, since we've reset the env
- @request = Puppet::Indirector::Request.new(:resource_type, :search, "*", nil)
+ request = Puppet::Indirector::Request.new(:resource_type, :search, "*", nil)
+ request.environment = environment
onepath = File.join(first, "one", "manifests")
FileUtils.mkdir_p(onepath)
@@ -121,7 +125,7 @@ describe Puppet::Indirector::ResourceType::Parser do
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 = @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
@@ -228,10 +232,11 @@ describe Puppet::Indirector::ResourceType::Parser do
second = File.join(dir, "second")
FileUtils.mkdir_p(first)
FileUtils.mkdir_p(second)
- Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}"
+ environment = Puppet::Node::Environment.create(:test, [first,second])
# Make a new request, since we've reset the env
- @request = Puppet::Indirector::Request.new(:resource_type, :search, "*", nil)
+ request = Puppet::Indirector::Request.new(:resource_type, :search, "*", nil)
+ request.environment = environment
onepath = File.join(first, "one", "manifests")
FileUtils.mkdir_p(onepath)
@@ -241,7 +246,7 @@ describe Puppet::Indirector::ResourceType::Parser do
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 = @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
diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb
index 5c9065fec..7a97708d3 100755
--- a/spec/unit/indirector/rest_spec.rb
+++ b/spec/unit/indirector/rest_spec.rb
@@ -136,6 +136,12 @@ describe Puppet::Indirector::REST do
let(:indirection) { Puppet::TestModel.indirection }
let(:model) { Puppet::TestModel }
+ around(:each) do |example|
+ Puppet.override(:current_environment => Puppet::Node::Environment.create(:production, [])) do
+ example.run
+ end
+ end
+
def mock_response(code, body, content_type='text/plain', encoding=nil)
obj = stub('http 200 ok', :code => code.to_s, :body => body)
obj.stubs(:[]).with('content-type').returns(content_type)
@@ -285,17 +291,41 @@ describe Puppet::Indirector::REST do
end
context 'when fail_on_404 is used in request' do
- let(:request) { find_request('foo', :fail_on_404 => true) }
-
it 'raises an error for a 404 when asked to do so' do
+ request = find_request('foo', :fail_on_404 => true)
response = mock_response('404', 'this is the notfound you are looking for')
connection.expects(:get).returns(response)
- expected_message = [
- 'Find /production/test_model/foo?fail_on_404=true',
- 'resulted in 404 with the message: this is the notfound you are looking for'].join( ' ')
+
+ expect do
+ terminus.find(request)
+ end.to raise_error(
+ Puppet::Error,
+ 'Find /production/test_model/foo?fail_on_404=true resulted in 404 with the message: this is the notfound you are looking for')
+ end
+
+ it 'truncates the URI when it is very long' do
+ request = find_request('foo', :fail_on_404 => true, :long_param => ('A' * 100) + 'B')
+ response = mock_response('404', 'this is the notfound you are looking for')
+ connection.expects(:get).returns(response)
+
+ expect do
+ terminus.find(request)
+ end.to raise_error(
+ Puppet::Error,
+ /\/production\/test_model\/foo.*long_param=A+\.\.\..*resulted in 404 with the message/)
+ end
+
+ it 'does not truncate the URI when logging debug information' do
+ Puppet.debug = true
+ request = find_request('foo', :fail_on_404 => true, :long_param => ('A' * 100) + 'B')
+ response = mock_response('404', 'this is the notfound you are looking for')
+ connection.expects(:get).returns(response)
+
expect do
terminus.find(request)
- end.to raise_error(Puppet::Error, expected_message)
+ end.to raise_error(
+ Puppet::Error,
+ /\/production\/test_model\/foo.*long_param=A+B.*resulted in 404 with the message/)
end
end
diff --git a/spec/unit/interface/face_collection_spec.rb b/spec/unit/interface/face_collection_spec.rb
index 0562c933e..89928b70a 100755
--- a/spec/unit/interface/face_collection_spec.rb
+++ b/spec/unit/interface/face_collection_spec.rb
@@ -11,12 +11,12 @@ describe Puppet::Interface::FaceCollection do
# the 'subject' of the specs will differ.
before :all do
# Save FaceCollection's global state
- faces = subject.instance_variable_get(:@faces)
+ faces = described_class.instance_variable_get(:@faces)
@faces = faces.dup
faces.each do |k, v|
@faces[k] = v.dup
end
- @faces_loaded = subject.instance_variable_get(:@loaded)
+ @faces_loaded = described_class.instance_variable_get(:@loaded)
# Save the already required face files
@required = []
diff --git a/spec/unit/module_tool/applications/builder_spec.rb b/spec/unit/module_tool/applications/builder_spec.rb
index e2b63c192..291473db9 100644
--- a/spec/unit/module_tool/applications/builder_spec.rb
+++ b/spec/unit/module_tool/applications/builder_spec.rb
@@ -1,4 +1,5 @@
require 'spec_helper'
+require 'puppet/file_system'
require 'puppet/module_tool/applications'
require 'puppet_spec/modules'
@@ -12,6 +13,330 @@ describe Puppet::ModuleTool::Applications::Builder do
let(:tarball) { File.join(path, 'pkg', release_name) + ".tar.gz" }
let(:builder) { Puppet::ModuleTool::Applications::Builder.new(path) }
+ shared_examples "a packagable module" do
+ def target_exists?(file)
+ File.exist?(File.join(path, "pkg", "#{module_name}-#{version}", file))
+ end
+
+ def build
+ tarrer = mock('tarrer')
+ Puppet::ModuleTool::Tar.expects(:instance).returns(tarrer)
+ Dir.expects(:chdir).with(File.join(path, 'pkg')).yields
+ tarrer.expects(:pack).with(release_name, tarball)
+
+ builder.run
+ end
+
+ def create_regular_files
+ Puppet::FileSystem.touch(File.join(path, '.dotfile'))
+ Puppet::FileSystem.touch(File.join(path, 'file.foo'))
+ Puppet::FileSystem.touch(File.join(path, 'REVISION'))
+ Puppet::FileSystem.touch(File.join(path, '~file'))
+ Puppet::FileSystem.touch(File.join(path, '#file'))
+ Puppet::FileSystem.mkpath(File.join(path, 'pkg'))
+ Puppet::FileSystem.mkpath(File.join(path, 'coverage'))
+ Puppet::FileSystem.mkpath(File.join(path, 'sub'))
+ Puppet::FileSystem.touch(File.join(path, 'sub/.dotfile'))
+ Puppet::FileSystem.touch(File.join(path, 'sub/file.foo'))
+ Puppet::FileSystem.touch(File.join(path, 'sub/REVISION'))
+ Puppet::FileSystem.touch(File.join(path, 'sub/~file'))
+ Puppet::FileSystem.touch(File.join(path, 'sub/#file'))
+ Puppet::FileSystem.mkpath(File.join(path, 'sub/pkg'))
+ Puppet::FileSystem.mkpath(File.join(path, 'sub/coverage'))
+ end
+
+ def create_symlinks
+ Puppet::FileSystem.touch(File.join(path, 'symlinkedfile'))
+ Puppet::FileSystem.symlink(File.join(path, 'symlinkedfile'), File.join(path, 'symlinkfile'))
+ end
+
+ def create_ignored_files
+ Puppet::FileSystem.touch(File.join(path, 'gitignored.foo'))
+ Puppet::FileSystem.mkpath(File.join(path, 'gitdirectory/sub'))
+ Puppet::FileSystem.touch(File.join(path, 'gitdirectory/gitartifact'))
+ Puppet::FileSystem.touch(File.join(path, 'gitdirectory/gitimportantfile'))
+ Puppet::FileSystem.touch(File.join(path, 'gitdirectory/sub/artifact'))
+ Puppet::FileSystem.touch(File.join(path, 'pmtignored.foo'))
+ Puppet::FileSystem.mkpath(File.join(path, 'pmtdirectory/sub'))
+ Puppet::FileSystem.touch(File.join(path, 'pmtdirectory/pmtimportantfile'))
+ Puppet::FileSystem.touch(File.join(path, 'pmtdirectory/pmtartifact'))
+ Puppet::FileSystem.touch(File.join(path, 'pmtdirectory/sub/artifact'))
+ end
+
+ def create_pmtignore_file
+ Puppet::FileSystem.open(File.join(path, '.pmtignore'), 0600, 'w') do |f|
+ f << <<-PMTIGNORE
+pmtignored.*
+pmtdirectory/sub/**
+pmtdirectory/pmt*
+!pmtimportantfile
+PMTIGNORE
+ end
+ end
+
+ def create_gitignore_file
+ Puppet::FileSystem.open(File.join(path, '.gitignore'), 0600, 'w') do |f|
+ f << <<-GITIGNORE
+gitignored.*
+gitdirectory/sub/**
+gitdirectory/git*
+!gitimportantfile
+GITIGNORE
+ end
+ end
+
+ def create_symlink_gitignore_file
+ Puppet::FileSystem.open(File.join(path, '.gitignore'), 0600, 'w') do |f|
+ f << <<-GITIGNORE
+symlinkfile
+ GITIGNORE
+ end
+ end
+
+ shared_examples "regular files are present" do
+ it "has metadata" do
+ expect(target_exists?('metadata.json')).to eq true
+ end
+
+ it "has checksums" do
+ expect(target_exists?('checksums.json')).to eq true
+ end
+
+ it "copies regular files" do
+ expect(target_exists?('file.foo')).to eq true
+ end
+ end
+
+ shared_examples "default artifacts are removed in module dir but not in subdirs" do
+ it "ignores dotfiles" do
+ expect(target_exists?('.dotfile')).to eq false
+ expect(target_exists?('sub/.dotfile')).to eq true
+ end
+
+ it "does not have .gitignore" do
+ expect(target_exists?('.gitignore')).to eq false
+ end
+
+ it "does not have .pmtignore" do
+ expect(target_exists?('.pmtignore')).to eq false
+ end
+
+ it "does not have pkg" do
+ expect(target_exists?('pkg')).to eq false
+ expect(target_exists?('sub/pkg')).to eq true
+ end
+
+ it "does not have coverage" do
+ expect(target_exists?('coverage')).to eq false
+ expect(target_exists?('sub/coverage')).to eq true
+ end
+
+ it "does not have REVISION" do
+ expect(target_exists?('REVISION')).to eq false
+ expect(target_exists?('sub/REVISION')).to eq true
+ end
+
+ it "does not have ~files" do
+ expect(target_exists?('~file')).to eq false
+ expect(target_exists?('sub/~file')).to eq true
+ end
+
+ it "does not have #files" do
+ expect(target_exists?('#file')).to eq false
+ expect(target_exists?('sub/#file')).to eq true
+ end
+ end
+
+ shared_examples "gitignored files are present" do
+ it "leaves regular files" do
+ expect(target_exists?('gitignored.foo')).to eq true
+ end
+
+ it "leaves directories" do
+ expect(target_exists?('gitdirectory')).to eq true
+ end
+
+ it "leaves files in directories" do
+ expect(target_exists?('gitdirectory/gitartifact')).to eq true
+ end
+
+ it "leaves exceptional files" do
+ expect(target_exists?('gitdirectory/gitimportantfile')).to eq true
+ end
+
+ it "leaves subdirectories" do
+ expect(target_exists?('gitdirectory/sub')).to eq true
+ end
+
+ it "leaves files in subdirectories" do
+ expect(target_exists?('gitdirectory/sub/artifact')).to eq true
+ end
+ end
+
+ shared_examples "gitignored files are not present" do
+ it "ignores regular files" do
+ expect(target_exists?('gitignored.foo')).to eq false
+ end
+
+ it "ignores directories" do
+ expect(target_exists?('gitdirectory')).to eq true
+ end
+
+ it "ignores files in directories" do
+ expect(target_exists?('gitdirectory/gitartifact')).to eq false
+ end
+
+ it "copies exceptional files" do
+ expect(target_exists?('gitdirectory/gitimportantfile')).to eq true
+ end
+
+ it "ignores subdirectories" do
+ expect(target_exists?('gitdirectory/sub')).to eq false
+ end
+
+ it "ignores files in subdirectories" do
+ expect(target_exists?('gitdirectory/sub/artifact')).to eq false
+ end
+ end
+
+ shared_examples "pmtignored files are present" do
+ it "leaves regular files" do
+ expect(target_exists?('pmtignored.foo')).to eq true
+ end
+
+ it "leaves directories" do
+ expect(target_exists?('pmtdirectory')).to eq true
+ end
+
+ it "ignores files in directories" do
+ expect(target_exists?('pmtdirectory/pmtartifact')).to eq true
+ end
+
+ it "leaves exceptional files" do
+ expect(target_exists?('pmtdirectory/pmtimportantfile')).to eq true
+ end
+
+ it "leaves subdirectories" do
+ expect(target_exists?('pmtdirectory/sub')).to eq true
+ end
+
+ it "leaves files in subdirectories" do
+ expect(target_exists?('pmtdirectory/sub/artifact')).to eq true
+ end
+ end
+
+ shared_examples "pmtignored files are not present" do
+ it "ignores regular files" do
+ expect(target_exists?('pmtignored.foo')).to eq false
+ end
+
+ it "ignores directories" do
+ expect(target_exists?('pmtdirectory')).to eq true
+ end
+
+ it "copies exceptional files" do
+ expect(target_exists?('pmtdirectory/pmtimportantfile')).to eq true
+ end
+
+ it "ignores files in directories" do
+ expect(target_exists?('pmtdirectory/pmtartifact')).to eq false
+ end
+
+ it "ignores subdirectories" do
+ expect(target_exists?('pmtdirectory/sub')).to eq false
+ end
+
+ it "ignores files in subdirectories" do
+ expect(target_exists?('pmtdirectory/sub/artifact')).to eq false
+ end
+ end
+
+ context "with no ignore files" do
+ before :each do
+ create_regular_files
+ create_ignored_files
+
+ build
+ end
+
+ it_behaves_like "regular files are present"
+ it_behaves_like "default artifacts are removed in module dir but not in subdirs"
+ it_behaves_like "pmtignored files are present"
+ it_behaves_like "gitignored files are present"
+ end
+
+ context "with .gitignore file" do
+ before :each do
+ create_regular_files
+ create_ignored_files
+ create_gitignore_file
+
+ build
+ end
+
+ it_behaves_like "regular files are present"
+ it_behaves_like "default artifacts are removed in module dir but not in subdirs"
+ it_behaves_like "pmtignored files are present"
+ it_behaves_like "gitignored files are not present"
+ end
+
+ context "with .pmtignore file" do
+ before :each do
+ create_regular_files
+ create_ignored_files
+ create_pmtignore_file
+
+ build
+ end
+
+ it_behaves_like "regular files are present"
+ it_behaves_like "default artifacts are removed in module dir but not in subdirs"
+ it_behaves_like "gitignored files are present"
+ it_behaves_like "pmtignored files are not present"
+ end
+
+ context "with .pmtignore and .gitignore file" do
+ before :each do
+ create_regular_files
+ create_ignored_files
+ create_pmtignore_file
+ create_gitignore_file
+
+ build
+ end
+
+ it_behaves_like "regular files are present"
+ it_behaves_like "default artifacts are removed in module dir but not in subdirs"
+ it_behaves_like "gitignored files are present"
+ it_behaves_like "pmtignored files are not present"
+ end
+
+ context "with unignored symlinks", :if => Puppet.features.manages_symlinks? do
+ before :each do
+ create_regular_files
+ create_symlinks
+ create_ignored_files
+ end
+
+ it "give an error about symlinks" do
+ expect { builder.run }.to raise_error
+ end
+ end
+
+ context "with .gitignore file and ignored symlinks", :if => Puppet.features.manages_symlinks? do
+ before :each do
+ create_regular_files
+ create_symlinks
+ create_ignored_files
+ create_symlink_gitignore_file
+ end
+
+ it "does not give an error about symlinks" do
+ expect { build }.not_to raise_error
+ end
+ end
+ end
+
context 'with metadata.json' do
before :each do
File.open(File.join(path, 'metadata.json'), 'w') do |f|
@@ -28,16 +353,48 @@ describe Puppet::ModuleTool::Applications::Builder do
end
end
- it "packages the module in a tarball named after the module" do
- tarrer = mock('tarrer')
- Puppet::ModuleTool::Tar.expects(:instance).returns(tarrer)
- Dir.expects(:chdir).with(File.join(path, 'pkg')).yields
- tarrer.expects(:pack).with(release_name, tarball)
+ it_behaves_like "a packagable module"
- builder.run
+ it "does not package with a symlink", :if => Puppet.features.manages_symlinks? do
+ FileUtils.touch(File.join(path, 'tempfile'))
+ Puppet::FileSystem.symlink(File.join(path, 'tempfile'), File.join(path, 'tempfile2'))
+
+ expect {
+ builder.run
+ }.to raise_error Puppet::ModuleTool::Errors::ModuleToolError, /symlinks/i
+ end
+
+ it "does not package with a symlink in a subdir", :if => Puppet.features.manages_symlinks? do
+ FileUtils.mkdir(File.join(path, 'manifests'))
+ FileUtils.touch(File.join(path, 'manifests/tempfile.pp'))
+ Puppet::FileSystem.symlink(File.join(path, 'manifests/tempfile.pp'), File.join(path, 'manifests/tempfile2.pp'))
+
+ expect {
+ builder.run
+ }.to raise_error Puppet::ModuleTool::Errors::ModuleToolError, /symlinks/i
end
end
+ context 'with metadata.json containing checksums' do
+ before :each do
+ File.open(File.join(path, 'metadata.json'), 'w') do |f|
+ f.puts({
+ "name" => "#{module_name}",
+ "version" => "#{version}",
+ "source" => "http://github.com/testing/#{module_name}",
+ "author" => "testing",
+ "license" => "Apache License Version 2.0",
+ "summary" => "Puppet testing module",
+ "description" => "This module can be used for basic testing",
+ "project_page" => "http://github.com/testing/#{module_name}",
+ "checksums" => {"README.md" => "deadbeef"}
+ }.to_json)
+ end
+ end
+
+ it_behaves_like "a packagable module"
+ end
+
context 'with Modulefile' do
before :each do
File.open(File.join(path, 'Modulefile'), 'w') do |f|
@@ -54,13 +411,6 @@ MODULEFILE
end
end
- it "packages the module in a tarball named after the module" do
- tarrer = mock('tarrer')
- Puppet::ModuleTool::Tar.expects(:instance).returns(tarrer)
- Dir.expects(:chdir).with(File.join(path, 'pkg')).yields
- tarrer.expects(:pack).with(release_name, tarball)
-
- builder.run
- end
+ it_behaves_like "a packagable module"
end
end
diff --git a/spec/unit/module_tool/applications/uninstaller_spec.rb b/spec/unit/module_tool/applications/uninstaller_spec.rb
index 2a8562ab9..66e71b638 100644
--- a/spec/unit/module_tool/applications/uninstaller_spec.rb
+++ b/spec/unit/module_tool/applications/uninstaller_spec.rb
@@ -113,6 +113,28 @@ describe Puppet::ModuleTool::Applications::Uninstaller do
end
end
+ context 'with --ignore-changes' do
+ def options
+ super.merge(:ignore_changes => true)
+ end
+
+ context 'with local changes' do
+ before do
+ mark_changed(File.join(primary_dir, 'stdlib'))
+ end
+
+ it 'overwrites the installed module with the greatest version matching that range' do
+ subject.should include :result => :success
+ end
+ end
+
+ context 'without local changes' do
+ it 'overwrites the installed module with the greatest version matching that range' do
+ subject.should include :result => :success
+ end
+ end
+ end
+
context "when using the --force flag" do
def options
diff --git a/spec/unit/module_tool/applications/unpacker_spec.rb b/spec/unit/module_tool/applications/unpacker_spec.rb
index 39b0c261f..81557df99 100644
--- a/spec/unit/module_tool/applications/unpacker_spec.rb
+++ b/spec/unit/module_tool/applications/unpacker_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
require 'json'
require 'puppet/module_tool/applications'
+require 'puppet/file_system'
require 'puppet_spec/modules'
describe Puppet::ModuleTool::Applications::Unpacker do
@@ -31,4 +32,43 @@ describe Puppet::ModuleTool::Applications::Unpacker do
Puppet::ModuleTool::Applications::Unpacker.run(filename, :target_dir => target)
File.should be_directory(File.join(target, 'mytarball'))
end
+
+ it "should warn about symlinks", :if => Puppet.features.manages_symlinks? do
+ untar = mock('Tar')
+ untar.expects(:unpack).with(filename, anything()) do |src, dest, _|
+ FileUtils.mkdir(File.join(dest, 'extractedmodule'))
+ File.open(File.join(dest, 'extractedmodule', 'metadata.json'), 'w+') do |file|
+ file.puts JSON.generate('name' => module_name, 'version' => '1.0.0')
+ end
+ FileUtils.touch(File.join(dest, 'extractedmodule/tempfile'))
+ Puppet::FileSystem.symlink(File.join(dest, 'extractedmodule/tempfile'), File.join(dest, 'extractedmodule/tempfile2'))
+ true
+ end
+
+ Puppet::ModuleTool::Tar.expects(:instance).returns(untar)
+ Puppet.expects(:warning).with(regexp_matches(/symlinks/i))
+
+ Puppet::ModuleTool::Applications::Unpacker.run(filename, :target_dir => target)
+ File.should be_directory(File.join(target, 'mytarball'))
+ end
+
+ it "should warn about symlinks in subdirectories", :if => Puppet.features.manages_symlinks? do
+ untar = mock('Tar')
+ untar.expects(:unpack).with(filename, anything()) do |src, dest, _|
+ FileUtils.mkdir(File.join(dest, 'extractedmodule'))
+ File.open(File.join(dest, 'extractedmodule', 'metadata.json'), 'w+') do |file|
+ file.puts JSON.generate('name' => module_name, 'version' => '1.0.0')
+ end
+ FileUtils.mkdir(File.join(dest, 'extractedmodule/manifests'))
+ FileUtils.touch(File.join(dest, 'extractedmodule/manifests/tempfile'))
+ Puppet::FileSystem.symlink(File.join(dest, 'extractedmodule/manifests/tempfile'), File.join(dest, 'extractedmodule/manifests/tempfile2'))
+ true
+ end
+
+ Puppet::ModuleTool::Tar.expects(:instance).returns(untar)
+ Puppet.expects(:warning).with(regexp_matches(/symlinks/i))
+
+ Puppet::ModuleTool::Applications::Unpacker.run(filename, :target_dir => target)
+ File.should be_directory(File.join(target, 'mytarball'))
+ end
end
diff --git a/spec/unit/module_tool/applications/upgrader_spec.rb b/spec/unit/module_tool/applications/upgrader_spec.rb
index 44627f94f..382e45a75 100644
--- a/spec/unit/module_tool/applications/upgrader_spec.rb
+++ b/spec/unit/module_tool/applications/upgrader_spec.rb
@@ -52,6 +52,16 @@ describe Puppet::ModuleTool::Applications::Upgrader do
end
context 'for an installed module' do
+ context 'with only one version' do
+ before { preinstall('puppetlabs-oneversion', '0.0.1') }
+ let(:module) { 'puppetlabs-oneversion' }
+
+ it 'declines to upgrade' do
+ subject.should include :result => :noop
+ subject[:error][:multiline].should =~ /already the latest version/
+ end
+ end
+
context 'without dependencies' do
before { preinstall('pmtacceptance-stdlib', '1.0.0') }
@@ -90,6 +100,7 @@ describe Puppet::ModuleTool::Applications::Upgrader do
context 'without options' do
it 'declines to upgrade' do
subject.should include :result => :noop
+ subject[:error][:multiline].should =~ /already the latest version/
end
end
@@ -165,6 +176,17 @@ describe Puppet::ModuleTool::Applications::Upgrader do
subject.should include :result => :failure
subject[:error].should include :oneline => "Could not upgrade '#{self.module}'; module has had changes made locally"
end
+
+ context 'with --ignore-changes' do
+ def options
+ super.merge(:ignore_changes => true)
+ end
+
+ it 'overwrites the installed module with the greatest version matching that range' do
+ subject.should include :result => :success
+ graph_should_include 'pmtacceptance-stdlib', v('1.0.0') => v('4.1.0')
+ end
+ end
end
context 'with dependencies' do
diff --git a/spec/unit/module_tool/installed_modules_spec.rb b/spec/unit/module_tool/installed_modules_spec.rb
new file mode 100644
index 000000000..b9492d986
--- /dev/null
+++ b/spec/unit/module_tool/installed_modules_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+require 'puppet/module_tool/installed_modules'
+require 'puppet_spec/modules'
+
+describe Puppet::ModuleTool::InstalledModules do
+ include PuppetSpec::Files
+
+ around do |example|
+ dir = tmpdir("deep_path")
+
+ FileUtils.mkdir_p(@modpath = File.join(dir, "modpath"))
+
+ @env = Puppet::Node::Environment.create(:env, [@modpath])
+ Puppet.override(:current_environment => @env) do
+ example.run
+ end
+ end
+
+ it 'works when given a semantic version' do
+ mod = PuppetSpec::Modules.create('goodsemver', @modpath, :metadata => {:version => '1.2.3'})
+ installed = described_class.new(@env)
+ expect(installed.modules["puppetlabs-#{mod.name}"].version).to eq(Semantic::Version.parse('1.2.3'))
+ end
+
+ it 'defaults when not given a semantic version' do
+ mod = PuppetSpec::Modules.create('badsemver', @modpath, :metadata => {:version => 'banana'})
+ Puppet.expects(:warning).with(regexp_matches(/Semantic Version/))
+ installed = described_class.new(@env)
+ expect(installed.modules["puppetlabs-#{mod.name}"].version).to eq(Semantic::Version.parse('0.0.0'))
+ end
+
+ it 'defaults when not given a full semantic version' do
+ mod = PuppetSpec::Modules.create('badsemver', @modpath, :metadata => {:version => '1.2'})
+ Puppet.expects(:warning).with(regexp_matches(/Semantic Version/))
+ installed = described_class.new(@env)
+ expect(installed.modules["puppetlabs-#{mod.name}"].version).to eq(Semantic::Version.parse('0.0.0'))
+ end
+
+ it 'still works if there is an invalid version in one of the modules' do
+ mod1 = PuppetSpec::Modules.create('badsemver', @modpath, :metadata => {:version => 'banana'})
+ mod2 = PuppetSpec::Modules.create('goodsemver', @modpath, :metadata => {:version => '1.2.3'})
+ mod3 = PuppetSpec::Modules.create('notquitesemver', @modpath, :metadata => {:version => '1.2'})
+ Puppet.expects(:warning).with(regexp_matches(/Semantic Version/)).twice
+ installed = described_class.new(@env)
+ expect(installed.modules["puppetlabs-#{mod1.name}"].version).to eq(Semantic::Version.parse('0.0.0'))
+ expect(installed.modules["puppetlabs-#{mod2.name}"].version).to eq(Semantic::Version.parse('1.2.3'))
+ expect(installed.modules["puppetlabs-#{mod3.name}"].version).to eq(Semantic::Version.parse('0.0.0'))
+ end
+end
diff --git a/spec/unit/module_tool/metadata_spec.rb b/spec/unit/module_tool/metadata_spec.rb
index 3b925c644..fce9c8f8d 100644
--- a/spec/unit/module_tool/metadata_spec.rb
+++ b/spec/unit/module_tool/metadata_spec.rb
@@ -5,6 +5,19 @@ describe Puppet::ModuleTool::Metadata do
let(:data) { {} }
let(:metadata) { Puppet::ModuleTool::Metadata.new }
+ describe 'property lookups' do
+ subject { metadata }
+
+ %w[ name version author summary license source project_page issues_url
+ dependencies dashed_name release_name description ].each do |prop|
+ describe "##{prop}" do
+ it "responds to the property" do
+ subject.send(prop)
+ end
+ end
+ end
+ end
+
describe "#update" do
subject { metadata.update(data) }
@@ -156,6 +169,61 @@ describe Puppet::ModuleTool::Metadata do
end
+ context "with a valid dependency" do
+ let(:data) { {'dependencies' => [{'name' => 'puppetlabs-goodmodule'}] }}
+
+ it "adds the dependency" do
+ subject.dependencies.size.should == 1
+ end
+ end
+
+ context "with a invalid dependency name" do
+ let(:data) { {'dependencies' => [{'name' => 'puppetlabsbadmodule'}] }}
+
+ it "raises an exception" do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "with a valid dependency version range" do
+ let(:data) { {'dependencies' => [{'name' => 'puppetlabs-badmodule', 'version_requirement' => '>= 2.0.0'}] }}
+
+ it "adds the dependency" do
+ subject.dependencies.size.should == 1
+ end
+ end
+
+ context "with a invalid version range" do
+ let(:data) { {'dependencies' => [{'name' => 'puppetlabsbadmodule', 'version_requirement' => '>= banana'}] }}
+
+ it "raises an exception" do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "with duplicate dependencies" do
+ let(:data) { {'dependencies' => [{'name' => 'puppetlabs-dupmodule', 'version_requirement' => '1.0.0'},
+ {'name' => 'puppetlabs-dupmodule', 'version_requirement' => '0.0.1'}] }
+ }
+
+ it "raises an exception" do
+ expect { subject }.to raise_error(ArgumentError)
+ end
+ end
+
+ context "adding a duplicate dependency" do
+ let(:data) { {'dependencies' => [{'name' => 'puppetlabs-origmodule', 'version_requirement' => '1.0.0'}] }}
+
+ it "with a different version raises an exception" do
+ metadata.add_dependency('puppetlabs-origmodule', '>= 0.0.1')
+ expect { subject }.to raise_error(ArgumentError)
+ end
+
+ it "with the same version does not add another dependency" do
+ metadata.add_dependency('puppetlabs-origmodule', '1.0.0')
+ subject.dependencies.size.should == 1
+ end
+ end
end
describe '#dashed_name' do
@@ -202,8 +270,8 @@ describe Puppet::ModuleTool::Metadata do
describe "#to_hash" do
subject { metadata.to_hash }
- its(:keys) do
- subject.sort.should == %w[ name version author summary license source issues_url project_page dependencies ].sort
+ it "contains the default set of keys" do
+ subject.keys.sort.should == %w[ name version author summary license source issues_url project_page dependencies ].sort
end
describe "['license']" do
@@ -213,8 +281,8 @@ describe Puppet::ModuleTool::Metadata do
end
describe "['dependencies']" do
- it "defaults to an empty Array" do
- subject['dependencies'].should == []
+ it "defaults to an empty set" do
+ subject['dependencies'].should == Set.new
end
end
diff --git a/spec/unit/module_tool/tar/mini_spec.rb b/spec/unit/module_tool/tar/mini_spec.rb
index ef030611b..179952741 100644
--- a/spec/unit/module_tool/tar/mini_spec.rb
+++ b/spec/unit/module_tool/tar/mini_spec.rb
@@ -54,6 +54,7 @@ describe Puppet::ModuleTool::Tar::Mini, :if => (Puppet.features.minitar? and Pup
reader = mock('GzipReader')
Zlib::GzipReader.expects(:open).with(sourcefile).yields(reader)
- Archive::Tar::Minitar.expects(:unpack).with(reader, destdir).yields(type, name, nil)
+ minitar.expects(:find_valid_files).with(reader).returns([name])
+ Archive::Tar::Minitar.expects(:unpack).with(reader, destdir, [name]).yields(type, name, nil)
end
end
diff --git a/spec/unit/network/authentication_spec.rb b/spec/unit/network/authentication_spec.rb
index 8f3653cad..5e2f2de87 100755
--- a/spec/unit/network/authentication_spec.rb
+++ b/spec/unit/network/authentication_spec.rb
@@ -81,6 +81,10 @@ describe Puppet::Network::Authentication do
cert.stubs(:unmunged_name).returns('foo')
end
+ after(:all) do
+ reload_module
+ end
+
it "should log a warning if a certificate's expiration is near" do
logger.expects(:warning)
subject.warn_if_near_expiration(cert)
diff --git a/spec/unit/network/http/api/v2/environments_spec.rb b/spec/unit/network/http/api/v2/environments_spec.rb
index 6c6d7a581..993e55011 100644
--- a/spec/unit/network/http/api/v2/environments_spec.rb
+++ b/spec/unit/network/http/api/v2/environments_spec.rb
@@ -23,20 +23,41 @@ describe Puppet::Network::HTTP::API::V2::Environments do
"production" => {
"settings" => {
"modulepath" => [File.expand_path("/first"), File.expand_path("/second")],
- "manifest" => File.expand_path("/manifests")
+ "manifest" => File.expand_path("/manifests"),
+ "environment_timeout" => 0,
+ "config_version" => ""
}
}
}
})
end
- it "the response conforms to the environments schema" do
+ it "the response conforms to the environments schema for unlimited timeout" do
+ conf_stub = stub 'conf_stub'
+ conf_stub.expects(:environment_timeout).returns(1.0 / 0.0)
environment = Puppet::Node::Environment.create(:production, [])
- handler = Puppet::Network::HTTP::API::V2::Environments.new(Puppet::Environments::Static.new(environment))
+ env_loader = Puppet::Environments::Static.new(environment)
+ env_loader.expects(:get_conf).with(:production).returns(conf_stub)
+ handler = Puppet::Network::HTTP::API::V2::Environments.new(env_loader)
response = Puppet::Network::HTTP::MemoryResponse.new
handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response)
expect(response.body).to validate_against('api/schemas/environments.json')
end
+
+ it "the response conforms to the environments schema for integer timeout" do
+ conf_stub = stub 'conf_stub'
+ conf_stub.expects(:environment_timeout).returns(1)
+ environment = Puppet::Node::Environment.create(:production, [])
+ env_loader = Puppet::Environments::Static.new(environment)
+ env_loader.expects(:get_conf).with(:production).returns(conf_stub)
+ handler = Puppet::Network::HTTP::API::V2::Environments.new(env_loader)
+ response = Puppet::Network::HTTP::MemoryResponse.new
+
+ handler.call(Puppet::Network::HTTP::Request.from_hash(:headers => { 'accept' => 'application/json' }), response)
+
+ expect(response.body).to validate_against('api/schemas/environments.json')
+ end
+
end
diff --git a/spec/unit/network/http/connection_spec.rb b/spec/unit/network/http/connection_spec.rb
index a5e6f64ae..ef6ca65d6 100644..100755
--- a/spec/unit/network/http/connection_spec.rb
+++ b/spec/unit/network/http/connection_spec.rb
@@ -11,50 +11,30 @@ describe Puppet::Network::HTTP::Connection do
let (:httpok) { Net::HTTPOK.new('1.1', 200, '') }
context "when providing HTTP connections" do
- after do
- Puppet::Network::HTTP::Connection.instance_variable_set("@ssl_host", nil)
- end
-
context "when initializing http instances" do
- before :each do
- # All of the cert stuff is tested elsewhere
- Puppet::Network::HTTP::Connection.stubs(:cert_setup)
- end
-
it "should return an http instance created with the passed host and port" do
- http = subject.send(:connection)
- http.should be_an_instance_of Net::HTTP
- http.address.should == host
- http.port.should == port
+ conn = Puppet::Network::HTTP::Connection.new(host, port, :verify => Puppet::SSL::Validator.no_validator)
+
+ expect(conn.address).to eq(host)
+ expect(conn.port).to eq(port)
end
it "should enable ssl on the http instance by default" do
- http = subject.send(:connection)
- http.should be_use_ssl
- end
+ conn = Puppet::Network::HTTP::Connection.new(host, port, :verify => Puppet::SSL::Validator.no_validator)
- it "can set ssl using an option" do
- Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator).send(:connection).should_not be_use_ssl
- Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => true, :verify => Puppet::SSL::Validator.no_validator).send(:connection).should be_use_ssl
+ expect(conn).to be_use_ssl
end
- context "proxy and timeout settings should propagate" do
- subject { Puppet::Network::HTTP::Connection.new(host, port, :verify => Puppet::SSL::Validator.no_validator).send(:connection) }
- before :each do
- Puppet[:http_proxy_host] = "myhost"
- Puppet[:http_proxy_port] = 432
- Puppet[:configtimeout] = 120
- end
+ it "can disable ssl using an option" do
+ conn = Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator)
- 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] }
+ expect(conn).to_not be_use_ssl
end
- it "should not set a proxy if the value is 'none'" do
- Puppet[:http_proxy_host] = 'none'
- subject.send(:connection).proxy_address.should be_nil
+ it "can enable ssl using an option" do
+ conn = Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => true, :verify => Puppet::SSL::Validator.no_validator)
+
+ expect(conn).to be_use_ssl
end
it "should raise Puppet::Error when invalid options are specified" do
@@ -96,7 +76,44 @@ describe Puppet::Network::HTTP::Connection do
end
end
- context "when validating HTTPS requests" do
+ class ConstantErrorValidator
+ def initialize(args)
+ @fails_with = args[:fails_with]
+ @error_string = args[:error_string] || ""
+ @peer_certs = args[:peer_certs] || []
+ end
+
+ def setup_connection(connection)
+ connection.stubs(:start).raises(OpenSSL::SSL::SSLError.new(@fails_with))
+ end
+
+ def peer_certs
+ @peer_certs
+ end
+
+ def verify_errors
+ [@error_string]
+ end
+ end
+
+ class NoProblemsValidator
+ def initialize(cert)
+ @cert = cert
+ end
+
+ def setup_connection(connection)
+ end
+
+ def peer_certs
+ [@cert]
+ end
+
+ def verify_errors
+ []
+ end
+ end
+
+ shared_examples_for 'ssl verifier' do
include PuppetSpec::Files
let (:host) { "my_server" }
@@ -117,11 +134,11 @@ describe Puppet::Network::HTTP::Connection do
Puppet[:confdir] = tmpdir('conf')
connection = Puppet::Network::HTTP::Connection.new(
- host, port,
- :verify => ConstantErrorValidator.new(
- :fails_with => 'hostname was not match with server certificate',
- :peer_certs => [Puppet::SSL::CertificateAuthority.new.generate(
- 'not_my_server', :dns_alt_names => 'foo,bar,baz')]))
+ host, port,
+ :verify => ConstantErrorValidator.new(
+ :fails_with => 'hostname was not match with server certificate',
+ :peer_certs => [Puppet::SSL::CertificateAuthority.new.generate(
+ 'not_my_server', :dns_alt_names => 'foo,bar,baz')]))
expect do
connection.get('request')
@@ -150,86 +167,104 @@ describe Puppet::Network::HTTP::Connection do
host, port,
:verify => NoProblemsValidator.new(cert))
+ Net::HTTP.any_instance.stubs(:start)
Net::HTTP.any_instance.stubs(:request).returns(httpok)
connection.expects(:warn_if_near_expiration).with(cert)
connection.get('request')
end
+ end
- class ConstantErrorValidator
- def initialize(args)
- @fails_with = args[:fails_with]
- @error_string = args[:error_string] || ""
- @peer_certs = args[:peer_certs] || []
- end
+ context "when using single use HTTPS connections" do
+ it_behaves_like 'ssl verifier' do
+ end
+ end
- def setup_connection(connection)
- connection.stubs(:request).with do
- true
- end.raises(OpenSSL::SSL::SSLError.new(@fails_with))
+ context "when using persistent HTTPS connections" do
+ around :each do |example|
+ pool = Puppet::Network::HTTP::Pool.new
+ Puppet.override(:http_pool => pool) do
+ example.run
end
+ pool.close
+ end
- def peer_certs
- @peer_certs
- end
+ it_behaves_like 'ssl verifier' do
+ end
+ end
- def verify_errors
- [@error_string]
- end
+ context "when response is a redirect" do
+ let (:site) { Puppet::Network::HTTP::Site.new('http', 'my_server', 8140) }
+ let (:other_site) { Puppet::Network::HTTP::Site.new('http', 'redirected', 9292) }
+ let (:other_path) { "other-path" }
+ let (:verify) { Puppet::SSL::Validator.no_validator }
+ let (:subject) { Puppet::Network::HTTP::Connection.new(site.host, site.port, :use_ssl => false, :verify => verify) }
+ let (:httpredirection) do
+ response = Net::HTTPFound.new('1.1', 302, 'Moved Temporarily')
+ response['location'] = "#{other_site.addr}/#{other_path}"
+ response.stubs(:read_body).returns("This resource has moved")
+ response
end
- class NoProblemsValidator
- def initialize(cert)
- @cert = cert
- end
+ def create_connection(site, options)
+ options[:use_ssl] = site.use_ssl?
+ Puppet::Network::HTTP::Connection.new(site.host, site.port, options)
+ end
- def setup_connection(connection)
- end
+ it "should redirect to the final resource location" do
+ http = stub('http')
+ http.stubs(:request).returns(httpredirection).then.returns(httpok)
- def peer_certs
- [@cert]
- end
+ seq = sequence('redirection')
+ pool = Puppet.lookup(:http_pool)
+ pool.expects(:with_connection).with(site, anything).yields(http).in_sequence(seq)
+ pool.expects(:with_connection).with(other_site, anything).yields(http).in_sequence(seq)
- def verify_errors
- []
- end
+ conn = create_connection(site, :verify => verify)
+ conn.get('/foo')
end
- end
- context "when response is a redirect" do
- let (:other_host) { "redirected" }
- let (:other_port) { 9292 }
- let (:other_path) { "other-path" }
- let (:subject) { Puppet::Network::HTTP::Connection.new("my_server", 8140, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator) }
- let (:httpredirection) { Net::HTTPFound.new('1.1', 302, 'Moved Temporarily') }
+ def expects_redirection(conn, &block)
+ http = stub('http')
+ http.stubs(:request).returns(httpredirection)
- before :each do
- httpredirection['location'] = "http://#{other_host}:#{other_port}/#{other_path}"
- httpredirection.stubs(:read_body).returns("This resource has moved")
+ pool = Puppet.lookup(:http_pool)
+ pool.expects(:with_connection).with(site, anything).yields(http)
+ pool
+ end
- socket = stub_everything("socket")
- TCPSocket.stubs(:open).returns(socket)
+ def expects_limit_exceeded(conn)
+ expect {
+ conn.get('/')
+ }.to raise_error(Puppet::Network::HTTP::RedirectionLimitExceededException)
+ end
- Net::HTTP::Get.any_instance.stubs(:exec).returns("")
- Net::HTTP::Post.any_instance.stubs(:exec).returns("")
+ it "should not redirect when the limit is 0" do
+ conn = create_connection(site, :verify => verify, :redirect_limit => 0)
+
+ pool = expects_redirection(conn)
+ pool.expects(:with_connection).with(other_site, anything).never
+
+ expects_limit_exceeded(conn)
end
- it "should redirect to the final resource location" do
- httpok.stubs(:read_body).returns(:body)
- Net::HTTPResponse.stubs(:read_new).returns(httpredirection).then.returns(httpok)
+ it "should redirect only once" do
+ conn = create_connection(site, :verify => verify, :redirect_limit => 1)
+
+ pool = expects_redirection(conn)
+ pool.expects(:with_connection).with(other_site, anything).once
- subject.get("/foo").body.should == :body
- subject.port.should == other_port
- subject.address.should == other_host
+ expects_limit_exceeded(conn)
end
- it "should raise an error after too many redirections" do
- Net::HTTPResponse.stubs(:read_new).returns(httpredirection)
+ it "should raise an exception when the redirect limit is exceeded" do
+ conn = create_connection(site, :verify => verify, :redirect_limit => 3)
- expect {
- subject.get("/foo")
- }.to raise_error(Puppet::Network::HTTP::RedirectionLimitExceededException)
+ pool = expects_redirection(conn)
+ pool.expects(:with_connection).with(other_site, anything).times(3)
+
+ expects_limit_exceeded(conn)
end
end
diff --git a/spec/unit/network/http/factory_spec.rb b/spec/unit/network/http/factory_spec.rb
new file mode 100755
index 000000000..107ededcd
--- /dev/null
+++ b/spec/unit/network/http/factory_spec.rb
@@ -0,0 +1,82 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP::Factory do
+ before :each do
+ Puppet::SSL::Key.indirection.terminus_class = :memory
+ Puppet::SSL::CertificateRequest.indirection.terminus_class = :memory
+ end
+
+ let(:site) { Puppet::Network::HTTP::Site.new('https', 'www.example.com', 443) }
+ def create_connection(site)
+ factory = Puppet::Network::HTTP::Factory.new
+
+ factory.create_connection(site)
+ end
+
+ it 'creates a connection for the site' do
+ conn = create_connection(site)
+
+ expect(conn.use_ssl?).to be_true
+ expect(conn.address).to eq(site.host)
+ expect(conn.port).to eq(site.port)
+ end
+
+ it 'creates a connection that has not yet been started' do
+ conn = create_connection(site)
+
+ expect(conn).to_not be_started
+ end
+
+ it 'creates a connection supporting at least HTTP 1.1' do
+ conn = create_connection(site)
+
+ expect(any_of(conn.class.version_1_1?, conn.class.version_1_1?)).to be_true
+ end
+
+ context "proxy settings" do
+ let(:proxy_host) { 'myhost' }
+ let(:proxy_port) { 432 }
+
+ it "should not set a proxy if the value is 'none'" do
+ Puppet[:http_proxy_host] = 'none'
+ conn = create_connection(site)
+
+ expect(conn.proxy_address).to be_nil
+ end
+
+ it 'sets proxy_address' do
+ Puppet[:http_proxy_host] = proxy_host
+ conn = create_connection(site)
+
+ expect(conn.proxy_address).to eq(proxy_host)
+ end
+
+ it 'sets proxy address and port' do
+ Puppet[:http_proxy_host] = proxy_host
+ Puppet[:http_proxy_port] = proxy_port
+ conn = create_connection(site)
+
+ expect(conn.proxy_port).to eq(proxy_port)
+ end
+
+ context 'socket timeouts' do
+ let(:timeout) { 5 }
+
+ it 'sets open timeout' do
+ Puppet[:configtimeout] = timeout
+ conn = create_connection(site)
+
+ expect(conn.open_timeout).to eq(timeout)
+ end
+
+ it 'sets read timeout' do
+ Puppet[:configtimeout] = timeout
+ conn = create_connection(site)
+
+ expect(conn.read_timeout).to eq(timeout)
+ end
+ end
+ end
+end
diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb
index 345818b4a..25df9d270 100755
--- a/spec/unit/network/http/handler_spec.rb
+++ b/spec/unit/network/http/handler_spec.rb
@@ -103,31 +103,30 @@ describe Puppet::Network::HTTP::Handler do
handler.stubs(:warn_if_near_expiration)
end
- it "should check the client certificate for upcoming expiration" do
- request = a_request
- cert = mock 'cert'
- handler.expects(:client_cert).returns(cert).with(request)
- handler.expects(:warn_if_near_expiration).with(cert)
-
- handler.process(request, response)
- end
-
it "should setup a profiler when the puppet-profiling header exists" do
request = a_request
request[:headers][Puppet::Network::HTTP::HEADER_ENABLE_PROFILING.downcase] = "true"
- handler.process(request, response)
+ p = HandlerTestProfiler.new
+
+ Puppet::Util::Profiler.expects(:add_profiler).with { |profiler|
+ profiler.is_a? Puppet::Util::Profiler::WallClock
+ }.returns(p)
- Puppet::Util::Profiler.current.should be_kind_of(Puppet::Util::Profiler::WallClock)
+ Puppet::Util::Profiler.expects(:remove_profiler).with { |profiler|
+ profiler == p
+ }
+
+ handler.process(request, response)
end
it "should not setup profiler when the profile parameter is missing" do
request = a_request
request[:params] = { }
- handler.process(request, response)
+ Puppet::Util::Profiler.expects(:add_profiler).never
- Puppet::Util::Profiler.current.should == Puppet::Util::Profiler::NONE
+ handler.process(request, response)
end
it "should raise an error if the request is formatted in an unknown format" do
@@ -219,4 +218,15 @@ describe Puppet::Network::HTTP::Handler do
request[:headers] || {}
end
end
+
+ class HandlerTestProfiler
+ def start(metric, description)
+ end
+
+ def finish(context, metric, description)
+ end
+
+ def shutdown()
+ end
+ end
end
diff --git a/spec/unit/network/http/nocache_pool_spec.rb b/spec/unit/network/http/nocache_pool_spec.rb
new file mode 100755
index 000000000..69e2d2e9a
--- /dev/null
+++ b/spec/unit/network/http/nocache_pool_spec.rb
@@ -0,0 +1,43 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+require 'puppet/network/http'
+require 'puppet/network/http/connection'
+
+describe Puppet::Network::HTTP::NoCachePool do
+ let(:site) { Puppet::Network::HTTP::Site.new('https', 'rubygems.org', 443) }
+ let(:verify) { stub('verify', :setup_connection => nil) }
+
+ it 'yields a connection' do
+ http = stub('http')
+
+ factory = Puppet::Network::HTTP::Factory.new
+ factory.stubs(:create_connection).returns(http)
+ pool = Puppet::Network::HTTP::NoCachePool.new(factory)
+
+ expect { |b|
+ pool.with_connection(site, verify, &b)
+ }.to yield_with_args(http)
+ end
+
+ it 'yields a new connection each time' do
+ http1 = stub('http1')
+ http2 = stub('http2')
+
+ factory = Puppet::Network::HTTP::Factory.new
+ factory.stubs(:create_connection).returns(http1).then.returns(http2)
+ pool = Puppet::Network::HTTP::NoCachePool.new(factory)
+
+ expect { |b|
+ pool.with_connection(site, verify, &b)
+ }.to yield_with_args(http1)
+
+ expect { |b|
+ pool.with_connection(site, verify, &b)
+ }.to yield_with_args(http2)
+ end
+
+ it 'has a close method' do
+ Puppet::Network::HTTP::NoCachePool.new.close
+ end
+end
diff --git a/spec/unit/network/http/pool_spec.rb b/spec/unit/network/http/pool_spec.rb
new file mode 100755
index 000000000..aef100953
--- /dev/null
+++ b/spec/unit/network/http/pool_spec.rb
@@ -0,0 +1,269 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+require 'openssl'
+require 'puppet/network/http'
+require 'puppet/network/http_pool'
+
+describe Puppet::Network::HTTP::Pool do
+ before :each do
+ Puppet::SSL::Key.indirection.terminus_class = :memory
+ Puppet::SSL::CertificateRequest.indirection.terminus_class = :memory
+ end
+
+ let(:site) do
+ Puppet::Network::HTTP::Site.new('https', 'rubygems.org', 443)
+ end
+
+ let(:different_site) do
+ Puppet::Network::HTTP::Site.new('https', 'github.com', 443)
+ end
+
+ let(:verify) do
+ stub('verify', :setup_connection => nil)
+ end
+
+ def create_pool
+ Puppet::Network::HTTP::Pool.new
+ end
+
+ def create_pool_with_connections(site, *connections)
+ pool = Puppet::Network::HTTP::Pool.new
+ connections.each do |conn|
+ pool.release(site, conn)
+ end
+ pool
+ end
+
+ def create_pool_with_expired_connections(site, *connections)
+ # setting keepalive timeout to -1 ensures any newly added
+ # connections have already expired
+ pool = Puppet::Network::HTTP::Pool.new(-1)
+ connections.each do |conn|
+ pool.release(site, conn)
+ end
+ pool
+ end
+
+ def create_connection(site)
+ stub(site.addr, :started? => false, :start => nil, :finish => nil, :use_ssl? => true, :verify_mode => OpenSSL::SSL::VERIFY_PEER)
+ end
+
+ context 'when yielding a connection' do
+ it 'yields a connection' do
+ conn = create_connection(site)
+ pool = create_pool_with_connections(site, conn)
+
+ expect { |b|
+ pool.with_connection(site, verify, &b)
+ }.to yield_with_args(conn)
+ end
+
+ it 'returns the connection to the pool' do
+ conn = create_connection(site)
+ pool = create_pool
+ pool.release(site, conn)
+
+ pool.with_connection(site, verify) { |c| }
+
+ expect(pool.pool[site].first.connection).to eq(conn)
+ end
+
+ it 'can yield multiple connections to the same site' do
+ lru_conn = create_connection(site)
+ mru_conn = create_connection(site)
+ pool = create_pool_with_connections(site, lru_conn, mru_conn)
+
+ pool.with_connection(site, verify) do |a|
+ expect(a).to eq(mru_conn)
+
+ pool.with_connection(site, verify) do |b|
+ expect(b).to eq(lru_conn)
+ end
+ end
+ end
+
+ it 'propagates exceptions' do
+ conn = create_connection(site)
+ pool = create_pool
+ pool.release(site, conn)
+
+ expect {
+ pool.with_connection(site, verify) do |c|
+ raise IOError, 'connection reset'
+ end
+ }.to raise_error(IOError, 'connection reset')
+ end
+
+ it 'does not re-cache connections when an error occurs' do
+ # we're not distinguishing between network errors that would
+ # suggest we close the socket, and other errors
+ conn = create_connection(site)
+ pool = create_pool
+ pool.release(site, conn)
+
+ pool.expects(:release).with(site, conn).never
+
+ pool.with_connection(site, verify) do |c|
+ raise IOError, 'connection reset'
+ end rescue nil
+ end
+
+ context 'when releasing connections' do
+ it 'releases HTTP connections' do
+ conn = create_connection(site)
+ conn.expects(:use_ssl?).returns(false)
+
+ pool = create_pool_with_connections(site, conn)
+ pool.expects(:release).with(site, conn)
+
+ pool.with_connection(site, verify) {|c| }
+ end
+
+ it 'releases secure HTTPS connections' do
+ conn = create_connection(site)
+ conn.expects(:use_ssl?).returns(true)
+ conn.expects(:verify_mode).returns(OpenSSL::SSL::VERIFY_PEER)
+
+ pool = create_pool_with_connections(site, conn)
+ pool.expects(:release).with(site, conn)
+
+ pool.with_connection(site, verify) {|c| }
+ end
+
+ it 'closes insecure HTTPS connections' do
+ conn = create_connection(site)
+ conn.expects(:use_ssl?).returns(true)
+ conn.expects(:verify_mode).returns(OpenSSL::SSL::VERIFY_NONE)
+
+ pool = create_pool_with_connections(site, conn)
+
+ pool.expects(:release).with(site, conn).never
+
+ pool.with_connection(site, verify) {|c| }
+ end
+ end
+ end
+
+ context 'when borrowing' do
+ it 'returns a new connection if the pool is empty' do
+ conn = create_connection(site)
+ pool = create_pool
+ pool.factory.expects(:create_connection).with(site).returns(conn)
+
+ expect(pool.borrow(site, verify)).to eq(conn)
+ end
+
+ it 'returns a matching connection' do
+ conn = create_connection(site)
+ pool = create_pool_with_connections(site, conn)
+
+ pool.factory.expects(:create_connection).never
+
+ expect(pool.borrow(site, verify)).to eq(conn)
+ end
+
+ it 'returns a new connection if there are no matching sites' do
+ different_conn = create_connection(different_site)
+ pool = create_pool_with_connections(different_site, different_conn)
+
+ conn = create_connection(site)
+ pool.factory.expects(:create_connection).with(site).returns(conn)
+
+ expect(pool.borrow(site, verify)).to eq(conn)
+ end
+
+ it 'returns started connections' do
+ conn = create_connection(site)
+ conn.expects(:start)
+
+ pool = create_pool
+ pool.factory.expects(:create_connection).with(site).returns(conn)
+
+ expect(pool.borrow(site, verify)).to eq(conn)
+ end
+
+ it "doesn't start a cached connection" do
+ conn = create_connection(site)
+ conn.expects(:start).never
+
+ pool = create_pool_with_connections(site, conn)
+ pool.borrow(site, verify)
+ end
+
+ it 'returns the most recently used connection from the pool' do
+ least_recently_used = create_connection(site)
+ most_recently_used = create_connection(site)
+
+ pool = create_pool_with_connections(site, least_recently_used, most_recently_used)
+ expect(pool.borrow(site, verify)).to eq(most_recently_used)
+ end
+
+ it 'finishes expired connections' do
+ conn = create_connection(site)
+ conn.expects(:finish)
+
+ pool = create_pool_with_expired_connections(site, conn)
+ pool.factory.expects(:create_connection => stub('conn', :start => nil))
+
+ pool.borrow(site, verify)
+ end
+
+ it 'logs an exception if it fails to close an expired connection' do
+ Puppet.expects(:log_exception).with(is_a(IOError), "Failed to close connection for #{site}: read timeout")
+
+ conn = create_connection(site)
+ conn.expects(:finish).raises(IOError, 'read timeout')
+
+ pool = create_pool_with_expired_connections(site, conn)
+ pool.factory.expects(:create_connection => stub('open_conn', :start => nil))
+
+ pool.borrow(site, verify)
+ end
+ end
+
+ context 'when releasing a connection' do
+ it 'adds the connection to an empty pool' do
+ conn = create_connection(site)
+
+ pool = create_pool
+ pool.release(site, conn)
+
+ expect(pool.pool[site].first.connection).to eq(conn)
+ end
+
+ it 'adds the connection to a pool with a connection for the same site' do
+ pool = create_pool
+ pool.release(site, create_connection(site))
+ pool.release(site, create_connection(site))
+
+ expect(pool.pool[site].count).to eq(2)
+ end
+
+ it 'adds the connection to a pool with a connection for a different site' do
+ pool = create_pool
+ pool.release(site, create_connection(site))
+ pool.release(different_site, create_connection(different_site))
+
+ expect(pool.pool[site].count).to eq(1)
+ expect(pool.pool[different_site].count).to eq(1)
+ end
+ end
+
+ context 'when closing' do
+ it 'clears the pool' do
+ pool = create_pool
+ pool.close
+
+ expect(pool.pool).to be_empty
+ end
+
+ it 'closes all cached connections' do
+ conn = create_connection(site)
+ conn.expects(:finish)
+
+ pool = create_pool_with_connections(site, conn)
+ pool.close
+ end
+ end
+end
diff --git a/spec/unit/network/http/rack/rest_spec.rb b/spec/unit/network/http/rack/rest_spec.rb
index 165b6ceb9..c35b789a2 100755
--- a/spec/unit/network/http/rack/rest_spec.rb
+++ b/spec/unit/network/http/rack/rest_spec.rb
@@ -12,11 +12,11 @@ describe "Puppet::Network::HTTP::RackREST", :if => Puppet.features.rack? 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
+ @handler = Puppet::Network::HTTP::RackREST.new(:handler => :foo)
end
def mk_req(uri, opts = {})
diff --git a/spec/unit/network/http/session_spec.rb b/spec/unit/network/http/session_spec.rb
new file mode 100755
index 000000000..4eba67d7d
--- /dev/null
+++ b/spec/unit/network/http/session_spec.rb
@@ -0,0 +1,43 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP::Session do
+ let(:connection) { stub('connection') }
+
+ def create_session(connection, expiration_time = nil)
+ expiration_time ||= Time.now + 60 * 60
+
+ Puppet::Network::HTTP::Session.new(connection, expiration_time)
+ end
+
+ it 'provides access to its connection' do
+ session = create_session(connection)
+
+ session.connection.should == connection
+ end
+
+ it 'expires a connection whose expiration time is in the past' do
+ now = Time.now
+ past = now - 1
+
+ session = create_session(connection, past)
+ session.expired?(now).should be_true
+ end
+
+ it 'expires a connection whose expiration time is now' do
+ now = Time.now
+
+ session = create_session(connection, now)
+ session.expired?(now).should be_true
+ end
+
+ it 'does not expire a connection whose expiration time is in the future' do
+ now = Time.now
+ future = now + 1
+
+ session = create_session(connection, future)
+ session.expired?(now).should be_false
+ end
+end
diff --git a/spec/unit/network/http/site_spec.rb b/spec/unit/network/http/site_spec.rb
new file mode 100755
index 000000000..06fcbf83d
--- /dev/null
+++ b/spec/unit/network/http/site_spec.rb
@@ -0,0 +1,90 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP::Site do
+ let(:scheme) { 'https' }
+ let(:host) { 'rubygems.org' }
+ let(:port) { 443 }
+
+ def create_site(scheme, host, port)
+ Puppet::Network::HTTP::Site.new(scheme, host, port)
+ end
+
+ it 'accepts scheme, host, and port' do
+ site = create_site(scheme, host, port)
+
+ expect(site.scheme).to eq(scheme)
+ expect(site.host).to eq(host)
+ expect(site.port).to eq(port)
+ end
+
+ it 'generates an external URI string' do
+ site = create_site(scheme, host, port)
+
+ expect(site.addr).to eq("https://rubygems.org:443")
+ end
+
+ it 'considers sites to be different when the scheme is different' do
+ https_site = create_site('https', host, port)
+ http_site = create_site('http', host, port)
+
+ expect(https_site).to_not eq(http_site)
+ end
+
+ it 'considers sites to be different when the host is different' do
+ rubygems_site = create_site(scheme, 'rubygems.org', port)
+ github_site = create_site(scheme, 'github.com', port)
+
+ expect(rubygems_site).to_not eq(github_site)
+ end
+
+ it 'considers sites to be different when the port is different' do
+ site_443 = create_site(scheme, host, 443)
+ site_80 = create_site(scheme, host, 80)
+
+ expect(site_443).to_not eq(site_80)
+ end
+
+ it 'compares values when determining equality' do
+ site = create_site(scheme, host, port)
+
+ sites = {}
+ sites[site] = site
+
+ another_site = create_site(scheme, host, port)
+
+ expect(sites.include?(another_site)).to be_true
+ end
+
+ it 'computes the same hash code for equivalent objects' do
+ site = create_site(scheme, host, port)
+ same_site = create_site(scheme, host, port)
+
+ expect(site.hash).to eq(same_site.hash)
+ end
+
+ it 'uses ssl with https' do
+ site = create_site('https', host, port)
+
+ expect(site).to be_use_ssl
+ end
+
+ it 'does not use ssl with http' do
+ site = create_site('http', host, port)
+
+ expect(site).to_not be_use_ssl
+ end
+
+ it 'moves to a new URI location' do
+ site = create_site('http', 'host1', 80)
+
+ uri = URI.parse('https://host2:443/some/where/else')
+ new_site = site.move_to(uri)
+
+ expect(new_site.scheme).to eq('https')
+ expect(new_site.host).to eq('host2')
+ expect(new_site.port).to eq(443)
+ end
+end
diff --git a/spec/unit/network/http/webrick_spec.rb b/spec/unit/network/http/webrick_spec.rb
index 17f61e339..edeb439a9 100755
--- a/spec/unit/network/http/webrick_spec.rb
+++ b/spec/unit/network/http/webrick_spec.rb
@@ -127,7 +127,7 @@ describe Puppet::Network::HTTP::WEBrick do
server.setup_logger
end
- it "should use the masterlog if the run_mode is master" do
+ it "should use the masterhttplog if the run_mode is master" do
Puppet.run_mode.stubs(:master?).returns(true)
log = make_absolute("/master/log")
Puppet[:masterhttplog] = log
diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb
index d8b84232e..a9c5783f2 100755
--- a/spec/unit/network/http_pool_spec.rb
+++ b/spec/unit/network/http_pool_spec.rb
@@ -9,7 +9,6 @@ describe Puppet::Network::HttpPool do
end
describe "when managing http instances" do
-
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 Puppet::Network::HTTP::Connection
@@ -47,7 +46,6 @@ describe Puppet::Network::HttpPool do
Puppet::Network::HttpPool.http_instance("me", 54321, true).should be_use_ssl
end
-
describe 'peer verification' do
def setup_standard_ssl_configuration
ca_cert_file = File.expand_path('/path/to/ssl/certs/ca_cert.pem')
@@ -77,12 +75,18 @@ describe Puppet::Network::HttpPool do
setup_standard_ssl_host
end
- it 'can enable peer verification' do
- Puppet::Network::HttpPool.http_instance("me", 54321, true, true).send(:connection).verify_mode.should == OpenSSL::SSL::VERIFY_PEER
+ it 'enables peer verification by default' do
+ response = Net::HTTPOK.new('1.1', 200, 'body')
+ conn = Puppet::Network::HttpPool.http_instance("me", 54321, true)
+ conn.expects(:execute_request).with { |http, request| expect(http.verify_mode).to eq(OpenSSL::SSL::VERIFY_PEER) }.returns(response)
+ conn.get('/')
end
it 'can disable peer verification' do
- Puppet::Network::HttpPool.http_instance("me", 54321, true, false).send(:connection).verify_mode.should == OpenSSL::SSL::VERIFY_NONE
+ response = Net::HTTPOK.new('1.1', 200, 'body')
+ conn = Puppet::Network::HttpPool.http_instance("me", 54321, true, false)
+ conn.expects(:execute_request).with { |http, request| expect(http.verify_mode).to eq(OpenSSL::SSL::VERIFY_NONE) }.returns(response)
+ conn.get('/')
end
end
@@ -91,5 +95,4 @@ describe Puppet::Network::HttpPool do
should_not equal(Puppet::Network::HttpPool.http_instance("me", 54321))
end
end
-
end
diff --git a/spec/unit/network/http_spec.rb b/spec/unit/network/http_spec.rb
new file mode 100755
index 000000000..4a149d3a8
--- /dev/null
+++ b/spec/unit/network/http_spec.rb
@@ -0,0 +1,10 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/network/http'
+
+describe Puppet::Network::HTTP do
+ it 'defines an http_pool context' do
+ pool = Puppet.lookup(:http_pool)
+ expect(pool).to be_a(Puppet::Network::HTTP::NoCachePool)
+ end
+end
diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb
index 0881b8576..3df5d329c 100755
--- a/spec/unit/node/environment_spec.rb
+++ b/spec/unit/node/environment_spec.rb
@@ -43,6 +43,42 @@ describe Puppet::Node::Environment do
Puppet::Node::Environment.new(one).should equal(one)
end
+ describe "equality" do
+ it "works as a hash key" do
+ base = Puppet::Node::Environment.create(:first, ["modules"], "manifests")
+ same = Puppet::Node::Environment.create(:first, ["modules"], "manifests")
+ different = Puppet::Node::Environment.create(:first, ["different"], "manifests")
+ hash = {}
+
+ hash[base] = "base env"
+ hash[same] = "same env"
+ hash[different] = "different env"
+
+ expect(hash[base]).to eq("same env")
+ expect(hash[different]).to eq("different env")
+ expect(hash).to have(2).item
+ end
+
+ it "is equal when name, modules, and manifests are the same" do
+ base = Puppet::Node::Environment.create(:base, ["modules"], "manifests")
+ different_name = Puppet::Node::Environment.create(:different, base.full_modulepath, base.manifest)
+
+ expect(base).to_not eq("not an environment")
+
+ expect(base).to eq(base)
+ expect(base.hash).to eq(base.hash)
+
+ expect(base.override_with(:modulepath => ["different"])).to_not eq(base)
+ expect(base.override_with(:modulepath => ["different"]).hash).to_not eq(base.hash)
+
+ expect(base.override_with(:manifest => "different")).to_not eq(base)
+ expect(base.override_with(:manifest => "different").hash).to_not eq(base.hash)
+
+ expect(different_name).to_not eq(base)
+ expect(different_name.hash).to_not eq(base.hash)
+ end
+ end
+
describe "overriding an existing environment" do
let(:original_path) { [tmpdir('original')] }
let(:new_path) { [tmpdir('new')] }
@@ -151,6 +187,60 @@ describe Puppet::Node::Environment do
end
end
+ it "does not register conflicting_manifest_settings? when not using directory environments" do
+ expect(Puppet::Node::Environment.create(:directory, [], '/some/non/default/manifest.pp').conflicting_manifest_settings?).to be_false
+ end
+
+ describe "when operating in the context of directory environments" do
+ before(:each) do
+ Puppet[:environmentpath] = "$confdir/environments"
+ Puppet[:default_manifest] = "/default/manifests/site.pp"
+ end
+
+ it "has no conflicting_manifest_settings? when disable_per_environment_manifest is false" do
+ expect(Puppet::Node::Environment.create(:directory, [], '/some/non/default/manifest.pp').conflicting_manifest_settings?).to be_false
+ end
+
+ context "when disable_per_environment_manifest is true" do
+ let(:config) { mock('config') }
+ let(:global_modulepath) { ["/global/modulepath"] }
+ let(:envconf) { Puppet::Settings::EnvironmentConf.new("/some/direnv", config, global_modulepath) }
+
+ before(:each) do
+ Puppet[:disable_per_environment_manifest] = true
+ end
+
+ def assert_manifest_conflict(expectation, envconf_manifest_value)
+ config.expects(:setting).with(:manifest).returns(
+ mock('setting', :value => envconf_manifest_value)
+ )
+ environment = Puppet::Node::Environment.create(:directory, [], '/default/manifests/site.pp')
+ loader = Puppet::Environments::Static.new(environment)
+ loader.stubs(:get_conf).returns(envconf)
+
+ Puppet.override(:environments => loader) do
+ expect(environment.conflicting_manifest_settings?).to eq(expectation)
+ end
+ end
+
+ it "has conflicting_manifest_settings when environment.conf manifest was set" do
+ assert_manifest_conflict(true, '/some/envconf/manifest/site.pp')
+ end
+
+ it "does not have conflicting_manifest_settings when environment.conf manifest is empty" do
+ assert_manifest_conflict(false, '')
+ end
+
+ it "does not have conflicting_manifest_settings when environment.conf manifest is nil" do
+ assert_manifest_conflict(false, nil)
+ end
+
+ it "does not have conflicting_manifest_settings when environment.conf manifest is an exact, uninterpolated match of default_manifest" do
+ assert_manifest_conflict(false, '/default/manifests/site.pp')
+ end
+ 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
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 85ec6e127..2de2b8279 100755
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -157,11 +157,9 @@ describe Puppet::Node do
Puppet::Node.should read_json_attribute('parameters').from(@node.to_pson).as({"a" => "b", "c" => "d"})
end
- it "should include the environment" do
- Puppet.override(:environments => env_loader) do
- @node.environment = environment
- Puppet::Node.should read_json_attribute('environment').from(@node.to_pson).as(environment)
- end
+ it "deserializes environment to environment_name as a string" do
+ @node.environment = environment
+ Puppet::Node.should read_json_attribute('environment_name').from(@node.to_pson).as('bar')
end
end
end
diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb
index d1e855a20..0ca01f521 100755
--- a/spec/unit/parser/compiler_spec.rb
+++ b/spec/unit/parser/compiler_spec.rb
@@ -97,6 +97,13 @@ describe Puppet::Parser::Compiler do
@compiler.environment.should equal(@node.environment)
end
+ it "fails if the node's environment has conflicting manifest settings" do
+ conflicted_environment = Puppet::Node::Environment.create(:testing, [], '/some/environment.conf/manifest.pp')
+ conflicted_environment.stubs(:conflicting_manifest_settings?).returns(true)
+ @node.environment = conflicted_environment
+ expect { Puppet::Parser::Compiler.compile(@node) }.to raise_error(Puppet::Error, /disable_per_environment_manifest.*true.*environment.conf.*manifest.*conflict/)
+ end
+
it "should include the resource type collection helper" do
Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper)
end
@@ -681,7 +688,7 @@ describe Puppet::Parser::Compiler do
it "should skip classes that have already been evaluated" do
@compiler.catalog.stubs(:tag)
- @scope.stubs(:class_scope).with(@class).returns("something")
+ @scope.stubs(:class_scope).with(@class).returns(@scope)
@compiler.expects(:add_resource).never
@@ -694,7 +701,7 @@ describe Puppet::Parser::Compiler do
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")
+ @scope.stubs(:class_scope).with(@class).returns(@scope)
@compiler.expects(:add_resource).never
@resource.expects(:evaluate).never
Puppet::Parser::Resource.expects(:new).never
diff --git a/spec/unit/parser/eparser_adapter_spec.rb b/spec/unit/parser/eparser_adapter_spec.rb
deleted file mode 100644
index 173cfb783..000000000
--- a/spec/unit/parser/eparser_adapter_spec.rb
+++ /dev/null
@@ -1,407 +0,0 @@
-#! /usr/bin/env ruby
-require 'spec_helper'
-require 'puppet/parser/e_parser_adapter'
-
-describe Puppet::Parser do
-
- Puppet::Parser::AST
-
- before :each do
- @known_resource_types = Puppet::Resource::TypeCollection.new("development")
- @classic_parser = Puppet::Parser::Parser.new "development"
- @parser = Puppet::Parser::EParserAdapter.new(@classic_parser)
- @classic_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::EParserAdapter.new
- }.to raise_error(ArgumentError, /wrong number of arguments/)
- end
-
- describe "when parsing append operator" do
-
- it "should not raise syntax errors" do
- expect { @parser.parse("$var += something") }.to_not raise_error
- end
-
- it "should raise syntax error on incomplete syntax " do
- expect {
- @parser.parse("$var += ")
- }.to raise_error(Puppet::ParseError, /Syntax error at end of file/)
- 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 }") }.to_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 { }") }.to_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 }")
- }.to raise_error(Puppet::ParseError, /Syntax error at '\)'/)
- end
-
- end
-
- describe "when parsing resource references" do
-
- it "should not raise syntax errors" do
- expect { @parser.parse('exec { test: param => File["a"] }') }.to_not raise_error
- end
-
- it "should not raise syntax errors with multiple references" do
- expect { @parser.parse('exec { test: param => File["a","b"] }') }.to_not raise_error
- end
-
- it "should create an ast::ResourceReference" do
- # NOTE: In egrammar, type and name are unified immediately to lower case whereas the regular grammar
- # keeps the UC name in some contexts - it gets downcased later as the name of the type is in lower case.
- #
- Puppet::Parser::AST::ResourceReference.expects(:new).with { |arg|
- arg[:line]==1 and arg[:pos] ==25 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 }') }.to_not raise_error
- end
-
- it "should not raise syntax errors with multiple overrides" do
- expect { @parser.parse('Resource["title1","title2"] { param => value }') }.to_not raise_error
- end
-
- it "should create an ast::ResourceOverride" do
- 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 { }") }.to_not raise_error
- end
-
- it "should not raise errors with empty else" do
- expect { @parser.parse("if false { notice('if') } else { }") }.to_not raise_error
- end
-
- it "should not raise errors with empty if and else" do
- expect { @parser.parse("if false { } else { }") }.to_not raise_error
- end
-
- it "should create a nop node for empty branch" do
- Puppet::Parser::AST::Nop.expects(:new).twice
- @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) }.to_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()") }.to_not raise_error
- end
-
- it "should not raise errors with rvalue function with no args" do
- expect { @parser.parse("$a = template()") }.to_not raise_error
- end
-
- it "should not raise errors with arguments" do
- expect { @parser.parse("notice(1)") }.to_not raise_error
- end
-
- it "should not raise errors with multiple arguments" do
- expect { @parser.parse("notice(1,2)") }.to_not raise_error
- end
-
- it "should not raise errors with multiple arguments and a trailing comma" do
- expect { @parser.parse("notice(1,2,)") }.to_not raise_error
- end
-
- end
-
- describe "when parsing arrays" do
- it "should parse an array" do
- expect { @parser.parse("$a = [1,2]") }.to_not raise_error
- end
-
- it "should not raise errors with a trailing comma" do
- expect { @parser.parse("$a = [1,2,]") }.to_not raise_error
- end
-
- it "should accept an empty array" do
- expect { @parser.parse("$var = []\n") }.to_not raise_error
- end
- end
-
- describe "when parsing classes" do
- before :each do
- @krt = Puppet::Resource::TypeCollection.new("development")
- @classic_parser = Puppet::Parser::Parser.new "development"
- @parser = Puppet::Parser::EParserAdapter.new(@classic_parser)
- @classic_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")
- @classic_parser = Puppet::Parser::Parser.new "development"
- @parser = Puppet::Parser::EParserAdapter.new(@classic_parser)
- @classic_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 }") }.to_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, /Defaults are not virtualizable/)
- 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, /Cannot assign to variables in other namespaces/)
- 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
-
- it "should treat classes as case insensitive" do
- @classic_parser.known_resource_types.import_ast(@parser.parse("class yayness {}"), '')
- @classic_parser.known_resource_types.hostclass('yayness').
- should == @classic_parser.find_hostclass("", "YayNess")
- end
-
- it "should treat defines as case insensitive" do
- @classic_parser.known_resource_types.import_ast(@parser.parse("define funtest {}"), '')
- @classic_parser.known_resource_types.hostclass('funtest').
- should == @classic_parser.find_hostclass("", "fUntEst")
- end
- context "when parsing method calls" do
- it "should parse method call with one param lambda" do
- expect { @parser.parse("$a.each |$a|{ debug $a }") }.to_not raise_error
- end
- it "should parse method call with two param lambda" do
- expect { @parser.parse("$a.each |$a,$b|{ debug $a }") }.to_not raise_error
- end
- it "should parse method call with two param lambda and default value" do
- expect { @parser.parse("$a.each |$a,$b=1|{ debug $a }") }.to_not raise_error
- end
- it "should parse method call without lambda (statement)" do
- expect { @parser.parse("$a.each") }.to_not raise_error
- end
- it "should parse method call without lambda (expression)" do
- expect { @parser.parse("$x = $a.each + 1") }.to_not raise_error
- end
- context "a receiver expression of type" do
- it "variable should be allowed" do
- expect { @parser.parse("$a.each") }.to_not raise_error
- end
- it "hasharrayaccess should be allowed" do
- expect { @parser.parse("$a[0][1].each") }.to_not raise_error
- end
- it "quoted text should be allowed" do
- expect { @parser.parse("\"monkey\".each") }.to_not raise_error
- expect { @parser.parse("'monkey'.each") }.to_not raise_error
- end
- it "selector text should be allowed" do
- expect { @parser.parse("$a ? { 'banana'=>[1,2,3]}.each") }.to_not raise_error
- end
- it "function call should be allowed" do
- expect { @parser.parse("duh(1,2,3).each") }.to_not raise_error
- end
- it "method call should be allowed" do
- expect { @parser.parse("$a.foo.bar") }.to_not raise_error
- end
- it "chained method calls with lambda should be allowed" do
- expect { @parser.parse("$a.foo||{}.bar||{}") }.to_not raise_error
- end
- end
- end
-end
diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb
index 3eb43b276..020ce740b 100755
--- a/spec/unit/parser/files_spec.rb
+++ b/spec/unit/parser/files_spec.rb
@@ -12,6 +12,25 @@ describe Puppet::Parser::Files do
@basepath = make_absolute("/somepath")
end
+ describe "when searching for files" do
+ it "should return fully-qualified files directly" do
+ Puppet::Parser::Files.expects(:modulepath).never
+ Puppet::Parser::Files.find_file(@basepath + "/my/file", environment).should == @basepath + "/my/file"
+ end
+
+ it "should return the first found file" do
+ mod = mock 'module'
+ mod.expects(:file).returns("/one/mymod/files/myfile")
+ environment.expects(:module).with("mymod").returns mod
+
+ Puppet::Parser::Files.find_file("mymod/myfile", environment).should == "/one/mymod/files/myfile"
+ end
+
+ it "should return nil if template is not found" do
+ Puppet::Parser::Files.find_file("foomod/myfile", environment).should be_nil
+ end
+ end
+
describe "when searching for templates" do
it "should return fully-qualified templates directly" do
Puppet::Parser::Files.expects(:modulepath).never
diff --git a/spec/unit/parser/functions/contain_spec.rb b/spec/unit/parser/functions/contain_spec.rb
index 3150e0c8e..2a5aa57c7 100644
--- a/spec/unit/parser/functions/contain_spec.rb
+++ b/spec/unit/parser/functions/contain_spec.rb
@@ -3,11 +3,15 @@ require 'spec_helper'
require 'puppet_spec/compiler'
require 'puppet/parser/functions'
require 'matchers/containment_matchers'
+require 'matchers/resource'
require 'matchers/include_in_order'
+require 'unit/parser/functions/shared'
+
describe 'The "contain" function' do
include PuppetSpec::Compiler
include ContainmentMatchers
+ include Matchers::Resource
it "includes the class" do
catalog = compile_to_catalog(<<-MANIFEST)
@@ -25,6 +29,41 @@ describe 'The "contain" function' do
expect(catalog.classes).to include("contained")
end
+ it "includes the class when using a fully qualified anchored name" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain ::contained
+ }
+
+ include container
+ MANIFEST
+
+ expect(catalog.classes).to include("contained")
+ end
+
+ it "ensures that the edge is with the correct class" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class outer {
+ class named { }
+ contain named
+ }
+
+ class named { }
+
+ include named
+ include outer
+ MANIFEST
+
+ expect(catalog).to have_resource("Class[Named]")
+ expect(catalog).to have_resource("Class[Outer]")
+ expect(catalog).to have_resource("Class[Outer::Named]")
+ expect(catalog).to contain_class("outer::named").in("outer")
+ end
+
it "makes the class contained in the current class" do
catalog = compile_to_catalog(<<-MANIFEST)
class contained {
@@ -182,4 +221,16 @@ describe 'The "contain" function' do
)
end
end
+
+ describe "When the future parser is in use" do
+ require 'puppet/pops'
+ before(:each) do
+ Puppet[:parser] = 'future'
+ compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo"))
+ @scope = Puppet::Parser::Scope.new(compiler)
+ end
+
+ it_should_behave_like 'all functions transforming relative to absolute names', :function_contain
+ it_should_behave_like 'an inclusion function, regardless of the type of class reference,', :contain
+ end
end
diff --git a/spec/unit/parser/functions/create_resources_spec.rb b/spec/unit/parser/functions/create_resources_spec.rb
index 79ed02f22..3e7bd8015 100755
--- a/spec/unit/parser/functions/create_resources_spec.rb
+++ b/spec/unit/parser/functions/create_resources_spec.rb
@@ -49,7 +49,7 @@ describe 'function for dynamically creating resources' do
end
it 'should be able to add exported resources' do
- catalog = compile_to_catalog("create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}})")
+ catalog = compile_to_catalog("create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}}) realize(File['/etc/foo'])")
catalog.resource(:file, "/etc/foo")['ensure'].should == 'present'
catalog.resource(:file, "/etc/foo").exported.should == true
end
@@ -202,5 +202,12 @@ describe 'function for dynamically creating resources' do
catalog.resource(:notify, "test")['message'].should == 'two'
catalog.resource(:class, "bar").should_not be_nil
end
+
+ it 'should fail with a correct error message if the syntax of an imported file is incorrect' do
+ expect{
+ Puppet[:modulepath] = my_fixture_dir
+ compile_to_catalog('include foo')
+ }.to raise_error(Puppet::Error, /Syntax error at.*/)
+ end
end
end
diff --git a/spec/unit/parser/functions/digest_spec.rb b/spec/unit/parser/functions/digest_spec.rb
new file mode 100755
index 000000000..e3c0762d4
--- /dev/null
+++ b/spec/unit/parser/functions/digest_spec.rb
@@ -0,0 +1,31 @@
+#!/usr/bin/env rspec
+require 'spec_helper'
+
+describe "the digest function", :uses_checksums => true do
+ before :all do
+ Puppet::Parser::Functions.autoloader.loadall
+ end
+
+ before :each do
+ n = Puppet::Node.new('unnamed')
+ c = Puppet::Parser::Compiler.new(n)
+ @scope = Puppet::Parser::Scope.new(c)
+ end
+
+ it "should exist" do
+ Puppet::Parser::Functions.function("digest").should == "function_digest"
+ end
+
+ with_digest_algorithms do
+ it "should use the proper digest function" do
+ result = @scope.function_digest([plaintext])
+ result.should(eql( checksum ))
+ end
+
+ it "should only accept one parameter" do
+ expect do
+ @scope.function_digest(['foo', 'bar'])
+ end.to raise_error(ArgumentError)
+ end
+ end
+end
diff --git a/spec/unit/parser/functions/file_spec.rb b/spec/unit/parser/functions/file_spec.rb
index 34d12e2f2..c5f157300 100755
--- a/spec/unit/parser/functions/file_spec.rb
+++ b/spec/unit/parser/functions/file_spec.rb
@@ -13,10 +13,6 @@ describe "the 'file' function" do
let :compiler do Puppet::Parser::Compiler.new(node) end
let :scope do Puppet::Parser::Scope.new(compiler) 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')
@@ -31,7 +27,17 @@ describe "the 'file' function" do
end
end
- it "should return the first file if given two files" do
+ it "should read a file from a module path" do
+ with_file_content('file content') do |name|
+ mod = mock 'module'
+ mod.stubs(:file).with('myfile').returns(name)
+ compiler.environment.stubs(:module).with('mymod').returns(mod)
+
+ scope.function_file(['mymod/myfile']).should == 'file content'
+ end
+ end
+
+ it "should return the first file if given two files with absolute paths" do
with_file_content('one') do |one|
with_file_content('two') do |two|
scope.function_file([one, two]).should == "one"
@@ -39,6 +45,43 @@ describe "the 'file' function" do
end
end
+ it "should return the first file if given two files with module paths" do
+ with_file_content('one') do |one|
+ with_file_content('two') do |two|
+ mod = mock 'module'
+ compiler.environment.expects(:module).with('mymod').returns(mod)
+ mod.expects(:file).with('one').returns(one)
+ mod.stubs(:file).with('two').returns(two)
+
+ scope.function_file(['mymod/one','mymod/two']).should == 'one'
+ end
+ end
+ end
+
+ it "should return the first file if given two files with mixed paths, absolute first" do
+ with_file_content('one') do |one|
+ with_file_content('two') do |two|
+ mod = mock 'module'
+ compiler.environment.stubs(:module).with('mymod').returns(mod)
+ mod.stubs(:file).with('two').returns(two)
+
+ scope.function_file([one,'mymod/two']).should == 'one'
+ end
+ end
+ end
+
+ it "should return the first file if given two files with mixed paths, module first" do
+ with_file_content('one') do |one|
+ with_file_content('two') do |two|
+ mod = mock 'module'
+ compiler.environment.expects(:module).with('mymod').returns(mod)
+ mod.stubs(:file).with('two').returns(two)
+
+ scope.function_file(['mymod/two',one]).should == 'two'
+ end
+ end
+ end
+
it "should not fail when some files are absent" do
expect {
with_file_content('one') do |one|
diff --git a/spec/unit/parser/functions/include_spec.rb b/spec/unit/parser/functions/include_spec.rb
index c1a5cbd5c..3fa0da35d 100755
--- a/spec/unit/parser/functions/include_spec.rb
+++ b/spec/unit/parser/functions/include_spec.rb
@@ -1,5 +1,6 @@
#! /usr/bin/env ruby
require 'spec_helper'
+require 'unit/parser/functions/shared'
describe "the 'include' function" do
before :all do
@@ -46,6 +47,19 @@ describe "the 'include' function" do
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
+ expect { @scope.function_include(["nosuchclass"]) }.to raise_error(Puppet::Error)
+ end
+
+ describe "When the future parser is in use" do
+ require 'puppet/pops'
+ require 'puppet_spec/compiler'
+ include PuppetSpec::Compiler
+
+ before(:each) do
+ Puppet[:parser] = 'future'
+ end
+
+ it_should_behave_like 'all functions transforming relative to absolute names', :function_include
+ it_should_behave_like 'an inclusion function, regardless of the type of class reference,', :include
end
end
diff --git a/spec/unit/parser/functions/realize_spec.rb b/spec/unit/parser/functions/realize_spec.rb
index 9f53f5a76..79e5eb155 100755
--- a/spec/unit/parser/functions/realize_spec.rb
+++ b/spec/unit/parser/functions/realize_spec.rb
@@ -1,53 +1,61 @@
-#! /usr/bin/env ruby
require 'spec_helper'
+require 'matchers/resource'
+require 'puppet_spec/compiler'
describe "the realize function" do
- before :all do
- Puppet::Parser::Functions.autoloader.loadall
- end
+ include Matchers::Resource
+ include PuppetSpec::Compiler
- before :each do
- @collector = stub_everything 'collector'
- node = Puppet::Node.new('localhost')
- @compiler = Puppet::Parser::Compiler.new(node)
- @scope = Puppet::Parser::Scope.new(@compiler)
- @compiler.stubs(:add_collection).with(@collector)
- end
+ it "realizes a single, referenced resource" do
+ catalog = compile_to_catalog(<<-EOM)
+ @notify { testing: }
+ realize(Notify[testing])
+ EOM
- it "should exist" do
- Puppet::Parser::Functions.function("realize").should == "function_realize"
+ expect(catalog).to have_resource("Notify[testing]")
end
- it "should create a Collector when called" do
-
- Puppet::Parser::Collector.expects(:new).returns(@collector)
+ it "realizes multiple resources" do
+ catalog = compile_to_catalog(<<-EOM)
+ @notify { testing: }
+ @notify { other: }
+ realize(Notify[testing], Notify[other])
+ EOM
- @scope.function_realize(["test"])
+ expect(catalog).to have_resource("Notify[testing]")
+ expect(catalog).to have_resource("Notify[other]")
end
- it "should assign the passed-in resources to the collector" do
- Puppet::Parser::Collector.stubs(:new).returns(@collector)
+ it "realizes resources provided in arrays" do
+ catalog = compile_to_catalog(<<-EOM)
+ @notify { testing: }
+ @notify { other: }
+ realize([Notify[testing], [Notify[other]]])
+ EOM
- @collector.expects(:resources=).with(["test"])
-
- @scope.function_realize(["test"])
+ expect(catalog).to have_resource("Notify[testing]")
+ expect(catalog).to have_resource("Notify[other]")
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"]])
+ it "fails when the resource does not exist" do
+ expect do
+ compile_to_catalog(<<-EOM)
+ realize(Notify[missing])
+ EOM
+ end.to raise_error(Puppet::Error, /Failed to realize/)
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"])
+ it "fails when no parameters given" do
+ expect do
+ compile_to_catalog(<<-EOM)
+ realize()
+ EOM
+ end.to raise_error(Puppet::Error, /Wrong number of arguments/)
end
+ it "silently does nothing when an empty array of resources is given" do
+ compile_to_catalog(<<-EOM)
+ realize([])
+ EOM
+ end
end
diff --git a/spec/unit/parser/functions/require_spec.rb b/spec/unit/parser/functions/require_spec.rb
index 72c3f9f5f..f0b4fcc28 100755
--- a/spec/unit/parser/functions/require_spec.rb
+++ b/spec/unit/parser/functions/require_spec.rb
@@ -1,5 +1,6 @@
#! /usr/bin/env ruby
require 'spec_helper'
+require 'unit/parser/functions/shared'
describe "the require function" do
before :all do
@@ -26,13 +27,13 @@ describe "the require function" do
end
it "should delegate to the 'include' puppet function" do
- @scope.expects(:function_include).with(["myclass"])
+ @scope.compiler.expects(:evaluate_classes).with(["myclass"], @scope, false)
@scope.function_require(["myclass"])
end
- it "should set the 'require' prarameter on the resource to a resource reference" do
- @scope.stubs(:function_include)
+ it "should set the 'require' parameter on the resource to a resource reference" do
+ @scope.compiler.stubs(:evaluate_classes)
@scope.function_require(["myclass"])
@resource["require"].should be_instance_of(Array)
@@ -40,7 +41,7 @@ describe "the require function" do
end
it "should lookup the absolute class path" do
- @scope.stubs(:function_include)
+ @scope.compiler.stubs(:evaluate_classes)
@scope.expects(:find_hostclass).with("myclass").returns(@klass)
@klass.expects(:name).returns("myclass")
@@ -49,7 +50,7 @@ describe "the require function" do
end
it "should append the required class to the require parameter" do
- @scope.stubs(:function_include)
+ @scope.compiler.stubs(:evaluate_classes)
one = Puppet::Resource.new(:file, "/one")
@resource[:require] = one
@@ -58,4 +59,17 @@ describe "the require function" do
@resource[:require].should be_include(one)
@resource[:require].detect { |r| r.to_s == "Class[Myclass]" }.should be_instance_of(Puppet::Resource)
end
+
+ describe "When the future parser is in use" do
+ require 'puppet/pops'
+ require 'puppet_spec/compiler'
+ include PuppetSpec::Compiler
+
+ before(:each) do
+ Puppet[:parser] = 'future'
+ end
+
+ it_should_behave_like 'all functions transforming relative to absolute names', :function_require
+ it_should_behave_like 'an inclusion function, regardless of the type of class reference,', :require
+ end
end
diff --git a/spec/unit/parser/functions/search_spec.rb b/spec/unit/parser/functions/search_spec.rb
index b2c042b04..54054bd6a 100755
--- a/spec/unit/parser/functions/search_spec.rb
+++ b/spec/unit/parser/functions/search_spec.rb
@@ -20,4 +20,9 @@ describe "the 'search' function" do
scope.expects(:add_namespace).with("who")
scope.function_search(["where", "what", "who"])
end
+
+ it "is deprecated" do
+ Puppet.expects(:deprecation_warning).with("The 'search' function is deprecated. See http://links.puppetlabs.com/search-function-deprecation")
+ scope.function_search(['wat'])
+ end
end
diff --git a/spec/unit/parser/functions/shared.rb b/spec/unit/parser/functions/shared.rb
new file mode 100644
index 000000000..f5adcd811
--- /dev/null
+++ b/spec/unit/parser/functions/shared.rb
@@ -0,0 +1,82 @@
+shared_examples_for 'all functions transforming relative to absolute names' do |func_method|
+
+ it 'transforms relative names to absolute' do
+ @scope.compiler.expects(:evaluate_classes).with(["::myclass"], @scope, false)
+ @scope.send(func_method, ["myclass"])
+ end
+
+ it 'accepts a Class[name] type' do
+ @scope.compiler.expects(:evaluate_classes).with(["::myclass"], @scope, false)
+ @scope.send(func_method, [Puppet::Pops::Types::TypeFactory.host_class('myclass')])
+ end
+
+ it 'accepts a Resource[class, name] type' do
+ @scope.compiler.expects(:evaluate_classes).with(["::myclass"], @scope, false)
+ @scope.send(func_method, [Puppet::Pops::Types::TypeFactory.resource('class', 'myclass')])
+ end
+
+ it 'raises and error for unspecific Class' do
+ expect {
+ @scope.send(func_method, [Puppet::Pops::Types::TypeFactory.host_class()])
+ }.to raise_error(ArgumentError, /Cannot use an unspecific Class\[\] Type/)
+ end
+
+ it 'raises and error for Resource that is not of class type' do
+ expect {
+ @scope.send(func_method, [Puppet::Pops::Types::TypeFactory.resource('file')])
+ }.to raise_error(ArgumentError, /Cannot use a Resource\[file\] where a Resource\['class', name\] is expected/)
+ end
+
+ it 'raises and error for Resource that is unspecific' do
+ expect {
+ @scope.send(func_method, [Puppet::Pops::Types::TypeFactory.resource()])
+ }.to raise_error(ArgumentError, /Cannot use an unspecific Resource\[\] where a Resource\['class', name\] is expected/)
+ end
+
+ it 'raises and error for Resource[class] that is unspecific' do
+ expect {
+ @scope.send(func_method, [Puppet::Pops::Types::TypeFactory.resource('class')])
+ }.to raise_error(ArgumentError, /Cannot use an unspecific Resource\['class'\] where a Resource\['class', name\] is expected/)
+ end
+
+end
+
+shared_examples_for 'an inclusion function, regardless of the type of class reference,' do |function|
+
+ it "and #{function} a class absolutely, even when a relative namespaced class of the same name is present" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo {
+ class bar { }
+ #{function} bar
+ }
+ class bar { }
+ include foo
+ MANIFEST
+ expect(catalog.classes).to include('foo','bar')
+ end
+
+ it "and #{function} a class absolutely by Class['type'] reference" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo {
+ class bar { }
+ #{function} Class['bar']
+ }
+ class bar { }
+ include foo
+ MANIFEST
+ expect(catalog.classes).to include('foo','bar')
+ end
+
+ it "and #{function} a class absolutely by Resource['type','title'] reference" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class foo {
+ class bar { }
+ #{function} Resource['class','bar']
+ }
+ class bar { }
+ include foo
+ MANIFEST
+ expect(catalog.classes).to include('foo','bar')
+ end
+
+end
diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb
index b3c04d1af..3c6266752 100755
--- a/spec/unit/parser/functions_spec.rb
+++ b/spec/unit/parser/functions_spec.rb
@@ -38,7 +38,7 @@ describe Puppet::Parser::Functions do
it "instruments the function to profile the execution" do
messages = []
- Puppet::Util::Profiler.current = Puppet::Util::Profiler::WallClock.new(proc { |msg| messages << msg }, "id")
+ Puppet::Util::Profiler.add_profiler(Puppet::Util::Profiler::WallClock.new(proc { |msg| messages << msg }, "id"))
Puppet::Parser::Functions.newfunction("name", :type => :rvalue) { |args| }
callable_functions_from(function_module).function_name([])
@@ -85,10 +85,7 @@ describe Puppet::Parser::Functions do
end
describe "when calling function to test arity" do
- let(:function_module) { Module.new }
- before do
- Puppet::Parser::Functions.stubs(:environment_module).returns(function_module)
- end
+ let(:function_module) { Puppet::Parser::Functions.environment_module(Puppet.lookup(:current_environment)) }
it "should raise an error if the function is called with too many arguments" do
Puppet::Parser::Functions.newfunction("name", :arity => 2) { |args| }
diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb
index 62234e214..f0f10e9f3 100755
--- a/spec/unit/parser/lexer_spec.rb
+++ b/spec/unit/parser/lexer_spec.rb
@@ -279,7 +279,8 @@ describe Puppet::Parser::Lexer::TOKENS[:NAME] do
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"]
+ lexer = stub("lexer")
+ @token.convert(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
@@ -845,6 +846,14 @@ describe "Puppet::Parser::Lexer in the old tests" do
end
end
+describe 'Puppet::Parser::Lexer handles reserved words' do
+ ['function', 'private', 'attr', 'type'].each do |reserved_bare_word|
+ it "by delivering '#{reserved_bare_word}' as a bare word" do
+ expect(tokens_scanned_from(reserved_bare_word)).to eq([[:NAME, {:value=>reserved_bare_word, :line => 1}]])
+ end
+ 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
diff --git a/spec/unit/parser/methods/map_spec.rb b/spec/unit/parser/methods/map_spec.rb
deleted file mode 100644
index 7f8e79789..000000000
--- a/spec/unit/parser/methods/map_spec.rb
+++ /dev/null
@@ -1,184 +0,0 @@
-require 'puppet'
-require 'spec_helper'
-require 'puppet_spec/compiler'
-
-require 'unit/parser/methods/shared'
-
-describe 'the map method' do
- include PuppetSpec::Compiler
-
- before :each do
- Puppet[:parser] = "future"
- end
-
- context "using future parser" do
- it 'map on an array (multiplying each value by 2)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1,2,3]
- $a.map |$x|{ $x*2}.each |$v|{
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_4")['ensure'].should == 'present'
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- end
-
- it 'map on an enumerable type (multiplying each value by 2)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = Integer[1,3]
- $a.map |$x|{ $x*2}.each |$v|{
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_4")['ensure'].should == 'present'
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- end
-
- it 'map on an integer (multiply each by 3)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- 3.map |$x|{ $x*3}.each |$v|{
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_0")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- end
-
- it 'map on a string' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {a=>x, b=>y}
- "ab".map |$x|{$a[$x]}.each |$v|{
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_x")['ensure'].should == 'present'
- catalog.resource(:file, "/file_y")['ensure'].should == 'present'
- end
-
- it 'map on an array (multiplying value by 10 in even index position)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1,2,3]
- $a.map |$i, $x|{ if $i % 2 == 0 {$x} else {$x*10}}.each |$v|{
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_20")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
-
- it 'map on a hash selecting keys' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.map |$x|{ $x[0]}.each |$k|{
- file { "/file_$k": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_a")['ensure'].should == 'present'
- catalog.resource(:file, "/file_b")['ensure'].should == 'present'
- catalog.resource(:file, "/file_c")['ensure'].should == 'present'
- end
-
- it 'map on a hash selecting keys - using two block parameters' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.map |$k,$v|{ file { "/file_$k": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_a")['ensure'].should == 'present'
- catalog.resource(:file, "/file_b")['ensure'].should == 'present'
- catalog.resource(:file, "/file_c")['ensure'].should == 'present'
- end
-
- it 'each on a hash selecting value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.map |$x|{ $x[1]}.each |$k|{ file { "/file_$k": ensure => present } }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
-
- it 'each on a hash selecting value - using two bloc parameters' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.map |$k,$v|{ file { "/file_$v": ensure => present } }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
-
- context "handles data type corner cases" do
- it "map gets values that are false" do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [false,false]
- $a.map |$x| { $x }.each |$i, $v| {
- file { "/file_$i.$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_0.false")['ensure'].should == 'present'
- catalog.resource(:file, "/file_1.false")['ensure'].should == 'present'
- end
-
- it "map gets values that are nil" do
- Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args|
- [nil]
- end
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = nil_array()
- $a.map |$x| { $x }.each |$i, $v| {
- file { "/file_$i.$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_0.")['ensure'].should == 'present'
- end
-
- it "map gets values that are undef" do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [$does_not_exist]
- $a.map |$x = "something"| { $x }.each |$i, $v| {
- file { "/file_$i.$v": ensure => present }
- }
- MANIFEST
- catalog.resource(:file, "/file_0.something")['ensure'].should == 'present'
- end
- end
-
- context 'map checks arguments and' do
- it 'raises an error when block has more than 2 argument' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].map |$index, $x, $yikes|{ }
- MANIFEST
- end.to raise_error(Puppet::Error, /block must define at most two parameters/)
- end
-
- it 'raises an error when block has fewer than 1 argument' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].map || { }
- MANIFEST
- end.to raise_error(Puppet::Error, /block must define at least one parameter/)
- end
- end
-
- it_should_behave_like 'all iterative functions argument checks', 'map'
- it_should_behave_like 'all iterative functions hash handling', 'map'
- end
-end
diff --git a/spec/unit/parser/methods/shared.rb b/spec/unit/parser/methods/shared.rb
deleted file mode 100644
index 42cfd2359..000000000
--- a/spec/unit/parser/methods/shared.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-
-shared_examples_for 'all iterative functions hash handling' do |func|
- it 'passes a hash entry as an array of the key and value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- {a=>1}.#{func} |$v| { notify { "${v[0]} ${v[1]}": } }
- MANIFEST
-
- catalog.resource(:notify, "a 1").should_not be_nil
- end
-end
-
-shared_examples_for 'all iterative functions argument checks' do |func|
-
- it 'raises an error when used against an unsupported type' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- 3.14.#{func} |$v| { }
- MANIFEST
- end.to raise_error(Puppet::Error, /must be something enumerable/)
- end
-
- it 'raises an error when called with any parameters besides a block' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].#{func}(1) |$v| { }
- MANIFEST
- end.to raise_error(Puppet::Error, /Wrong number of arguments/)
- end
-
- it 'raises an error when called without a block' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].#{func}()
- MANIFEST
- end.to raise_error(Puppet::Error, /Wrong number of arguments/)
- end
-
- it 'raises an error when called without a block' do
- expect do
- compile_to_catalog(<<-MANIFEST)
- [1].#{func}(1)
- MANIFEST
- end.to raise_error(Puppet::Error, /must be a parameterized block/)
- end
-end
diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb
index 659ffa942..5454528a7 100755
--- a/spec/unit/parser/type_loader_spec.rb
+++ b/spec/unit/parser/type_loader_spec.rb
@@ -3,7 +3,6 @@ require 'spec_helper'
require 'puppet/parser/type_loader'
require 'puppet/parser/parser_factory'
-require 'puppet/parser/e_parser_adapter'
require 'puppet_spec/modules'
require 'puppet_spec/files'
diff --git a/spec/unit/pops/benchmark_spec.rb b/spec/unit/pops/benchmark_spec.rb
index 03c2e743d..462c03947 100644
--- a/spec/unit/pops/benchmark_spec.rb
+++ b/spec/unit/pops/benchmark_spec.rb
@@ -118,7 +118,7 @@ $a = "interpolate ${foo} and stuff"
end
context "Measure Evaluator" do
- let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new }
+ let(:parser) { Puppet::Pops::Parser::EvaluatingParser.new }
let(:node) { 'node.example.com' }
let(:scope) { s = create_test_scope_for_node(node); s }
it "evaluator", :profile => true do
diff --git a/spec/unit/pops/binder/bindings_composer_spec.rb b/spec/unit/pops/binder/bindings_composer_spec.rb
index 93bc44722..d8e4b6f9c 100644
--- a/spec/unit/pops/binder/bindings_composer_spec.rb
+++ b/spec/unit/pops/binder/bindings_composer_spec.rb
@@ -34,31 +34,33 @@ describe 'BinderComposer' do
it 'should load everything without errors' do
Puppet.settings[:confdir] = config_directory
Puppet.settings[:libdir] = File.join(config_directory, 'lib')
- Puppet.settings[:modulepath] = File.join(config_directory, 'modules')
- # this ensure the binder is active at the right time
- # (issues with getting a /dev/null path for "confdir" / "libdir")
- raise "Binder not active" unless scope.compiler.is_binder_active?
- diagnostics = diag
- composer = Puppet::Pops::Binder::BindingsComposer.new()
- the_scope = scope
- the_scope['fqdn'] = 'localhost'
- the_scope['environment'] = 'production'
- layered_bindings = composer.compose(scope)
- # puts Puppet::Pops::Binder::BindingsModelDumper.new().dump(layered_bindings)
- binder = Puppet::Pops::Binder::Binder.new(layered_bindings)
- injector = Puppet::Pops::Binder::Injector.new(binder)
- expect(injector.lookup(scope, 'awesome_x')).to be == 'golden'
- expect(injector.lookup(scope, 'good_x')).to be == 'golden'
- expect(injector.lookup(scope, 'rotten_x')).to be == nil
- expect(injector.lookup(scope, 'the_meaning_of_life')).to be == 42
- expect(injector.lookup(scope, 'has_funny_hat')).to be == 'the pope'
- expect(injector.lookup(scope, 'all your base')).to be == 'are belong to us'
- expect(injector.lookup(scope, 'env_meaning_of_life')).to be == 'production thinks it is 42'
- expect(injector.lookup(scope, '::quick::brown::fox')).to be == 'echo: quick brown fox'
+ Puppet.override(:environments => Puppet::Environments::Static.new(Puppet::Node::Environment.create(:production, [File.join(config_directory, 'modules')]))) do
+ # this ensure the binder is active at the right time
+ # (issues with getting a /dev/null path for "confdir" / "libdir")
+ raise "Binder not active" unless scope.compiler.is_binder_active?
+
+ diagnostics = diag
+ composer = Puppet::Pops::Binder::BindingsComposer.new()
+ the_scope = scope
+ the_scope['fqdn'] = 'localhost'
+ the_scope['environment'] = 'production'
+ layered_bindings = composer.compose(scope)
+ # puts Puppet::Pops::Binder::BindingsModelDumper.new().dump(layered_bindings)
+ binder = Puppet::Pops::Binder::Binder.new(layered_bindings)
+ injector = Puppet::Pops::Binder::Injector.new(binder)
+ expect(injector.lookup(scope, 'awesome_x')).to be == 'golden'
+ expect(injector.lookup(scope, 'good_x')).to be == 'golden'
+ expect(injector.lookup(scope, 'rotten_x')).to be == nil
+ expect(injector.lookup(scope, 'the_meaning_of_life')).to be == 42
+ expect(injector.lookup(scope, 'has_funny_hat')).to be == 'the pope'
+ expect(injector.lookup(scope, 'all your base')).to be == 'are belong to us'
+ expect(injector.lookup(scope, 'env_meaning_of_life')).to be == 'production thinks it is 42'
+ expect(injector.lookup(scope, '::quick::brown::fox')).to be == 'echo: quick brown fox'
+ end
end
end
# TODO: test error conditions (see BinderConfigChecker for what to test)
-end \ No newline at end of file
+end
diff --git a/spec/unit/pops/binder/injector_spec.rb b/spec/unit/pops/binder/injector_spec.rb
index 32eba3260..b62ceaeb9 100644
--- a/spec/unit/pops/binder/injector_spec.rb
+++ b/spec/unit/pops/binder/injector_spec.rb
@@ -101,14 +101,16 @@ describe 'Injector' do
let(:bindings) { factory.named_bindings('test') }
let(:scope) { null_scope()}
- let(:duck_type) { type_factory.ruby(InjectorSpecModule::TestDuck) }
-
let(:binder) { Puppet::Pops::Binder::Binder }
let(:lbinder) do
binder.new(layered_bindings)
end
+ def duck_type
+ # create distinct instances
+ type_factory.ruby(InjectorSpecModule::TestDuck)
+ end
let(:layered_bindings) { factory.layered_bindings(test_layer_with_bindings(bindings.model)) }
@@ -528,7 +530,7 @@ describe 'Injector' do
expect {
the_ducks = injector(lbinder).lookup(scope, hash_of_duck, "donalds_nephews")
- }.to_not raise_error(/Duplicate key/)
+ }.to_not raise_error
end
it "should produce detailed type error message" do
@@ -592,11 +594,11 @@ describe 'Injector' do
it "should fail attempts to append, perform uniq or flatten on type incompatible multibind hash" do
hash_of_integer = type_factory.hash_of(type_factory.integer())
ids = ["ducks1", "ducks2", "ducks3"]
- mb = bindings.multibind(ids[0]).type(hash_of_integer).name('broken_family0')
+ mb = bindings.multibind(ids[0]).type(hash_of_integer.copy).name('broken_family0')
mb.producer_options(:conflict_resolution => :append)
- mb = bindings.multibind(ids[1]).type(hash_of_integer).name('broken_family1')
+ mb = bindings.multibind(ids[1]).type(hash_of_integer.copy).name('broken_family1')
mb.producer_options(:flatten => :true)
- mb = bindings.multibind(ids[2]).type(hash_of_integer).name('broken_family2')
+ mb = bindings.multibind(ids[2]).type(hash_of_integer.copy).name('broken_family2')
mb.producer_options(:uniq => :true)
injector = injector(binder.new(factory.layered_bindings(test_layer_with_bindings(bindings.model))))
diff --git a/spec/unit/pops/evaluator/access_ops_spec.rb b/spec/unit/pops/evaluator/access_ops_spec.rb
index d0965ad5d..0fa4779a0 100644
--- a/spec/unit/pops/evaluator/access_ops_spec.rb
+++ b/spec/unit/pops/evaluator/access_ops_spec.rb
@@ -236,7 +236,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do
it "Tuple parameterization gives an error if parameter is not a type" do
expr = fqr('Tuple')['String']
- expect { evaluate(expr)}.to raise_error(/Tuple-Type, Cannot use String where Abstract-Type is expected/)
+ expect { evaluate(expr)}.to raise_error(/Tuple-Type, Cannot use String where Any-Type is expected/)
end
it 'produces a varargs Tuple when the last two arguments specify size constraint' do
@@ -415,12 +415,12 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl/AccessOperator' do
# Ruby Type
#
it 'creates a Ruby Type instance when applied to a Ruby Type' do
- type_expr = fqr('Ruby')['String']
+ type_expr = fqr('Runtime')['ruby','String']
tf = Puppet::Pops::Types::TypeFactory
expect(evaluate(type_expr)).to eql(tf.ruby_type('String'))
# arguments are flattened
- type_expr = fqr('Ruby')[['String']]
+ type_expr = fqr('Runtime')[['ruby', 'String']]
expect(evaluate(type_expr)).to eql(tf.ruby_type('String'))
end
diff --git a/spec/unit/pops/evaluator/comparison_ops_spec.rb b/spec/unit/pops/evaluator/comparison_ops_spec.rb
index 29938542e..e3564195c 100644
--- a/spec/unit/pops/evaluator/comparison_ops_spec.rb
+++ b/spec/unit/pops/evaluator/comparison_ops_spec.rb
@@ -88,10 +88,13 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
end
context "of booleans" do
- it "true == true == true" do; evaluate(literal(true) == literal(true)).should == true ; end;
- it "false == false == true" do; evaluate(literal(false) == literal(false)).should == true ; end;
- it "true == false != true" do; evaluate(literal(true) == literal(false)).should == false ; end;
- it "false == '' == false" do; evaluate(literal(false) == literal('')).should == false ; end;
+ it "true == true == true" do; evaluate(literal(true) == literal(true)).should == true ; end;
+ it "false == false == true" do; evaluate(literal(false) == literal(false)).should == true ; end;
+ it "true == false != true" do; evaluate(literal(true) == literal(false)).should == false ; end;
+ it "false == '' == false" do; evaluate(literal(false) == literal('')).should == false ; end;
+ it "undef == '' == false" do; evaluate(literal(:undef) == literal('')).should == false ; end;
+ it "undef == undef == true" do; evaluate(literal(:undef) == literal(:undef)).should == true ; end;
+ it "nil == undef == true" do; evaluate(literal(nil) == literal(:undef)).should == true ; end;
end
context "of collections" do
diff --git a/spec/unit/pops/evaluator/evaluating_parser_spec.rb b/spec/unit/pops/evaluator/evaluating_parser_spec.rb
index 210b55b20..5e80e4076 100644
--- a/spec/unit/pops/evaluator/evaluating_parser_spec.rb
+++ b/spec/unit/pops/evaluator/evaluating_parser_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
require 'puppet/pops'
require 'puppet/pops/evaluator/evaluator_impl'
+require 'puppet/loaders'
require 'puppet_spec/pops'
require 'puppet_spec/scope'
require 'puppet/parser/e4_parser_adapter'
@@ -16,18 +17,29 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
before(:each) do
Puppet[:strict_variables] = true
- # These must be set since the is 3x logic that triggers on these even if the tests are explicit
- # about selection of parser and evaluator
+ # These must be set since the 3x logic switches some behaviors on these even if the tests explicitly
+ # use the 4x parser and evaluator.
#
Puppet[:parser] = 'future'
- Puppet[:evaluator] = 'future'
+
# Puppetx cannot be loaded until the correct parser has been set (injector is turned off otherwise)
require 'puppetx'
+
+ # Tests needs a known configuration of node/scope/compiler since it parses and evaluates
+ # snippets as the compiler will evaluate them, butwithout the overhead of compiling a complete
+ # catalog for each tested expression.
+ #
+ @parser = Puppet::Pops::Parser::EvaluatingParser.new
+ @node = Puppet::Node.new('node.example.com')
+ @node.environment = Puppet::Node::Environment.create(:testing, [])
+ @compiler = Puppet::Parser::Compiler.new(@node)
+ @scope = Puppet::Parser::Scope.new(@compiler)
+ @scope.source = Puppet::Resource::Type.new(:node, 'node.example.com')
+ @scope.parent = @compiler.topscope
end
- let(:parser) { Puppet::Pops::Parser::EvaluatingParser::Transitional.new }
- let(:node) { 'node.example.com' }
- let(:scope) { s = create_test_scope_for_node(node); s }
+ let(:parser) { @parser }
+ let(:scope) { @scope }
types = Puppet::Pops::Types::TypeFactory
context "When evaluator evaluates literals" do
@@ -206,7 +218,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"'a' !~ 'b.*'" => true,
'$x = a; a =~ "$x.*"' => true,
"a =~ Pattern['a.*']" => true,
- "a =~ Regexp['a.*']" => true,
+ "a =~ Regexp['a.*']" => false, # String is not subtype of Regexp. PUP-957
"$x = /a.*/ a =~ $x" => true,
"$x = Pattern['a.*'] a =~ $x" => true,
"1 =~ Integer" => true,
@@ -240,6 +252,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"/ana/ in bananas" => true,
"/xxx/ in bananas" => false,
"ANA in bananas" => false, # ANA is a type, not a String
+ "String[1] in bananas" => false, # Philosophically true though :-)
"'ANA' in bananas" => true,
"ana in 'BANANAS'" => true,
"/ana/ in 'BANANAS'" => false,
@@ -278,7 +291,20 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
end
{
- 'Object' => ['Data', 'Scalar', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Collection',
+ "if /(ana)/ in bananas {$1}" => 'ana',
+ "if /(xyz)/ in bananas {$1} else {$1}" => nil,
+ "$a = bananas =~ /(ana)/; $b = /(xyz)/ in bananas; $1" => 'ana',
+ "$a = xyz =~ /(xyz)/; $b = /(ana)/ in bananas; $1" => 'ana',
+ "if /p/ in [pineapple, bananas] {$0}" => 'p',
+ "if /b/ in [pineapple, bananas] {$0}" => 'b',
+ }.each do |source, result|
+ it "sets match variables for a regexp search using in such that '#{source}' produces '#{result}'" do
+ parser.evaluate_string(scope, source, __FILE__).should == result
+ end
+ end
+
+ {
+ 'Any' => ['Data', 'Scalar', 'Numeric', 'Integer', 'Float', 'Boolean', 'String', 'Pattern', 'Collection',
'Array', 'Hash', 'CatalogEntry', 'Resource', 'Class', 'Undef', 'File', 'NotYetKnownResourceType'],
# Note, Data > Collection is false (so not included)
@@ -364,11 +390,21 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
context "on strings requiring boxing to Numeric" do
{
"'2' + '2'" => 4,
+ "'-2' + '2'" => 0,
+ "'- 2' + '2'" => 0,
+ '"-\t 2" + "2"' => 0,
+ "'+2' + '2'" => 4,
+ "'+ 2' + '2'" => 4,
"'2.2' + '2.2'" => 4.4,
+ "'-2.2' + '2.2'" => 0.0,
"'0xF7' + '010'" => 0xFF,
"'0xF7' + '0x8'" => 0xFF,
"'0367' + '010'" => 0xFF,
"'012.3' + '010'" => 20.3,
+ "'-0x2' + '0x4'" => 2,
+ "'+0x2' + '0x4'" => 6,
+ "'-02' + '04'" => 2,
+ "'+02' + '04'" => 6,
}.each do |source, result|
it "should parse and evaluate the expression '#{source}' to #{result}" do
parser.evaluate_string(scope, source, __FILE__).should == result
@@ -380,6 +416,10 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"'0xWTF' + '010'" => :error,
"'0x12.3' + '010'" => :error,
"'0x12.3' + '010'" => :error,
+ '"-\n 2" + "2"' => :error,
+ '"-\v 2" + "2"' => :error,
+ '"-2\n" + "2"' => :error,
+ '"-2\n " + "2"' => :error,
}.each do |source, result|
it "should parse and raise error for '#{source}'" do
expect { parser.evaluate_string(scope, source, __FILE__) }.to raise_error(Puppet::ParseError)
@@ -395,8 +435,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"$a = 5; $a" => 5,
"$a = 5; $b = 6; $a" => 5,
"$a = $b = 5; $a == $b" => true,
- "$a = [1,2,3]; [x].map |$x| { $a += x; $a }" => [[1,2,3,'x']],
- "$a = [a,x,c]; [x].map |$x| { $a -= x; $a }" => [['a','c']],
}.each do |source, result|
it "should parse and evaluate the expression '#{source}' to #{result}" do
parser.evaluate_string(scope, source, __FILE__).should == result
@@ -404,13 +442,11 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
end
{
- "[a,b,c] = [1,2,3]; $a == 1 and $b == 2 and $c == 3" => :error,
- "[a,b,c] = {b=>2,c=>3,a=>1}; $a == 1 and $b == 2 and $c == 3" => :error,
- "$a = [1,2,3]; [x].collect |$x| { [a] += x; $a }" => :error,
- "$a = [a,x,c]; [x].collect |$x| { [a] -= x; $a }" => :error,
+ "[a,b,c] = [1,2,3]" => /attempt to assign to 'an Array Expression'/,
+ "[a,b,c] = {b=>2,c=>3,a=>1}" => /attempt to assign to 'an Array Expression'/,
}.each do |source, result|
- it "should parse and evaluate the expression '#{source}' to #{result}" do
- expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Puppet::ParseError)
+ it "should parse and evaluate the expression '#{source}' to error with #{result}" do
+ expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(Puppet::ParseError, result)
end
end
end
@@ -458,6 +494,9 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"case Integer {
Integer : { no }
Type[Integer] : { yes } }" => 'yes',
+ # supports unfold
+ "case ringo {
+ *[paul, john, ringo, george] : { 'beatle' } }" => 'beatle',
}.each do |source, result|
it "should parse and evaluate the expression '#{source}' to #{result}" do
@@ -467,16 +506,42 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
{
"2 ? { 1 => no, 2 => yes}" => 'yes',
- "3 ? { 1 => no, 2 => no}" => nil,
"3 ? { 1 => no, 2 => no, default => yes }" => 'yes',
- "3 ? { 1 => no, default => yes, 3 => no }" => 'yes',
+ "3 ? { 1 => no, default => yes, 3 => no }" => 'no',
+ "3 ? { 1 => no, 3 => no, default => yes }" => 'no',
+ "4 ? { 1 => no, default => yes, 3 => no }" => 'yes',
+ "4 ? { 1 => no, 3 => no, default => yes }" => 'yes',
"'banana' ? { /.*(ana).*/ => $1 }" => 'ana',
"[2] ? { Array[String] => yes, Array => yes}" => 'yes',
+ "ringo ? *[paul, john, ringo, george] => 'beatle'" => 'beatle',
}.each do |source, result|
it "should parse and evaluate the expression '#{source}' to #{result}" do
parser.evaluate_string(scope, source, __FILE__).should == result
end
end
+
+ it 'fails if a selector does not match' do
+ expect{parser.evaluate_string(scope, "2 ? 3 => 4")}.to raise_error(/No matching entry for selector parameter with value '2'/)
+ end
+ end
+
+ context "When evaluator evaluated unfold" do
+ {
+ "*[1,2,3]" => [1,2,3],
+ "*1" => [1],
+ "*'a'" => ['a']
+ }.each do |source, result|
+ it "should parse and evaluate the expression '#{source}' to #{result}" do
+ parser.evaluate_string(scope, source, __FILE__).should == result
+ end
+ end
+
+ it "should parse and evaluate the expression '*{a=>10, b=>20} to [['a',10],['b',20]]" do
+ result = parser.evaluate_string(scope, '*{a=>10, b=>20}', __FILE__)
+ expect(result).to include(['a', 10])
+ expect(result).to include(['b', 20])
+ end
+
end
context "When evaluator performs [] operations" do
@@ -501,6 +566,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"[1,2,3,4][-5,-3]" => [1,2],
"[1,2,3,4][-6,-3]" => [1,2],
"[1,2,3,4][2,-3]" => [],
+ "[1,*[2,3],4]" => [1,2,3,4],
+ "[1,*[2,3],4][1]" => 2,
}.each do |source, result|
it "should parse and evaluate the expression '#{source}' to #{result}" do
parser.evaluate_string(scope, source, __FILE__).should == result
@@ -651,24 +718,82 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
adapted_parser = Puppet::Parser::E4ParserAdapter.new
adapted_parser.file = __FILE__
ast = adapted_parser.parse(source)
- scope.known_resource_types.import_ast(ast, '')
- ast.code.safeevaluate(scope).should == 'chocolate'
+ Puppet.override({:global_scope => scope}, "test") do
+ scope.known_resource_types.import_ast(ast, '')
+ ast.code.safeevaluate(scope).should == 'chocolate'
+ end
end
# Resource default and override expressions and resource parameter access with []
{
+ # Properties
"notify { id: message=>explicit} Notify[id][message]" => "explicit",
"Notify { message=>by_default} notify {foo:} Notify[foo][message]" => "by_default",
"notify {foo:} Notify[foo]{message =>by_override} Notify[foo][message]" => "by_override",
+ # Parameters
+ "notify { id: withpath=>explicit} Notify[id][withpath]" => "explicit",
+ "Notify { withpath=>by_default } notify { foo: } Notify[foo][withpath]" => "by_default",
+ "notify {foo:}
+ Notify[foo]{withpath=>by_override}
+ Notify[foo][withpath]" => "by_override",
+ # Metaparameters
"notify { foo: tag => evoe} Notify[foo][tag]" => "evoe",
- # Does not produce the defaults for tag
+ # Does not produce the defaults for tag parameter (title, type or names of scopes)
"notify { foo: } Notify[foo][tag]" => nil,
+ # But a default may be specified on the type
+ "Notify { tag=>by_default } notify { foo: } Notify[foo][tag]" => "by_default",
+ "Notify { tag=>by_default }
+ notify { foo: }
+ Notify[foo]{ tag=>by_override }
+ Notify[foo][tag]" => "by_override",
}.each do |source, result|
it "should parse and evaluate the expression '#{source}' to #{result}" do
parser.evaluate_string(scope, source, __FILE__).should == result
end
end
+ # Virtual and realized resource default and overridden resource parameter access with []
+ {
+ # Properties
+ "@notify { id: message=>explicit } Notify[id][message]" => "explicit",
+ "@notify { id: message=>explicit }
+ realize Notify[id]
+ Notify[id][message]" => "explicit",
+ "Notify { message=>by_default } @notify { id: } Notify[id][message]" => "by_default",
+ "Notify { message=>by_default }
+ @notify { id: tag=>thisone }
+ Notify <| tag == thisone |>;
+ Notify[id][message]" => "by_default",
+ "@notify { id: } Notify[id]{message=>by_override} Notify[id][message]" => "by_override",
+ # Parameters
+ "@notify { id: withpath=>explicit } Notify[id][withpath]" => "explicit",
+ "Notify { withpath=>by_default }
+ @notify { id: }
+ Notify[id][withpath]" => "by_default",
+ "@notify { id: }
+ realize Notify[id]
+ Notify[id]{withpath=>by_override}
+ Notify[id][withpath]" => "by_override",
+ # Metaparameters
+ "@notify { id: tag=>explicit } Notify[id][tag]" => "explicit",
+ }.each do |source, result|
+ it "parses and evaluates virtual and realized resources in the expression '#{source}' to #{result}" do
+ expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)
+ end
+ end
+
+ # Exported resource attributes
+ {
+ "@@notify { id: message=>explicit } Notify[id][message]" => "explicit",
+ "@@notify { id: message=>explicit, tag=>thisone }
+ Notify <<| tag == thisone |>>
+ Notify[id][message]" => "explicit",
+ }.each do |source, result|
+ it "parses and evaluates exported resources in the expression '#{source}' to #{result}" do
+ expect(parser.evaluate_string(scope, source, __FILE__)).to eq(result)
+ end
+ end
+
# Resource default and override expressions and resource parameter access error conditions
{
"notify { xid: message=>explicit} Notify[id][message]" => /Resource not found/,
@@ -676,6 +801,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
# NOTE: these meta-esque parameters are not recognized as such
"notify { id: message=>explicit} Notify[id][title]" => /does not have a parameter called 'title'/,
"notify { id: message=>explicit} Notify[id]['type']" => /does not have a parameter called 'type'/,
+ "notify { id: message=>explicit } Notify[id]{message=>override}" => /'message' is already set on Notify\[id\]/
}.each do |source, result|
it "should parse '#{source}' and raise error matching #{result}" do
expect { parser.evaluate_string(scope, source, __FILE__)}.to raise_error(result)
@@ -713,7 +839,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
"!! true" => true,
"!! false" => false,
"! 'x'" => false,
- "! ''" => true,
+ "! ''" => false,
"! undef" => true,
"! [a]" => false,
"! []" => false,
@@ -744,12 +870,15 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
end
context "When evaluator performs calls" do
+
let(:populate) do
parser.evaluate_string(scope, "$a = 10 $b = [1,2,3]")
end
{
'sprintf( "x%iy", $a )' => "x10y",
+ # unfolds
+ 'sprintf( *["x%iy", $a] )' => "x10y",
'"x%iy".sprintf( $a )' => "x10y",
'$b.reduce |$memo,$x| { $memo + $x }' => 6,
'reduce($b) |$memo,$x| { $memo + $x }' => 6,
@@ -771,6 +900,69 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
it "provides location information on error in unparenthesized call logic" do
expect{parser.evaluate_string(scope, "include non_existing_class", __FILE__)}.to raise_error(Puppet::ParseError, /line 1\:1/)
end
+
+ it 'defaults can be given in a lambda and used only when arg is missing' do
+ env_loader = @compiler.loaders.public_environment_loader
+ fc = Puppet::Functions.create_function(:test) do
+ dispatch :test do
+ param 'Integer', 'count'
+ required_block_param
+ end
+ def test(count, block)
+ block.call({}, *[].fill(10, 0, count))
+ end
+ end
+ the_func = fc.new({}, env_loader)
+ env_loader.add_entry(:function, 'test', the_func, __FILE__)
+ expect(parser.evaluate_string(scope, "test(1) |$x, $y=20| { $x + $y}")).to eql(30)
+ expect(parser.evaluate_string(scope, "test(2) |$x, $y=20| { $x + $y}")).to eql(20)
+ end
+
+ it 'a given undef does not select the default value' do
+ env_loader = @compiler.loaders.public_environment_loader
+ fc = Puppet::Functions.create_function(:test) do
+ dispatch :test do
+ param 'Any', 'lambda_arg'
+ required_block_param
+ end
+ def test(lambda_arg, block)
+ block.call({}, lambda_arg)
+ end
+ end
+ the_func = fc.new({}, env_loader)
+ env_loader.add_entry(:function, 'test', the_func, __FILE__)
+
+ expect(parser.evaluate_string(scope, "test(undef) |$x=20| { $x == undef}")).to eql(true)
+ end
+
+ it 'a given undef is given as nil' do
+ env_loader = @compiler.loaders.public_environment_loader
+ fc = Puppet::Functions.create_function(:assert_no_undef) do
+ dispatch :assert_no_undef do
+ param 'Any', 'x'
+ end
+
+ def assert_no_undef(x)
+ case x
+ when Array
+ return unless x.include?(:undef)
+ when Hash
+ return unless x.keys.include?(:undef) || x.values.include?(:undef)
+ else
+ return unless x == :undef
+ end
+ raise "contains :undef"
+ end
+ end
+
+ the_func = fc.new({}, env_loader)
+ env_loader.add_entry(:function, 'assert_no_undef', the_func, __FILE__)
+
+ expect{parser.evaluate_string(scope, "assert_no_undef(undef)")}.to_not raise_error()
+ expect{parser.evaluate_string(scope, "assert_no_undef([undef])")}.to_not raise_error()
+ expect{parser.evaluate_string(scope, "assert_no_undef({undef => 1})")}.to_not raise_error()
+ expect{parser.evaluate_string(scope, "assert_no_undef({1 => undef})")}.to_not raise_error()
+ end
end
context "When evaluator performs string interpolation" do
@@ -971,7 +1163,7 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
expect { parser.evaluate_string(scope, src)}.to raise_error(/Cannot parse invalid JSON string/)
end
- it "parses interpolated heredoc epression" do
+ it "parses interpolated heredoc expression" do
src = <<-CODE
$name = 'Fjodor'
@("END")
@@ -983,12 +1175,6 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
end
context "Handles Deprecations and Discontinuations" do
- around(:each) do |example|
- Puppet.override({:loaders => Puppet::Pops::Loaders.new(Puppet::Node::Environment.create(:testing, []))}, 'test') do
- example.run
- end
- end
-
it 'of import statements' do
source = "\nimport foo"
# Error references position 5 at the opening '{'
@@ -1002,7 +1188,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
source = '1+1 { "title": }'
# Error references position 5 at the opening '{'
# Set file to nil to make it easier to match with line number (no file name in output)
- expect { parser.parse_string(source, nil) }.to raise_error(/Expression is not valid as a resource.*line 1:5/)
+ expect { parser.evaluate_string(scope, source) }.to raise_error(
+ /Illegal Resource Type expression, expected result to be a type name, or untitled Resource.*line 1:2/)
end
it 'for non r-value producing <| |>' do
@@ -1058,6 +1245,39 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
end
+ context 'does not leak variables' do
+ it 'local variables are gone when lambda ends' do
+ source = <<-SOURCE
+ [1,2,3].each |$x| { $y = $x}
+ $a = $y
+ SOURCE
+ expect do
+ parser.evaluate_string(scope, source)
+ end.to raise_error(/Unknown variable: 'y'/)
+ end
+
+ it 'lambda parameters are gone when lambda ends' do
+ source = <<-SOURCE
+ [1,2,3].each |$x| { $y = $x}
+ $a = $x
+ SOURCE
+ expect do
+ parser.evaluate_string(scope, source)
+ end.to raise_error(/Unknown variable: 'x'/)
+ end
+
+ it 'does not leak match variables' do
+ source = <<-SOURCE
+ if 'xyz' =~ /(x)(y)(z)/ { notice $2 }
+ case 'abc' {
+ /(a)(b)(c)/ : { $x = $2 }
+ }
+ "-$x-$2-"
+ SOURCE
+ expect(parser.evaluate_string(scope, source)).to eq('-b--')
+ end
+ end
+
matcher :have_relationship do |expected|
calc = Puppet::Pops::Types::TypeCalculator.new
diff --git a/spec/unit/pops/evaluator/logical_ops_spec.rb b/spec/unit/pops/evaluator/logical_ops_spec.rb
index e5cdd1f93..d6a179e03 100644
--- a/spec/unit/pops/evaluator/logical_ops_spec.rb
+++ b/spec/unit/pops/evaluator/logical_ops_spec.rb
@@ -63,8 +63,8 @@ describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
evaluate(literal('x').not()).should == false
end
- it "'' == false" do
- evaluate(literal('').not()).should == true
+ it "'' == true" do
+ evaluate(literal('').not()).should == false
end
it ":undef == false" do
diff --git a/spec/unit/pops/evaluator/variables_spec.rb b/spec/unit/pops/evaluator/variables_spec.rb
index fe93842c4..6c1e9f821 100644
--- a/spec/unit/pops/evaluator/variables_spec.rb
+++ b/spec/unit/pops/evaluator/variables_spec.rb
@@ -49,111 +49,6 @@ describe 'Puppet::Pops::Impl::EvaluatorImpl' do
expect { evaluate_l(block(var('a').set(10), var('a').set(20))) }.to raise_error(/Cannot reassign variable a/)
end
- context "-= operations" do
- # Also see collections_ops_spec.rb where delete via - is fully tested, here only the
- # the -= operation itself is tested (there are many combinations)
- #
- it 'deleting from non existing value produces :undef, nil -= ?' do
- top_scope_block = var('b').set([1,2,3])
- local_scope_block = block(var('a').minus_set([4]), fqn('a').var)
- evaluate_l(top_scope_block, local_scope_block).should == :undef
- end
-
- it 'deletes from a list' do
- top_scope_block = var('a').set([1,2,3])
- local_scope_block = block(var('a').minus_set([2]), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block).should == [1,3]
- end
-
- it 'deletes from a hash' do
- top_scope_block = var('a').set({'a'=>1,'b'=>2,'c'=>3})
- local_scope_block = block(var('a').minus_set('b'), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block).should == {'a'=>1,'c'=>3}
- end
- end
-
- context "+= operations" do
- # Also see collections_ops_spec.rb where concatenation via + is fully tested
- it "appending to non existing value, nil += []" do
- top_scope_block = var('b').set([1,2,3])
- local_scope_block = var('a').plus_set([4])
- evaluate_l(top_scope_block, local_scope_block).should == [4]
- end
-
- context "appending to list" do
- it "from list, [] += []" do
- top_scope_block = var('a').set([1,2,3])
- local_scope_block = block(var('a').plus_set([4]), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4]
- end
-
- it "from hash, [] += {a=>b}" do
- top_scope_block = var('a').set([1,2,3])
- local_scope_block = block(var('a').plus_set({'a' => 1, 'b'=>2}), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block).should satisfy {|result|
- # hash in 1.8.7 is not insertion order preserving, hence this hoop
- result == [1,2,3,['a',1],['b',2]] || result == [1,2,3,['b',2],['a',1]]
- }
- end
-
- it "from single value, [] += x" do
- top_scope_block = var('a').set([1,2,3])
- local_scope_block = block(var('a').plus_set(4), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,4]
- end
-
- it "from embedded list, [] += [[x]]" do
- top_scope_block = var('a').set([1,2,3])
- local_scope_block = block(var('a').plus_set([[4,5]]), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block).should == [1,2,3,[4,5]]
- end
- end
-
- context "appending to hash" do
- it "from hash, {a=>b} += {x=>y}" do
- top_scope_block = var('a').set({'a' => 1, 'b' => 2})
- local_scope_block = block(var('a').plus_set({'c' => 3}), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block) do |scope|
- # Assert no change to top scope hash
- scope['a'].should == {'a' =>1, 'b'=> 2}
- end.should == {'a' => 1, 'b' => 2, 'c' => 3}
- end
-
- it "from list, {a=>b} += ['x', y]" do
- top_scope_block = var('a').set({'a' => 1, 'b' => 2})
- local_scope_block = block(var('a').plus_set(['c', 3]), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block) do |scope|
- # Assert no change to top scope hash
- scope['a'].should == {'a' =>1, 'b'=> 2}
- end.should == {'a' => 1, 'b' => 2, 'c' => 3}
- end
-
- it "with overwrite from hash, {a=>b} += {a=>c}" do
- top_scope_block = var('a').set({'a' => 1, 'b' => 2})
- local_scope_block = block(var('a').plus_set({'b' => 4, 'c' => 3}),fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block) do |scope|
- # Assert no change to top scope hash
- scope['a'].should == {'a' =>1, 'b'=> 2}
- end.should == {'a' => 1, 'b' => 4, 'c' => 3}
- end
-
- it "with overwrite from list, {a=>b} += ['a', c]" do
- top_scope_block = var('a').set({'a' => 1, 'b' => 2})
- local_scope_block = block(var('a').plus_set(['b', 4, 'c', 3]), fqn('a').var())
- evaluate_l(top_scope_block, local_scope_block) do |scope|
- # Assert no change to topscope hash
- scope['a'].should == {'a' =>1, 'b'=> 2}
- end.should == {'a' => 1, 'b' => 4, 'c' => 3}
- end
-
- it "from odd length array - error" do
- top_scope_block = var('a').set({'a' => 1, 'b' => 2})
- local_scope_block = var('a').plus_set(['b', 4, 'c'])
- expect { evaluate_l(top_scope_block, local_scope_block) }.to raise_error(/Append assignment \+= failed with error: odd number of arguments for Hash/)
- end
- end
- end
-
context "access to numeric variables" do
it "without a match" do
evaluate_l(block(literal(2) + literal(2),
diff --git a/spec/unit/pops/issues_spec.rb b/spec/unit/pops/issues_spec.rb
index 93f093d61..82dce1b44 100644
--- a/spec/unit/pops/issues_spec.rb
+++ b/spec/unit/pops/issues_spec.rb
@@ -23,4 +23,174 @@ describe "Puppet::Pops::Issues" do
x = Puppet::Pops::Issues::NOT_TOP_LEVEL
x.format().should == "Classes, definitions, and nodes may only appear at toplevel or inside other classes"
end
+
+end
+
+describe "Puppet::Pops::IssueReporter" do
+
+ let(:acceptor) { Puppet::Pops::Validation::Acceptor.new }
+
+ def fake_positioned(number)
+ stub("positioned_#{number}", :line => number, :pos => number)
+ end
+
+ def diagnostic(severity, number)
+ Puppet::Pops::Validation::Diagnostic.new(
+ severity,
+ Puppet::Pops::Issues::Issue.new(number) { "#{severity}#{number}" },
+ "#{severity}file",
+ fake_positioned(number))
+ end
+
+ def warning(number)
+ diagnostic(:warning, number)
+ end
+
+ def deprecation(number)
+ diagnostic(:deprecation, number)
+ end
+
+ def error(number)
+ diagnostic(:error, number)
+ end
+
+ context "given warnings" do
+
+ before(:each) do
+ acceptor.accept( warning(1) )
+ acceptor.accept( deprecation(1) )
+ end
+
+ it "emits warnings if told to emit them" do
+ Puppet.expects(:warning).twice.with(regexp_matches(/warning1|deprecation1/))
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })
+ end
+
+ it "does not emit warnings if not told to emit them" do
+ Puppet.expects(:warning).never
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, {})
+ end
+
+ it "emits no warnings if :max_warnings is 0" do
+ acceptor.accept( warning(2) )
+ Puppet[:max_warnings] = 0
+ Puppet.expects(:warning).once.with(regexp_matches(/deprecation1/))
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })
+ end
+
+ it "emits no more than 1 warning if :max_warnings is 1" do
+ acceptor.accept( warning(2) )
+ acceptor.accept( warning(3) )
+ Puppet[:max_warnings] = 1
+ Puppet.expects(:warning).twice.with(regexp_matches(/warning1|deprecation1/))
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })
+ end
+
+ it "does not emit more deprecations warnings than the max deprecation warnings" do
+ acceptor.accept( deprecation(2) )
+ Puppet[:max_deprecations] = 0
+ Puppet.expects(:warning).once.with(regexp_matches(/warning1/))
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })
+ end
+
+ it "does not emit deprecation warnings, but does emit regular warnings if disable_warnings includes deprecations" do
+ Puppet[:disable_warnings] = 'deprecations'
+ Puppet.expects(:warning).once.with(regexp_matches(/warning1/))
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })
+ end
+ end
+
+ context "given errors" do
+ it "logs nothing, but raises the given :message if :emit_errors is repressing error logging" do
+ acceptor.accept( error(1) )
+ Puppet.expects(:err).never
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_errors => false, :message => 'special'})
+ end.to raise_error(Puppet::ParseError, 'special')
+ end
+
+ it "prefixes :message if a single error is raised" do
+ acceptor.accept( error(1) )
+ Puppet.expects(:err).never
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :message => 'special'})
+ end.to raise_error(Puppet::ParseError, /special error1/)
+ end
+
+ it "logs nothing and raises immediately if there is only one error" do
+ acceptor.accept( error(1) )
+ Puppet.expects(:err).never
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })
+ end.to raise_error(Puppet::ParseError, /error1/)
+ end
+
+ it "logs nothing and raises immediately if there are multiple errors but max_errors is 0" do
+ acceptor.accept( error(1) )
+ acceptor.accept( error(2) )
+ Puppet[:max_errors] = 0
+ Puppet.expects(:err).never
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })
+ end.to raise_error(Puppet::ParseError, /error1/)
+ end
+
+ it "logs the :message if there is more than one allowed error" do
+ acceptor.accept( error(1) )
+ acceptor.accept( error(2) )
+ Puppet.expects(:err).times(3).with(regexp_matches(/error1|error2|special/))
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :message => 'special'})
+ end.to raise_error(Puppet::ParseError, /Giving up/)
+ end
+
+ it "emits accumulated errors before raising a 'giving up' message if there are more errors than allowed" do
+ acceptor.accept( error(1) )
+ acceptor.accept( error(2) )
+ acceptor.accept( error(3) )
+ Puppet[:max_errors] = 2
+ Puppet.expects(:err).times(2).with(regexp_matches(/error1|error2/))
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })
+ end.to raise_error(Puppet::ParseError, /3 errors.*Giving up/)
+ end
+
+ it "emits accumulated errors before raising a 'giving up' message if there are multiple errors but fewer than limits" do
+ acceptor.accept( error(1) )
+ acceptor.accept( error(2) )
+ acceptor.accept( error(3) )
+ Puppet[:max_errors] = 4
+ Puppet.expects(:err).times(3).with(regexp_matches(/error[123]/))
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })
+ end.to raise_error(Puppet::ParseError, /3 errors.*Giving up/)
+ end
+
+ it "emits errors regardless of disable_warnings setting" do
+ acceptor.accept( error(1) )
+ acceptor.accept( error(2) )
+ Puppet[:disable_warnings] = 'deprecations'
+ Puppet.expects(:err).times(2).with(regexp_matches(/error1|error2/))
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { })
+ end.to raise_error(Puppet::ParseError, /Giving up/)
+ end
+ end
+
+ context "given both" do
+
+ it "logs warnings and errors" do
+ acceptor.accept( warning(1) )
+ acceptor.accept( error(1) )
+ acceptor.accept( error(2) )
+ acceptor.accept( error(3) )
+ acceptor.accept( deprecation(1) )
+ Puppet[:max_errors] = 2
+ Puppet.expects(:warning).twice.with(regexp_matches(/warning1|deprecation1/))
+ Puppet.expects(:err).times(2).with(regexp_matches(/error[123]/))
+ expect do
+ Puppet::Pops::IssueReporter.assert_and_report(acceptor, { :emit_warnings => true })
+ end.to raise_error(Puppet::ParseError, /3 errors.*2 warnings.*Giving up/)
+ end
+ end
end
diff --git a/spec/unit/pops/loaders/dependency_loader_spec.rb b/spec/unit/pops/loaders/dependency_loader_spec.rb
index dbea5b208..cbdefe897 100644
--- a/spec/unit/pops/loaders/dependency_loader_spec.rb
+++ b/spec/unit/pops/loaders/dependency_loader_spec.rb
@@ -36,6 +36,23 @@ describe 'dependency loader' do
expect(function.class.name).to eq('testmodule::foo')
expect(function.is_a?(Puppet::Functions::Function)).to eq(true)
end
+
+ it 'can load something in a qualified name space more than once' do
+ module_dir = dir_containing('testmodule', {
+ 'lib' => { 'puppet' => { 'functions' => { 'testmodule' => {
+ 'foo.rb' => 'Puppet::Functions.create_function("testmodule::foo") { def foo; end; }'
+ }}}}})
+ module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, loaders, 'testmodule', module_dir, 'test1')
+ dep_loader = Puppet::Pops::Loader::DependencyLoader.new(static_loader, 'test-dep', [module_loader])
+
+ function = dep_loader.load_typed(typed_name(:function, 'testmodule::foo')).value
+ expect(function.class.name).to eq('testmodule::foo')
+ expect(function.is_a?(Puppet::Functions::Function)).to eq(true)
+
+ function = dep_loader.load_typed(typed_name(:function, 'testmodule::foo')).value
+ expect(function.class.name).to eq('testmodule::foo')
+ expect(function.is_a?(Puppet::Functions::Function)).to eq(true)
+ end
end
def typed_name(type, name)
diff --git a/spec/unit/pops/loaders/loader_paths_spec.rb b/spec/unit/pops/loaders/loader_paths_spec.rb
index 2929fe7f8..2cd565a8c 100644
--- a/spec/unit/pops/loaders/loader_paths_spec.rb
+++ b/spec/unit/pops/loaders/loader_paths_spec.rb
@@ -5,7 +5,6 @@ require 'puppet/loaders'
describe 'loader paths' do
include PuppetSpec::Files
- before(:each) { Puppet[:biff] = true }
let(:static_loader) { Puppet::Pops::Loader::StaticLoader.new() }
let(:unused_loaders) { nil }
@@ -17,17 +16,13 @@ describe 'loader paths' do
'lib' => {
'puppet' => {
'functions' => {},
- 'parser' => {
- 'functions' => {},
- }
}}})
module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, unused_loaders, 'testmodule', module_dir, 'test1')
effective_paths = Puppet::Pops::Loader::LoaderPaths.relative_paths_for_type(:function, module_loader)
expect(effective_paths.collect(&:generic_path)).to eq([
- File.join(module_dir, 'lib', 'puppet', 'functions'), # 4x functions
- File.join(module_dir, 'lib', 'puppet','parser', 'functions') # 3x functions
+ File.join(module_dir, 'lib', 'puppet', 'functions')
])
end
@@ -39,8 +34,6 @@ describe 'loader paths' do
expect(effective_paths.size).to be_eql(1)
expect(effective_paths[0].generic_path).to be_eql(File.join(module_dir, 'lib', 'puppet', 'functions'))
- expect(module_loader.path_index.size).to be_eql(1)
- expect(module_loader.path_index.include?(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo.rb'))).to be(true)
end
it 'all function smart-paths produces entries if they exist' do
@@ -48,19 +41,15 @@ describe 'loader paths' do
'lib' => {
'puppet' => {
'functions' => {'foo4x.rb' => 'ignored in this test'},
- 'parser' => {
- 'functions' => {'foo3x.rb' => 'ignored in this test'},
- }
}}})
module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, unused_loaders, 'testmodule', module_dir, 'test1')
effective_paths = module_loader.smart_paths.effective_paths(:function)
- expect(effective_paths.size).to eq(2)
- expect(module_loader.path_index.size).to eq(2)
+ expect(effective_paths.size).to eq(1)
+ expect(module_loader.path_index.size).to eq(1)
path_index = module_loader.path_index
- expect(path_index.include?(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo4x.rb'))).to eq(true)
- expect(path_index.include?(File.join(module_dir, 'lib', 'puppet', 'parser', 'functions', 'foo3x.rb'))).to eq(true)
+ expect(path_index).to include(File.join(module_dir, 'lib', 'puppet', 'functions', 'foo4x.rb'))
end
end
end
diff --git a/spec/unit/pops/loaders/loaders_spec.rb b/spec/unit/pops/loaders/loaders_spec.rb
index daa9c716c..831236698 100644
--- a/spec/unit/pops/loaders/loaders_spec.rb
+++ b/spec/unit/pops/loaders/loaders_spec.rb
@@ -4,6 +4,25 @@ require 'puppet_spec/files'
require 'puppet/pops'
require 'puppet/loaders'
+describe 'loader helper classes' do
+ it 'NamedEntry holds values and is frozen' do
+ ne = Puppet::Pops::Loader::Loader::NamedEntry.new('name', 'value', 'origin')
+ expect(ne.frozen?).to be_true
+ expect(ne.typed_name).to eql('name')
+ expect(ne.origin).to eq('origin')
+ expect(ne.value).to eq('value')
+ end
+
+ it 'TypedName holds values and is frozen' do
+ tn = Puppet::Pops::Loader::Loader::TypedName.new(:function, '::foo::bar')
+ expect(tn.frozen?).to be_true
+ expect(tn.type).to eq(:function)
+ expect(tn.name_parts).to eq(['foo', 'bar'])
+ expect(tn.name).to eq('foo::bar')
+ expect(tn.qualified).to be_true
+ end
+end
+
describe 'loaders' do
include PuppetSpec::Files
@@ -29,17 +48,6 @@ describe 'loaders' do
expect(loaders.private_environment_loader().to_s).to eql("(DependencyLoader 'environment' [])")
end
- it 'can load 3x system functions' do
- Puppet[:biff] = true
- loaders = Puppet::Pops::Loaders.new(empty_test_env)
- puppet_loader = loaders.puppet_system_loader()
-
- function = puppet_loader.load_typed(typed_name(:function, 'sprintf')).value
-
- expect(function.class.name).to eq('sprintf')
- expect(function).to be_a(Puppet::Functions::Function)
- end
-
it 'can load a function using a qualified or unqualified name from a module with metadata' do
loaders = Puppet::Pops::Loaders.new(environment_for(module_with_metadata))
modulea_loader = loaders.public_loader_for_module('modulea')
@@ -91,6 +99,18 @@ describe 'loaders' do
expect(function.call({})).to eql("usee::callee() was told 'passed value' + I am user::caller()")
end
+ it 'can load a function more than once from modules' do
+ env = environment_for(dependent_modules_with_metadata)
+ loaders = Puppet::Pops::Loaders.new(env)
+
+ moduleb_loader = loaders.private_loader_for_module('user')
+ function = moduleb_loader.load_typed(typed_name(:function, 'user::caller')).value
+ expect(function.call({})).to eql("usee::callee() was told 'passed value' + I am user::caller()")
+
+ function = moduleb_loader.load_typed(typed_name(:function, 'user::caller')).value
+ expect(function.call({})).to eql("usee::callee() was told 'passed value' + I am user::caller()")
+ end
+
def environment_for(*module_paths)
Puppet::Node::Environment.create(:'*test*', module_paths, '')
end
diff --git a/spec/unit/pops/loaders/module_loaders_spec.rb b/spec/unit/pops/loaders/module_loaders_spec.rb
index 9d0c1a86d..058e765de 100644
--- a/spec/unit/pops/loaders/module_loaders_spec.rb
+++ b/spec/unit/pops/loaders/module_loaders_spec.rb
@@ -84,35 +84,6 @@ describe 'FileBased module loader' do
expect(function.is_a?(Puppet::Functions::Function)).to eq(true)
end
- context 'when delegating 3x to 4x' do
- before(:each) { Puppet[:biff] = true }
-
- it 'can load a 3x function API ruby function in global name space' do
- module_dir = dir_containing('testmodule', {
- 'lib' => {
- 'puppet' => {
- 'parser' => {
- 'functions' => {
- 'foo3x.rb' => <<-CODE
- Puppet::Parser::Functions::newfunction(
- :foo3x, :type => :rvalue,
- :arity => 1
- ) do |args|
- args[0]
- end
- CODE
- }
- }
- }
- }})
-
- module_loader = Puppet::Pops::Loader::ModuleLoaders::FileBased.new(static_loader, loaders, 'testmodule', module_dir, 'test1')
- function = module_loader.load_typed(typed_name(:function, 'foo3x')).value
- expect(function.class.name).to eq('foo3x')
- expect(function.is_a?(Puppet::Functions::Function)).to eq(true)
- end
- end
-
def typed_name(type, name)
Puppet::Pops::Loader::Loader::TypedName.new(type, name)
end
diff --git a/spec/unit/pops/loaders/static_loader_spec.rb b/spec/unit/pops/loaders/static_loader_spec.rb
index e1a73273e..3d85f4522 100644
--- a/spec/unit/pops/loaders/static_loader_spec.rb
+++ b/spec/unit/pops/loaders/static_loader_spec.rb
@@ -37,6 +37,12 @@ describe 'the static loader' do
it "uses the evaluator to format output" do
expect(loader.load(:function, level).call({}, ['yay', 'surprise']).to_s).to eql('[yay, surprise]')
end
+
+ it 'outputs name of source (scope) by passing it to the Log utility' do
+ the_scope = {}
+ Puppet::Util::Log.any_instance.expects(:source=).with(the_scope)
+ loader.load(:function, level).call(the_scope, 'x')
+ end
end
end
diff --git a/spec/unit/pops/parser/epp_parser_spec.rb b/spec/unit/pops/parser/epp_parser_spec.rb
index 0db4ba7d9..fb32b9ba4 100644
--- a/spec/unit/pops/parser/epp_parser_spec.rb
+++ b/spec/unit/pops/parser/epp_parser_spec.rb
@@ -51,36 +51,65 @@ describe "epp parser" do
context "handles parsing of" do
it "text (and nothing else)" do
- dump(parse("Hello World")).should == "(lambda (epp (block (render-s 'Hello World'))))"
+ dump(parse("Hello World")).should == [
+ "(lambda (epp (block",
+ " (render-s 'Hello World')",
+ ")))"].join("\n")
end
it "template parameters" do
- dump(parse("<%|$x|%>Hello World")).should == "(lambda (parameters x) (epp (block (render-s 'Hello World'))))"
+ dump(parse("<%|$x|%>Hello World")).should == [
+ "(lambda (parameters x) (epp (block",
+ " (render-s 'Hello World')",
+ ")))"].join("\n")
end
it "template parameters with default" do
- dump(parse("<%|$x='cigar'|%>Hello World")).should == "(lambda (parameters (= x 'cigar')) (epp (block (render-s 'Hello World'))))"
+ dump(parse("<%|$x='cigar'|%>Hello World")).should == [
+ "(lambda (parameters (= x 'cigar')) (epp (block",
+ " (render-s 'Hello World')",
+ ")))"].join("\n")
end
it "template parameters with and without default" do
- dump(parse("<%|$x='cigar', $y|%>Hello World")).should == "(lambda (parameters (= x 'cigar') y) (epp (block (render-s 'Hello World'))))"
+ dump(parse("<%|$x='cigar', $y|%>Hello World")).should == [
+ "(lambda (parameters (= x 'cigar') y) (epp (block",
+ " (render-s 'Hello World')",
+ ")))"].join("\n")
end
it "template parameters + additional setup" do
- dump(parse("<%|$x| $y = 10 %>Hello World")).should == "(lambda (parameters x) (epp (block (= $y 10) (render-s 'Hello World'))))"
+ dump(parse("<%|$x| $y = 10 %>Hello World")).should == [
+ "(lambda (parameters x) (epp (block",
+ " (= $y 10)",
+ " (render-s 'Hello World')",
+ ")))"].join("\n")
end
it "comments" do
- dump(parse("<%#($x='cigar', $y)%>Hello World")).should == "(lambda (epp (block (render-s 'Hello World'))))"
+ dump(parse("<%#($x='cigar', $y)%>Hello World")).should == [
+ "(lambda (epp (block",
+ " (render-s 'Hello World')",
+ ")))"
+ ].join("\n")
end
it "verbatim epp tags" do
- dump(parse("<%% contemplating %%>Hello World")).should == "(lambda (epp (block (render-s '<% contemplating %>Hello World'))))"
+ dump(parse("<%% contemplating %%>Hello World")).should == [
+ "(lambda (epp (block",
+ " (render-s '<% contemplating %>Hello World')",
+ ")))"
+ ].join("\n")
end
it "expressions" do
- dump(parse("We all live in <%= 3.14 - 2.14 %> world")).should ==
- "(lambda (epp (block (render-s 'We all live in ') (render (- 3.14 2.14)) (render-s ' world'))))"
+ dump(parse("We all live in <%= 3.14 - 2.14 %> world")).should == [
+ "(lambda (epp (block",
+ " (render-s 'We all live in ')",
+ " (render (- 3.14 2.14))",
+ " (render-s ' world')",
+ ")))"
+ ].join("\n")
end
end
end
diff --git a/spec/unit/pops/parser/evaluating_parser_spec.rb b/spec/unit/pops/parser/evaluating_parser_spec.rb
index 8448a5af3..7645b9bc2 100644
--- a/spec/unit/pops/parser/evaluating_parser_spec.rb
+++ b/spec/unit/pops/parser/evaluating_parser_spec.rb
@@ -9,7 +9,6 @@ describe 'The Evaluating Parser' do
include PuppetSpec::Scope
let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() }
- let(:diag) { Puppet::Pops::Binder::Hiera2::DiagnosticProducer.new(acceptor) }
let(:scope) { s = create_test_scope_for_node(node); s }
let(:node) { 'node.example.com' }
diff --git a/spec/unit/pops/parser/lexer2_spec.rb b/spec/unit/pops/parser/lexer2_spec.rb
index 8ccdc2630..b9a0a916b 100644
--- a/spec/unit/pops/parser/lexer2_spec.rb
+++ b/spec/unit/pops/parser/lexer2_spec.rb
@@ -21,7 +21,7 @@ describe 'Lexer2' do
include EgrammarLexer2Spec
{
- :LBRACK => '[',
+ :LISTSTART => '[',
:RBRACK => ']',
:LBRACE => '{',
:RBRACE => '}',
@@ -69,6 +69,10 @@ describe 'Lexer2' do
end
end
+ it "should lex [ in position after non whitespace as LBRACK" do
+ tokens_scanned_from("a[").should match_tokens2(:NAME, :LBRACK)
+ end
+
{
"case" => :CASE,
"class" => :CLASS,
@@ -186,6 +190,16 @@ describe 'Lexer2' do
end
end
+ { "''" => [2, ""],
+ "'a'" => [3, "a"],
+ "'a\\'b'" => [6, "a'b"],
+ }.each do |source, expected|
+ it "should lex a single quoted STRING on the form #{source} as having length #{expected[0]}" do
+ length, value = expected
+ tokens_scanned_from(source).should match_tokens2([:STRING, value, {:line => 1, :pos=>1, :length=> length}])
+ end
+ end
+
{ '""' => '',
'"a"' => 'a',
'"a\'b"' => "a'b",
@@ -229,7 +243,7 @@ describe 'Lexer2' do
it "differentiates between foo[x] and foo [x] (whitespace)" do
tokens_scanned_from("$a[1]").should match_tokens2(:VARIABLE, :LBRACK, :NUMBER, :RBRACK)
- tokens_scanned_from("$a [1]").should match_tokens2(:VARIABLE, :LBRACK, :NUMBER, :RBRACK)
+ tokens_scanned_from("$a [1]").should match_tokens2(:VARIABLE, :LISTSTART, :NUMBER, :RBRACK)
tokens_scanned_from("a[1]").should match_tokens2(:NAME, :LBRACK, :NUMBER, :RBRACK)
tokens_scanned_from("a [1]").should match_tokens2(:NAME, :LISTSTART, :NUMBER, :RBRACK)
tokens_scanned_from(" if \n\r\t\nif if ").should match_tokens2(:IF, :IF, :IF)
@@ -257,7 +271,9 @@ describe 'Lexer2' do
"!~" => [:NOMATCH, "!~ /./"],
"," => [:COMMA, ", /./"],
"(" => [:LPAREN, "( /./"],
- "[" => [:LBRACK, "[ /./"],
+ "[" => [:LISTSTART, "[ /./"],
+ "[" => [[:NAME, :LBRACK], "a[ /./"],
+ "[" => [[:NAME, :LISTSTART], "a [ /./"],
"{" => [:LBRACE, "{ /./"],
"+" => [:PLUS, "+ /./"],
"-" => [:MINUS, "- /./"],
@@ -265,7 +281,8 @@ describe 'Lexer2' do
";" => [:SEMIC, "; /./"],
}.each do |token, entry|
it "should lex regexp after '#{token}'" do
- tokens_scanned_from(entry[1]).should match_tokens2(entry[0], :REGEX)
+ expected = [entry[0], :REGEX].flatten
+ tokens_scanned_from(entry[1]).should match_tokens2(*expected)
end
end
diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb
deleted file mode 100755
index 40d9b3e51..000000000
--- a/spec/unit/pops/parser/lexer_spec.rb
+++ /dev/null
@@ -1,840 +0,0 @@
-#! /usr/bin/env ruby
-require 'spec_helper'
-
-require 'puppet/pops'
-
-# This is a special matcher to match easily lexer output
-RSpec::Matchers.define :be_like do |*expected|
- match do |actual|
- diffable
- 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
-
-module EgrammarLexerSpec
- def self.tokens_scanned_from(s)
- lexer = Puppet::Pops::Parser::Lexer.new
- lexer.string = s
- tokens = lexer.fullscan[0..-2]
- tokens.map do |t|
- key = t[0]
- options = t[1]
- if options[:locator]
- # unresolved locations needs to be resolved for tests that check positioning
- [key,
- options[:locator].to_location_hash(
- options[:offset],
- options[:end_offset]).merge({:value => options[:value]}) ]
- else
- t
- end
- end
- end
-end
-
-describe Puppet::Pops::Parser::Lexer do
- include EgrammarLexerSpec
-
- describe "when reading strings" do
- before { @lexer = Puppet::Pops::Parser::Lexer.new }
-
- it "should increment the line count for every carriage return in the string" do
- @lexer.string = "'this\nis\natest'"
- @lexer.fullscan[0..-2]
-
- line = @lexer.line
- line.should == 3
- end
-
- it "should not increment the line count for escapes in the string" do
- @lexer.string = "'this\\nis\\natest'"
- @lexer.fullscan[0..-2]
-
- @lexer.line.should == 1
- end
-
- it "should not think the terminator is escaped, when preceeded by an even number of backslashes" do
- @lexer.string = "'here\nis\nthe\nstring\\\\'with\nextra\njunk"
- @lexer.fullscan[0..-2]
-
- @lexer.line.should == 6
- end
-
- {
- 'r' => "\r",
- 'n' => "\n",
- 't' => "\t",
- 's' => " "
- }.each do |esc, expected_result|
- it "should recognize \\#{esc} sequence" do
- @lexer.string = "\\#{esc}'"
- @lexer.slurpstring("'")[0].should == expected_result
- end
- end
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::Token, "when initializing" do
- it "should create a regex if the first argument is a string" do
- Puppet::Pops::Parser::Lexer::Token.new("something", :NAME).regex.should == %r{something}
- end
-
- it "should set the string if the first argument is one" do
- Puppet::Pops::Parser::Lexer::Token.new("something", :NAME).string.should == "something"
- end
-
- it "should set the regex if the first argument is one" do
- Puppet::Pops::Parser::Lexer::Token.new(%r{something}, :NAME).regex.should == %r{something}
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::TokenList do
- before do
- @list = Puppet::Pops::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"
- expect { @list.add_token :name, "whatever" }.to 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::Pops::Parser::Lexer::TOKENS do
- before do
- @lexer = Puppet::Pops::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 => '+=',
- :DELETES => '-=',
- :PLUS => '+',
- :MINUS => '-',
- :DIV => '/',
- :TIMES => '*',
- :LSHIFT => '<<',
- :RSHIFT => '>>',
- :MATCH => '=~',
- :NOMATCH => '!~',
- :IN_EDGE => '->',
- :OUT_EDGE => '<-',
- :IN_EDGE_SUB => '~>',
- :OUT_EDGE_SUB => '<~',
- :PIPE => '|',
- }.each do |name, string|
- it "should have a token named #{name.to_s}" do
- Puppet::Pops::Parser::Lexer::TOKENS[name].should_not be_nil
- end
-
- it "should match '#{string}' for the token #{name.to_s}" do
- Puppet::Pops::Parser::Lexer::TOKENS[name].string.should == string
- end
- end
-
- {
- "case" => :CASE,
- "class" => :CLASS,
- "default" => :DEFAULT,
- "define" => :DEFINE,
-# "import" => :IMPORT, # done as a function in egrammar
- "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::Pops::Parser::Lexer::KEYWORDS[name].should_not be_nil
- end
-
- it "should have the keyword for #{name.to_s} set to #{string}" do
- Puppet::Pops::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,
- :LBRACE, :RBRACE,
- :RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name|
- it "should have a token named #{name.to_s}" do
- Puppet::Pops::Parser::Lexer::TOKENS[name].should_not be_nil
- end
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:CLASSREF] do
- before { @token = Puppet::Pops::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::Pops::Parser::Lexer::TOKENS[:NAME] do
- before { @token = Puppet::Pops::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::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(nil)
- @token.convert(stub("lexer"), "myval").should == [Puppet::Pops::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::Pops::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::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword)
- @token.convert(stub('lexer'), "true").should == [Puppet::Pops::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::Pops::Parser::Lexer::KEYWORDS.expects(:lookup).returns(keyword)
- @token.convert(stub('lexer'), "false").should == [Puppet::Pops::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::Pops::Parser::Lexer::TOKENS[:NUMBER] do
- before do
- @token = Puppet::Pops::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
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:COMMENT] do
- before { @token = Puppet::Pops::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 "'s block should return the comment without any text" do
- # This is a silly test, the original tested that the comments was processed, but
- # all comments are skipped anyway, and never collected for documentation.
- #
- @token.convert(@lexer,"# this is a comment")[1].should == ""
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:MLCOMMENT] do
- before do
- @token = Puppet::Pops::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
-
-# # TODO: REWRITE THIS TEST TO NOT BE BASED ON INTERNALS
-# 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 "'s block should return the comment without the comment marks" do
- # This is a silly test, the original tested that the comments was processed, but
- # all comments are skipped anyway, and never collected for documentation.
- #
- @lexer.stubs(:line=).with(0)
-
- @token.convert(@lexer,"/* this is a comment */")[1].should == ""
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:RETURN] do
- before { @token = Puppet::Pops::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
-end
-
-shared_examples_for "handling `-` in standard variable names for egrammar" 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 "when `variable_with_dash` is disabled it should NOT accept #{var.inspect} as a valid variable name" do
- Puppet[:allow_variables_with_dashes] = false
- (subject.regex.match(var) || [])[0].should_not == var
- end
-
- it "when `variable_with_dash` is enabled it should NOT accept #{var.inspect} as a valid variable name" do
- Puppet[:allow_variables_with_dashes] = true
- (subject.regex.match(var) || [])[0].should_not == var
- end
- end
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR] do
- its(:skip_text) { should be_false }
-
- it_should_behave_like "handling `-` in standard variable names for egrammar", '$'
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:VARIABLE] do
- its(:skip_text) { should be_false }
-
- it_should_behave_like "handling `-` in standard variable names for egrammar", ''
-end
-
-describe "the horrible deprecation / compatibility variables with dashes" do
-
- context "deprecation warnings" do
- before :each do Puppet[:allow_variables_with_dashes] = true end
-
- it "does not warn about a variable without a dash" do
- Puppet.expects(:deprecation_warning).never
-
- EgrammarLexerSpec.tokens_scanned_from('$c').should == [
- [:VARIABLE, {:value=>"c", :line=>1, :pos=>1, :offset=>0, :length=>2}]
- ]
- end
-
- it "does not warn about referencing a class name that contains a dash" do
- Puppet.expects(:deprecation_warning).never
-
- EgrammarLexerSpec.tokens_scanned_from('foo-bar').should == [
- [:NAME, {:value=>"foo-bar", :line=>1, :pos=>1, :offset=>0, :length=>7}]
- ]
- end
- end
-end
-
-
-describe Puppet::Pops::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 "\r\n"'} => [[:STRING,'single quoted string with an escaped "\r\n"']],
- %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"]],
- # Keyword variables
- %q["$true"] => [[:DQPRE,""],[:VARIABLE, "true"],[:DQPOST,""]],
- %q["$false"] => [[:DQPRE,""],[:VARIABLE, "false"],[:DQPOST,""]],
- %q["$if"] => [[:DQPRE,""],[:VARIABLE, "if"],[:DQPOST,""]],
- %q["$case"] => [[:DQPRE,""],[:VARIABLE, "case"],[:DQPOST,""]],
- %q["$unless"] => [[:DQPRE,""],[:VARIABLE, "unless"],[:DQPOST,""]],
- %q["$undef"] => [[:DQPRE,""],[:VARIABLE, "undef"],[:DQPOST,""]],
- # Expressions
- %q["${true}"] => [[:DQPRE,""],[:BOOLEAN, true],[:DQPOST,""]],
- %q["${false}"] => [[:DQPRE,""],[:BOOLEAN, false],[:DQPOST,""]],
- %q["${undef}"] => [[:DQPRE,""],:UNDEF,[:DQPOST,""]],
- %q["${if true {false}}"] => [[:DQPRE,""],:IF,[:BOOLEAN, true], :LBRACE, [:BOOLEAN, false], :RBRACE, [:DQPOST,""]],
- %q["${unless true {false}}"] => [[:DQPRE,""],:UNLESS,[:BOOLEAN, true], :LBRACE, [:BOOLEAN, false], :RBRACE, [:DQPOST,""]],
- %q["${case true {true:{false}}}"] => [
- [:DQPRE,""],:CASE,[:BOOLEAN, true], :LBRACE, [:BOOLEAN, true], :COLON, :LBRACE, [:BOOLEAN, false],
- :RBRACE, :RBRACE, [:DQPOST,""]],
- %q[{ "${a}" => 1 }] => [ :LBRACE, [:DQPRE,""], [:VARIABLE,"a"], [:DQPOST,""], :FARROW, [:NAME,"1"], :RBRACE ],
- }.each { |src,expected_result|
- it "should handle #{src} correctly" do
- EgrammarLexerSpec.tokens_scanned_from(src).should be_like(*expected_result)
- end
- }
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:DOLLAR_VAR] do
- before { @token = Puppet::Pops::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::Pops::Parser::Lexer::TOKENS[:VARIABLE], "myval"]
- end
-end
-
-describe Puppet::Pops::Parser::Lexer::TOKENS[:REGEX] do
- before { @token = Puppet::Pops::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
- EgrammarLexerSpec.tokens_scanned_from("$x =~ /this \\/ foo/").should be_like(__,__,[:REGEX,%r{this / foo}])
- end
-
- it "should not lex chained division as a regex" do
- EgrammarLexerSpec.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
- EgrammarLexerSpec.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")}
- }
- }
- EgrammarLexerSpec.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::Pops::Parser::Lexer::TOKENS[:REGEX], Regexp.new(/myregex/)]
- end
-end
-
-describe Puppet::Pops::Parser::Lexer, "when lexing comments" do
- before { @lexer = Puppet::Pops::Parser::Lexer.new }
-
- it "should skip whitespace before lexing the next token after a non-token" do
- EgrammarLexerSpec.tokens_scanned_from("/* 1\n\n */ \ntest").should be_like([:NAME, "test"])
- 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::Pops::Parser::Lexer in the old tests" do
- before { @lexer = Puppet::Pops::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|
- EgrammarLexerSpec.tokens_scanned_from(source).should be_like(*expected)
- }
- end
-
- it "should fail usefully" do
- expect { EgrammarLexerSpec.tokens_scanned_from('^') }.to raise_error(RuntimeError)
- end
-
- it "should fail if the string is not set" do
- expect { @lexer.fullscan }.to raise_error(Puppet::LexError)
- end
-
- it "should correctly identify keywords" do
- EgrammarLexerSpec.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| EgrammarLexerSpec.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| EgrammarLexerSpec.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| EgrammarLexerSpec.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| EgrammarLexerSpec.tokens_scanned_from(t).should be_like([:NAME,t]) }
- end
-
- it "should correctly parse empty strings" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = ""') }.to_not raise_error
- end
-
- it "should correctly parse virtual resources" do
- EgrammarLexerSpec.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|
- EgrammarLexerSpec.tokens_scanned_from(string).should be_like([:VARIABLE,string.sub(/^\$/,'')])
- end
- end
-
- it "should end variables at `-`" do
- EgrammarLexerSpec.tokens_scanned_from('$hyphenated-variable').
- should be_like([:VARIABLE, "hyphenated"], [:MINUS, '-'], [:NAME, 'variable'])
- end
-
- it "should not include whitespace in a variable" do
- EgrammarLexerSpec.tokens_scanned_from("$foo bar").should_not be_like([:VARIABLE, "foo bar"])
- end
- it "should not include excess colons in a variable" do
- EgrammarLexerSpec.tokens_scanned_from("$foo::::bar").should_not be_like([:VARIABLE, "foo::::bar"])
- end
-end
-
-describe "Puppet::Pops::Parser::Lexer in the old tests when lexing example files" do
- my_fixtures('*.pp') do |file|
- it "should correctly lex #{file}" do
- lexer = Puppet::Pops::Parser::Lexer.new
- lexer.file = file
- expect { lexer.fullscan }.to_not raise_error
- end
- end
-end
-
-describe "when trying to lex a non-existent file" do
- include PuppetSpec::Files
-
- it "should return an empty list of tokens" do
- lexer = Puppet::Pops::Parser::Lexer.new
- lexer.file = nofile = tmpfile('lexer')
- Puppet::FileSystem.exist?(nofile).should == false
-
- lexer.fullscan.should == [[false,false]]
- end
-end
-
-describe "when string quotes are not closed" do
- it "should report with message including an \" opening quote" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = "') }.to raise_error(/after '"'/)
- end
-
- it "should report with message including an \' opening quote" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = \'') }.to raise_error(/after "'"/)
- end
-
- it "should report <eof> if immediately followed by eof" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = "') }.to raise_error(/followed by '<eof>'/)
- end
-
- it "should report max 5 chars following quote" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = "123456') }.to raise_error(/followed by '12345...'/)
- end
-
- it "should escape control chars" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = "12\n3456') }.to raise_error(/followed by '12\\n3...'/)
- end
-
- it "should resport position of opening quote" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = "123456') }.to raise_error(/at line 1:8/)
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = "123456') }.to raise_error(/at line 1:9/)
- end
-end
-
-describe "when lexing number, bad input should not go unpunished" do
- it "should slap bad octal as such" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = 0778') }.to raise_error(/Not a valid octal/)
- end
-
- it "should slap bad hex as such" do
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = 0xFG') }.to raise_error(/Not a valid hex/)
- expect { EgrammarLexerSpec.tokens_scanned_from('$var = 0xfg') }.to raise_error(/Not a valid hex/)
- end
- # Note, bad decimals are probably impossible to enter, as they are not recognized as complete numbers, instead,
- # the error will be something else, depending on what follows some initial digit.
- #
-end
-
-describe "when lexing interpolation detailed positioning should be correct" do
- it "should correctly position a string without interpolation" do
- EgrammarLexerSpec.tokens_scanned_from('"not interpolated"').should be_like(
- [:STRING, {:value=>"not interpolated", :line=>1, :offset=>0, :pos=>1, :length=>18}])
- end
-
- it "should correctly position a string with false start in interpolation" do
- EgrammarLexerSpec.tokens_scanned_from('"not $$$ rpolated"').should be_like(
- [:STRING, {:value=>"not $$$ rpolated", :line=>1, :offset=>0, :pos=>1, :length=>18}])
- end
-
- it "should correctly position pre-mid-end interpolation " do
- EgrammarLexerSpec.tokens_scanned_from('"pre $x mid $y end"').should be_like(
- [:DQPRE, {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>6}],
- [:VARIABLE, {:value=>"x", :line=>1, :offset=>6, :pos=>7, :length=>1}],
- [:DQMID, {:value=>" mid ", :line=>1, :offset=>7, :pos=>8, :length=>6}],
- [:VARIABLE, {:value=>"y", :line=>1, :offset=>13, :pos=>14, :length=>1}],
- [:DQPOST, {:value=>" end", :line=>1, :offset=>14, :pos=>15, :length=>5}]
- )
- end
-
- it "should correctly position pre-mid-end interpolation using ${} " do
- EgrammarLexerSpec.tokens_scanned_from('"pre ${x} mid ${y} end"').should be_like(
- [:DQPRE, {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
- [:VARIABLE, {:value=>"x", :line=>1, :offset=>7, :pos=>8, :length=>1}],
- [:DQMID, {:value=>" mid ", :line=>1, :offset=>8, :pos=>9, :length=>8}],
- [:VARIABLE, {:value=>"y", :line=>1, :offset=>16, :pos=>17, :length=>1}],
- [:DQPOST, {:value=>" end", :line=>1, :offset=>17, :pos=>18, :length=>6}]
- )
- end
-
- it "should correctly position pre-end interpolation using ${} with f call" do
- EgrammarLexerSpec.tokens_scanned_from('"pre ${x()} end"').should be_like(
- [:DQPRE, {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
- [:NAME, {:value=>"x", :line=>1, :offset=>7, :pos=>8, :length=>1}],
- [:LPAREN, {:value=>"(", :line=>1, :offset=>8, :pos=>9, :length=>1}],
- [:RPAREN, {:value=>")", :line=>1, :offset=>9, :pos=>10, :length=>1}],
- [:DQPOST, {:value=>" end", :line=>1, :offset=>10, :pos=>11, :length=>6}]
- )
- end
-
- it "should correctly position pre-end interpolation using ${} with $x" do
- EgrammarLexerSpec.tokens_scanned_from('"pre ${$x} end"').should be_like(
- [:DQPRE, {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
- [:VARIABLE, {:value=>"x", :line=>1, :offset=>7, :pos=>8, :length=>2}],
- [:DQPOST, {:value=>" end", :line=>1, :offset=>9, :pos=>10, :length=>6}]
- )
- end
-
- it "should correctly position pre-end interpolation across lines" do
- EgrammarLexerSpec.tokens_scanned_from(%Q["pre ${\n$x} end"]).should be_like(
- [:DQPRE, {:value=>"pre ", :line=>1, :offset=>0, :pos=>1, :length=>7}],
- [:VARIABLE, {:value=>"x", :line=>2, :offset=>8, :pos=>1, :length=>2}],
- [:DQPOST, {:value=>" end", :line=>2, :offset=>10, :pos=>3, :length=>6}]
- )
- end
-
- it "should correctly position interpolation across lines when strings have embedded newlines" do
- EgrammarLexerSpec.tokens_scanned_from(%Q["pre \n\n${$x}\n mid$y"]).should be_like(
- [:DQPRE, {:value=>"pre \n\n", :line=>1, :offset=>0, :pos=>1, :length=>9}],
- [:VARIABLE, {:value=>"x", :line=>3, :offset=>9, :pos=>3, :length=>2}],
- [:DQMID, {:value=>"\n mid", :line=>3, :offset=>11, :pos=>5, :length=>7}],
- [:VARIABLE, {:value=>"y", :line=>4, :offset=>18, :pos=>6, :length=>1}]
- )
- end
-end
diff --git a/spec/unit/pops/parser/parse_basic_expressions_spec.rb b/spec/unit/pops/parser/parse_basic_expressions_spec.rb
index f5aeb2a29..2190e54bb 100644
--- a/spec/unit/pops/parser/parse_basic_expressions_spec.rb
+++ b/spec/unit/pops/parser/parse_basic_expressions_spec.rb
@@ -134,6 +134,11 @@ describe "egrammar parsing basic expressions" do
it "$a = 'a' !~ 'b.*'" do; dump(parse("$a = 'a' !~ 'b.*'")).should == "(= $a (!~ 'a' 'b.*'))" ; end
end
+ context "When parsing unfold" do
+ it "$a = *[1,2]" do; dump(parse("$a = *[1,2]")).should == "(= $a (unfold ([] 1 2)))" ; end
+ it "$a = *1" do; dump(parse("$a = *1")).should == "(= $a (unfold 1))" ; end
+ end
+
context "When parsing Lists" do
it "$a = []" do
dump(parse("$a = []")).should == "(= $a ([]))"
diff --git a/spec/unit/pops/parser/parse_calls_spec.rb b/spec/unit/pops/parser/parse_calls_spec.rb
index 115c160d6..ee80544f5 100644
--- a/spec/unit/pops/parser/parse_calls_spec.rb
+++ b/spec/unit/pops/parser/parse_calls_spec.rb
@@ -66,7 +66,7 @@ describe "egrammar parsing function calls" do
# For egrammar where a bare word can be a "statement"
it "$a = foo bar # illegal, must have parentheses" do
- dump(parse("$a = foo bar")).should == "(block (= $a foo) bar)"
+ dump(parse("$a = foo bar")).should == "(block\n (= $a foo)\n bar\n)"
end
context "in nested scopes" do
@@ -94,8 +94,11 @@ describe "egrammar parsing function calls" do
end
it "$a.foo |$x|{ }" do
- dump(parse("$a.foo |$x|{ $b = $x}")).should ==
- "(call-method (. $a foo) (lambda (parameters x) (block (= $b $x))))"
+ dump(parse("$a.foo |$x|{ $b = $x}")).should == [
+ "(call-method (. $a foo) (lambda (parameters x) (block",
+ " (= $b $x)",
+ ")))"
+ ].join("\n")
end
end
end
diff --git a/spec/unit/pops/parser/parse_conditionals_spec.rb b/spec/unit/pops/parser/parse_conditionals_spec.rb
index b8b8d9c8e..591b20e97 100644
--- a/spec/unit/pops/parser/parse_conditionals_spec.rb
+++ b/spec/unit/pops/parser/parse_conditionals_spec.rb
@@ -30,10 +30,14 @@ describe "egrammar parsing conditionals" do
end
it "if true { $a = 10 $b = 10 } else {$a = 20}" do
- dump(parse("if true { $a = 10 $b = 20} else {$a = 20}")).should ==
- ["(if true",
- " (then (block (= $a 10) (= $b 20)))",
- " (else (= $a 20)))"].join("\n")
+ dump(parse("if true { $a = 10 $b = 20} else {$a = 20}")).should == [
+ "(if true",
+ " (then (block",
+ " (= $a 10)",
+ " (= $b 20)",
+ " ))",
+ " (else (= $a 20)))"
+ ].join("\n")
end
it "allows a parenthesized conditional expression" do
@@ -142,7 +146,10 @@ describe "egrammar parsing conditionals" do
it "case $a { a : {$b = 10 $c = 20}}" do
dump(parse("case $a { a : {$b = 10 $c = 20}}")).should ==
["(case $a",
- " (when (a) (then (block (= $b 10) (= $c 20)))))"
+ " (when (a) (then (block",
+ " (= $b 10)",
+ " (= $c 20)",
+ " ))))"
].join("\n")
end
end
diff --git a/spec/unit/pops/parser/parse_containers_spec.rb b/spec/unit/pops/parser/parse_containers_spec.rb
index 57d6efee9..a05c5975d 100644
--- a/spec/unit/pops/parser/parse_containers_spec.rb
+++ b/spec/unit/pops/parser/parse_containers_spec.rb
@@ -10,7 +10,12 @@ describe "egrammar parsing containers" do
context "When parsing file scope" do
it "$a = 10 $b = 20" do
- dump(parse("$a = 10 $b = 20")).should == "(block (= $a 10) (= $b 20))"
+ dump(parse("$a = 10 $b = 20")).should == [
+ "(block",
+ " (= $a 10)",
+ " (= $b 20)",
+ ")"
+ ].join("\n")
end
it "$a = 10" do
@@ -24,7 +29,11 @@ describe "egrammar parsing containers" do
end
it "class foo { class bar {} }" do
- dump(parse("class foo { class bar {}}")).should == "(class foo (block (class foo::bar ())))"
+ dump(parse("class foo { class bar {}}")).should == [
+ "(class foo (block",
+ " (class foo::bar ())",
+ "))"
+ ].join("\n")
end
it "class foo::bar {}" do
@@ -52,7 +61,12 @@ describe "egrammar parsing containers" do
end
it "class foo {$a = 10 $b = 20}" do
- dump(parse("class foo {$a = 10 $b = 20}")).should == "(class foo (block (= $a 10) (= $b 20)))"
+ dump(parse("class foo {$a = 10 $b = 20}")).should == [
+ "(class foo (block",
+ " (= $a 10)",
+ " (= $b 20)",
+ "))"
+ ].join("\n")
end
context "it should handle '3x weirdness'" do
@@ -100,6 +114,16 @@ describe "egrammar parsing containers" do
}.to raise_error(/not a valid classname/)
end
end
+
+ context 'it should allow keywords as attribute names' do
+ ['and', 'case', 'class', 'default', 'define', 'else', 'elsif', 'if', 'in', 'inherits', 'node', 'or',
+ 'undef', 'unless', 'type', 'attr', 'function', 'private'].each do |keyword|
+ it "such as #{keyword}" do
+ expect {parse("class x ($#{keyword}){} class { x: #{keyword} => 1 }")}.to_not raise_error
+ end
+ end
+ end
+
end
context "When the parser parses define" do
@@ -108,12 +132,20 @@ describe "egrammar parsing containers" do
end
it "class foo { define bar {}}" do
- dump(parse("class foo {define bar {}}")).should == "(class foo (block (define foo::bar ())))"
+ dump(parse("class foo {define bar {}}")).should == [
+ "(class foo (block",
+ " (define foo::bar ())",
+ "))"
+ ].join("\n")
end
it "define foo { define bar {}}" do
# This is illegal, but handled as part of validation
- dump(parse("define foo { define bar {}}")).should == "(define foo (block (define bar ())))"
+ dump(parse("define foo { define bar {}}")).should == [
+ "(define foo (block",
+ " (define bar ())",
+ "))"
+ ].join("\n")
end
it "define foo::bar {}" do
@@ -133,7 +165,12 @@ describe "egrammar parsing containers" do
end
it "define foo {$a = 10 $b = 20}" do
- dump(parse("define foo {$a = 10 $b = 20}")).should == "(define foo (block (= $a 10) (= $b 20)))"
+ dump(parse("define foo {$a = 10 $b = 20}")).should == [
+ "(define foo (block",
+ " (= $a 10)",
+ " (= $b 20)",
+ "))"
+ ].join("\n")
end
context "it should handle '3x weirdness'" do
@@ -152,6 +189,15 @@ describe "egrammar parsing containers" do
expect { dump(parse("define default {}")).should == "(define default ())"}.to raise_error(Puppet::ParseError)
end
end
+
+ context 'it should allow keywords as attribute names' do
+ ['and', 'case', 'class', 'default', 'define', 'else', 'elsif', 'if', 'in', 'inherits', 'node', 'or',
+ 'undef', 'unless', 'type', 'attr', 'function', 'private'].each do |keyword|
+ it "such as #{keyword}" do
+ expect {parse("define x ($#{keyword}){} x { y: #{keyword} => 1 }")}.to_not raise_error
+ end
+ end
+ end
end
context "When parsing node" do
@@ -159,6 +205,10 @@ describe "egrammar parsing containers" do
dump(parse("node foo {}")).should == "(node (matches 'foo') ())"
end
+ it "node foo, {} # trailing comma" do
+ dump(parse("node foo, {}")).should == "(node (matches 'foo') ())"
+ end
+
it "node kermit.example.com {}" do
dump(parse("node kermit.example.com {}")).should == "(node (matches 'kermit.example.com') ())"
end
@@ -200,7 +250,12 @@ describe "egrammar parsing containers" do
end
it "node foo inherits bar {$a = 10 $b = 20}" do
- dump(parse("node foo inherits bar {$a = 10 $b = 20}")).should == "(node (matches 'foo') (parent 'bar') (block (= $a 10) (= $b 20)))"
+ dump(parse("node foo inherits bar {$a = 10 $b = 20}")).should == [
+ "(node (matches 'foo') (parent 'bar') (block",
+ " (= $a 10)",
+ " (= $b 20)",
+ "))"
+ ].join("\n")
end
end
end
diff --git a/spec/unit/pops/parser/parse_resource_spec.rb b/spec/unit/pops/parser/parse_resource_spec.rb
index ee7e13445..cf7e7cb01 100644
--- a/spec/unit/pops/parser/parse_resource_spec.rb
+++ b/spec/unit/pops/parser/parse_resource_spec.rb
@@ -9,72 +9,98 @@ describe "egrammar parsing resource declarations" do
include ParserRspecHelper
context "When parsing regular resource" do
- it "file { 'title': }" do
- dump(parse("file { 'title': }")).should == [
- "(resource file",
- " ('title'))"
- ].join("\n")
- end
+ ["File", "file"].each do |word|
+ it "#{word} { 'title': }" do
+ dump(parse("#{word} { 'title': }")).should == [
+ "(resource file",
+ " ('title'))"
+ ].join("\n")
+ end
- it "file { 'title': path => '/somewhere', mode => 0777}" do
- dump(parse("file { 'title': path => '/somewhere', mode => 0777}")).should == [
- "(resource file",
- " ('title'",
- " (path => '/somewhere')",
- " (mode => 0777)))"
- ].join("\n")
- end
+ it "#{word} { 'title': path => '/somewhere', mode => '0777'}" do
+ dump(parse("#{word} { 'title': path => '/somewhere', mode => '0777'}")).should == [
+ "(resource file",
+ " ('title'",
+ " (path => '/somewhere')",
+ " (mode => '0777')))"
+ ].join("\n")
+ end
- it "file { 'title': path => '/somewhere', }" do
- dump(parse("file { 'title': path => '/somewhere', }")).should == [
- "(resource file",
- " ('title'",
- " (path => '/somewhere')))"
- ].join("\n")
- end
+ it "#{word} { 'title': path => '/somewhere', }" do
+ dump(parse("#{word} { 'title': path => '/somewhere', }")).should == [
+ "(resource file",
+ " ('title'",
+ " (path => '/somewhere')))"
+ ].join("\n")
+ end
- it "file { 'title': , }" do
- dump(parse("file { 'title': , }")).should == [
- "(resource file",
- " ('title'))"
- ].join("\n")
- end
+ it "#{word} { 'title': , }" do
+ dump(parse("#{word} { 'title': , }")).should == [
+ "(resource file",
+ " ('title'))"
+ ].join("\n")
+ end
- it "file { 'title': ; }" do
- dump(parse("file { 'title': ; }")).should == [
- "(resource file",
- " ('title'))"
- ].join("\n")
- end
+ it "#{word} { 'title': ; }" do
+ dump(parse("#{word} { 'title': ; }")).should == [
+ "(resource file",
+ " ('title'))"
+ ].join("\n")
+ end
- it "file { 'title': ; 'other_title': }" do
- dump(parse("file { 'title': ; 'other_title': }")).should == [
- "(resource file",
- " ('title')",
- " ('other_title'))"
- ].join("\n")
- end
+ it "#{word} { 'title': ; 'other_title': }" do
+ dump(parse("#{word} { 'title': ; 'other_title': }")).should == [
+ "(resource file",
+ " ('title')",
+ " ('other_title'))"
+ ].join("\n")
+ end
- it "file { 'title1': path => 'x'; 'title2': path => 'y'}" do
- dump(parse("file { 'title1': path => 'x'; 'title2': path => 'y'}")).should == [
- "(resource file",
- " ('title1'",
- " (path => 'x'))",
- " ('title2'",
- " (path => 'y')))",
- ].join("\n")
+ # PUP-2898, trailing ';'
+ it "#{word} { 'title': ; 'other_title': ; }" do
+ dump(parse("#{word} { 'title': ; 'other_title': ; }")).should == [
+ "(resource file",
+ " ('title')",
+ " ('other_title'))"
+ ].join("\n")
+ end
+
+ it "#{word} { 'title1': path => 'x'; 'title2': path => 'y'}" do
+ dump(parse("#{word} { 'title1': path => 'x'; 'title2': path => 'y'}")).should == [
+ "(resource file",
+ " ('title1'",
+ " (path => 'x'))",
+ " ('title2'",
+ " (path => 'y')))",
+ ].join("\n")
+ end
+
+ it "#{word} { title: * => {mode => '0777'} }" do
+ dump(parse("#{word} { title: * => {mode => '0777'}}")).should == [
+ "(resource file",
+ " (title",
+ " (* => ({} (mode '0777')))))"
+ ].join("\n")
+ end
end
end
- context "When parsing resource defaults" do
+ context "When parsing (type based) resource defaults" do
it "File { }" do
dump(parse("File { }")).should == "(resource-defaults file)"
end
- it "File { mode => 0777 }" do
- dump(parse("File { mode => 0777}")).should == [
+ it "File { mode => '0777' }" do
+ dump(parse("File { mode => '0777'}")).should == [
"(resource-defaults file",
- " (mode => 0777))"
+ " (mode => '0777'))"
+ ].join("\n")
+ end
+
+ it "File { * => {mode => '0777'} } (even if validated to be illegal)" do
+ dump(parse("File { * => {mode => '0777'}}")).should == [
+ "(resource-defaults file",
+ " (* => ({} (mode '0777'))))"
].join("\n")
end
end
@@ -85,36 +111,92 @@ describe "egrammar parsing resource declarations" do
end
it "File['x'] { x => 1 }" do
- dump(parse("File['x'] { x => 1}")).should == "(override (slice file 'x')\n (x => 1))"
+ dump(parse("File['x'] { x => 1}")).should == [
+ "(override (slice file 'x')",
+ " (x => 1))"
+ ].join("\n")
end
+
it "File['x', 'y'] { x => 1 }" do
- dump(parse("File['x', 'y'] { x => 1}")).should == "(override (slice file ('x' 'y'))\n (x => 1))"
+ dump(parse("File['x', 'y'] { x => 1}")).should == [
+ "(override (slice file ('x' 'y'))",
+ " (x => 1))"
+ ].join("\n")
end
it "File['x'] { x => 1, y => 2 }" do
- dump(parse("File['x'] { x => 1, y=> 2}")).should == "(override (slice file 'x')\n (x => 1)\n (y => 2))"
+ dump(parse("File['x'] { x => 1, y=> 2}")).should == [
+ "(override (slice file 'x')",
+ " (x => 1)",
+ " (y => 2))"
+ ].join("\n")
end
it "File['x'] { x +> 1 }" do
- dump(parse("File['x'] { x +> 1}")).should == "(override (slice file 'x')\n (x +> 1))"
+ dump(parse("File['x'] { x +> 1}")).should == [
+ "(override (slice file 'x')",
+ " (x +> 1))"
+ ].join("\n")
+ end
+
+ it "File['x'] { * => {mode => '0777'} } (even if validated to be illegal)" do
+ dump(parse("File['x'] { * => {mode => '0777'}}")).should == [
+ "(override (slice file 'x')",
+ " (* => ({} (mode '0777'))))"
+ ].join("\n")
end
end
context "When parsing virtual and exported resources" do
- it "@@file { 'title': }" do
+ it "parses exported @@file { 'title': }" do
dump(parse("@@file { 'title': }")).should == "(exported-resource file\n ('title'))"
end
- it "@file { 'title': }" do
+ it "parses virtual @file { 'title': }" do
dump(parse("@file { 'title': }")).should == "(virtual-resource file\n ('title'))"
end
- it "@file { mode => 0777 }" do
- # Defaults are not virtualizeable
- expect {
- dump(parse("@file { mode => 0777 }")).should == ""
- }.to raise_error(Puppet::ParseError, /Defaults are not virtualizable/)
+ it "nothing before the title colon is a syntax error" do
+ expect do
+ parse("@file {: mode => '0777' }")
+ end.to raise_error(/Syntax error/)
+ end
+
+ it "raises error for user error; not a resource" do
+ # The expression results in VIRTUAL, CALL FUNCTION('file', HASH) since the resource body has
+ # no title.
+ expect do
+ parse("@file { mode => '0777' }")
+ end.to raise_error(/Virtual \(@\) can only be applied to a Resource Expression/)
+ end
+
+ it "parses global defaults with @ (even if validated to be illegal)" do
+ dump(parse("@File { mode => '0777' }")).should == [
+ "(virtual-resource-defaults file",
+ " (mode => '0777'))"
+ ].join("\n")
+ end
+
+ it "parses global defaults with @@ (even if validated to be illegal)" do
+ dump(parse("@@File { mode => '0777' }")).should == [
+ "(exported-resource-defaults file",
+ " (mode => '0777'))"
+ ].join("\n")
+ end
+
+ it "parses override with @ (even if validated to be illegal)" do
+ dump(parse("@File[foo] { mode => '0777' }")).should == [
+ "(virtual-override (slice file foo)",
+ " (mode => '0777'))"
+ ].join("\n")
+ end
+
+ it "parses override combined with @@ (even if validated to be illegal)" do
+ dump(parse("@@File[foo] { mode => '0777' }")).should == [
+ "(exported-override (slice file foo)",
+ " (mode => '0777'))"
+ ].join("\n")
end
end
@@ -220,22 +302,22 @@ describe "egrammar parsing resource declarations" do
dump(parse("File <| tag == 'foo' |>")).should == "(collect file\n (<| |> (== tag 'foo')))"
end
- it "File <| tag == 'foo' and mode != 0777 |>" do
- dump(parse("File <| tag == 'foo' and mode != 0777 |>")).should == "(collect file\n (<| |> (&& (== tag 'foo') (!= mode 0777))))"
+ it "File <| tag == 'foo' and mode != '0777' |>" do
+ dump(parse("File <| tag == 'foo' and mode != '0777' |>")).should == "(collect file\n (<| |> (&& (== tag 'foo') (!= mode '0777'))))"
end
- it "File <| tag == 'foo' or mode != 0777 |>" do
- dump(parse("File <| tag == 'foo' or mode != 0777 |>")).should == "(collect file\n (<| |> (|| (== tag 'foo') (!= mode 0777))))"
+ it "File <| tag == 'foo' or mode != '0777' |>" do
+ dump(parse("File <| tag == 'foo' or mode != '0777' |>")).should == "(collect file\n (<| |> (|| (== tag 'foo') (!= mode '0777'))))"
end
- it "File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>" do
- dump(parse("File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>")).should ==
- "(collect file\n (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode 0777)))))"
+ it "File <| tag == 'foo' or tag == 'bar' and mode != '0777' |>" do
+ dump(parse("File <| tag == 'foo' or tag == 'bar' and mode != '0777' |>")).should ==
+ "(collect file\n (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode '0777')))))"
end
- it "File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>" do
- dump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>")).should ==
- "(collect file\n (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode 0777))))"
+ it "File <| (tag == 'foo' or tag == 'bar') and mode != '0777' |>" do
+ dump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != '0777' |>")).should ==
+ "(collect file\n (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode '0777'))))"
end
end
end
diff --git a/spec/unit/pops/parser/parser_spec.rb b/spec/unit/pops/parser/parser_spec.rb
index fb44a08c9..23f537eeb 100644
--- a/spec/unit/pops/parser/parser_spec.rb
+++ b/spec/unit/pops/parser/parser_spec.rb
@@ -14,4 +14,20 @@ describe Puppet::Pops::Parser::Parser do
model.body.class.should == Puppet::Pops::Model::AssignmentExpression
end
+ it "should accept empty input and return a model" do
+ parser = Puppet::Pops::Parser::Parser.new()
+ model = parser.parse_string("").current
+ model.class.should == Puppet::Pops::Model::Program
+ model.body.class.should == Puppet::Pops::Model::Nop
+ end
+
+ it "should accept empty input containing only comments and report location at end of input" do
+ parser = Puppet::Pops::Parser::Parser.new()
+ model = parser.parse_string("# comment\n").current
+ model.class.should == Puppet::Pops::Model::Program
+ model.body.class.should == Puppet::Pops::Model::Nop
+ adapter = Puppet::Pops::Adapters::SourcePosAdapter.adapt(model.body)
+ expect(adapter.offset).to eq(10)
+ expect(adapter.length).to eq(0)
+ end
end
diff --git a/spec/unit/pops/parser/parsing_typed_parameters_spec.rb b/spec/unit/pops/parser/parsing_typed_parameters_spec.rb
new file mode 100644
index 000000000..678f6523c
--- /dev/null
+++ b/spec/unit/pops/parser/parsing_typed_parameters_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+require 'puppet/pops'
+require 'puppet/pops/evaluator/evaluator_impl'
+require 'puppet_spec/pops'
+require 'puppet_spec/scope'
+require 'puppet/parser/e4_parser_adapter'
+
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+#require File.join(File.dirname(__FILE__), '/evaluator_rspec_helper')
+
+describe 'Puppet::Pops::Evaluator::EvaluatorImpl' do
+ include PuppetSpec::Pops
+ include PuppetSpec::Scope
+ before(:each) do
+
+ # These must be set since the is 3x logic that triggers on these even if the tests are explicit
+ # about selection of parser and evaluator
+ #
+ Puppet[:parser] = 'future'
+ end
+
+ let(:parser) { Puppet::Pops::Parser::EvaluatingParser.new }
+
+ context "captures-rest parameter" do
+ it 'is allowed in lambda when placed last' do
+ source = <<-CODE
+ foo() |$a, *$b| { $a + $b[0] }
+ CODE
+ expect do
+ parser.parse_string(source, __FILE__)
+ end.to_not raise_error()
+ end
+
+ it 'allows a type annotation' do
+ source = <<-CODE
+ foo() |$a, Integer *$b| { $a + $b[0] }
+ CODE
+ expect do
+ parser.parse_string(source, __FILE__)
+ end.to_not raise_error()
+ end
+
+ it 'is not allowed in lambda except last' do
+ source = <<-CODE
+ foo() |*$a, $b| { $a + $b[0] }
+ CODE
+ expect do
+ parser.parse_string(source, __FILE__)
+ end.to raise_error(Puppet::ParseError, /Parameter \$a is not last, and has 'captures rest'/)
+ end
+
+ it 'is not allowed in define' do
+ source = <<-CODE
+ define foo(*$a) { }
+ CODE
+ expect do
+ parser.parse_string(source, __FILE__)
+ end.to raise_error(Puppet::ParseError, /Parameter \$a has 'captures rest' - not supported in a 'define'/)
+ end
+
+ it 'is not allowed in class' do
+ source = <<-CODE
+ class foo(*$a) { }
+ CODE
+ expect do
+ parser.parse_string(source, __FILE__)
+ end.to raise_error(Puppet::ParseError, /Parameter \$a has 'captures rest' - not supported in a Host Class Definition/)
+ end
+ end
+end
diff --git a/spec/unit/pops/transformer/transform_calls_spec.rb b/spec/unit/pops/transformer/transform_calls_spec.rb
index f58c79e1e..dba0d560f 100644
--- a/spec/unit/pops/transformer/transform_calls_spec.rb
+++ b/spec/unit/pops/transformer/transform_calls_spec.rb
@@ -34,6 +34,7 @@ describe "transformation to Puppet AST for function calls" do
"realize bar" => '(invoke realize bar)',
"contain bar" => '(invoke contain bar)',
"include bar" => '(invoke include bar)',
+ "tag bar" => '(invoke tag bar)',
"info bar" => '(invoke info bar)',
"notice bar" => '(invoke notice bar)',
@@ -54,7 +55,6 @@ describe "transformation to Puppet AST for function calls" do
{
"foo bar" => '(block foo bar)',
- "tag bar" => '(block tag bar)',
"tag" => 'tag',
}.each do |source, result|
it "should not transform #{source}, and instead produce #{result}" do
diff --git a/spec/unit/pops/transformer/transform_resource_spec.rb b/spec/unit/pops/transformer/transform_resource_spec.rb
deleted file mode 100644
index 251ef2f75..000000000
--- a/spec/unit/pops/transformer/transform_resource_spec.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-#! /usr/bin/env ruby
-require 'spec_helper'
-require 'puppet/pops'
-
-# relative to this spec file (./) does not work as this file is loaded by rspec
-require File.join(File.dirname(__FILE__), '/transformer_rspec_helper')
-
-describe "transformation to Puppet AST for resource declarations" do
- include TransformerRspecHelper
-
- context "When transforming regular resource" do
- it "file { 'title': }" do
- astdump(parse("file { 'title': }")).should == [
- "(resource file",
- " ('title'))"
- ].join("\n")
- end
-
- it "file { 'title': ; 'other_title': }" do
- astdump(parse("file { 'title': ; 'other_title': }")).should == [
- "(resource file",
- " ('title')",
- " ('other_title'))"
- ].join("\n")
- end
-
- it "file { 'title': path => '/somewhere', mode => 0777}" do
- astdump(parse("file { 'title': path => '/somewhere', mode => 0777}")).should == [
- "(resource file",
- " ('title'",
- " (path => '/somewhere')",
- " (mode => 0777)))"
- ].join("\n")
- end
-
- it "file { 'title1': path => 'x'; 'title2': path => 'y'}" do
- astdump(parse("file { 'title1': path => 'x'; 'title2': path => 'y'}")).should == [
- "(resource file",
- " ('title1'",
- " (path => 'x'))",
- " ('title2'",
- " (path => 'y')))",
- ].join("\n")
- end
- end
-
- context "When transforming resource defaults" do
- it "File { }" do
- astdump(parse("File { }")).should == "(resource-defaults file)"
- end
-
- it "File { mode => 0777 }" do
- astdump(parse("File { mode => 0777}")).should == [
- "(resource-defaults file",
- " (mode => 0777))"
- ].join("\n")
- end
- end
-
- context "When transforming resource override" do
- it "File['x'] { }" do
- astdump(parse("File['x'] { }")).should == "(override (slice file 'x'))"
- end
-
- it "File['x'] { x => 1 }" do
- astdump(parse("File['x'] { x => 1}")).should == "(override (slice file 'x')\n (x => 1))"
- end
-
- it "File['x', 'y'] { x => 1 }" do
- astdump(parse("File['x', 'y'] { x => 1}")).should == "(override (slice file ('x' 'y'))\n (x => 1))"
- end
-
- it "File['x'] { x => 1, y => 2 }" do
- astdump(parse("File['x'] { x => 1, y=> 2}")).should == "(override (slice file 'x')\n (x => 1)\n (y => 2))"
- end
-
- it "File['x'] { x +> 1 }" do
- astdump(parse("File['x'] { x +> 1}")).should == "(override (slice file 'x')\n (x +> 1))"
- end
- end
-
- context "When transforming virtual and exported resources" do
- it "@@file { 'title': }" do
- astdump(parse("@@file { 'title': }")).should == "(exported-resource file\n ('title'))"
- end
-
- it "@file { 'title': }" do
- astdump(parse("@file { 'title': }")).should == "(virtual-resource file\n ('title'))"
- end
- end
-
- context "When transforming class resource" do
- it "class { 'cname': }" do
- astdump(parse("class { 'cname': }")).should == [
- "(resource class",
- " ('cname'))"
- ].join("\n")
- end
-
- it "class { 'cname': x => 1, y => 2}" do
- astdump(parse("class { 'cname': x => 1, y => 2}")).should == [
- "(resource class",
- " ('cname'",
- " (x => 1)",
- " (y => 2)))"
- ].join("\n")
- end
-
- it "class { 'cname1': x => 1; 'cname2': y => 2}" do
- astdump(parse("class { 'cname1': x => 1; 'cname2': y => 2}")).should == [
- "(resource class",
- " ('cname1'",
- " (x => 1))",
- " ('cname2'",
- " (y => 2)))",
- ].join("\n")
- end
- end
-
- context "When transforming Relationships" do
- it "File[a] -> File[b]" do
- astdump(parse("File[a] -> File[b]")).should == "(-> (slice file a) (slice file b))"
- end
-
- it "File[a] <- File[b]" do
- astdump(parse("File[a] <- File[b]")).should == "(<- (slice file a) (slice file b))"
- end
-
- it "File[a] ~> File[b]" do
- astdump(parse("File[a] ~> File[b]")).should == "(~> (slice file a) (slice file b))"
- end
-
- it "File[a] <~ File[b]" do
- astdump(parse("File[a] <~ File[b]")).should == "(<~ (slice file a) (slice file b))"
- end
-
- it "Should chain relationships" do
- astdump(parse("a -> b -> c")).should ==
- "(-> (-> a b) c)"
- end
-
- it "Should chain relationships" do
- astdump(parse("File[a] -> File[b] ~> File[c] <- File[d] <~ File[e]")).should ==
- "(<~ (<- (~> (-> (slice file a) (slice file b)) (slice file c)) (slice file d)) (slice file e))"
- end
- end
-
- context "When transforming collection" do
- context "of virtual resources" do
- it "File <| |>" do
- astdump(parse("File <| |>")).should == "(collect file\n (<| |>))"
- end
- end
-
- context "of exported resources" do
- it "File <<| |>>" do
- astdump(parse("File <<| |>>")).should == "(collect file\n (<<| |>>))"
- end
- end
-
- context "queries are parsed with correct precedence" do
- it "File <| tag == 'foo' |>" do
- astdump(parse("File <| tag == 'foo' |>")).should == "(collect file\n (<| |> (== tag 'foo')))"
- end
-
- it "File <| tag == 'foo' and mode != 0777 |>" do
- astdump(parse("File <| tag == 'foo' and mode != 0777 |>")).should == "(collect file\n (<| |> (&& (== tag 'foo') (!= mode 0777))))"
- end
-
- it "File <| tag == 'foo' or mode != 0777 |>" do
- astdump(parse("File <| tag == 'foo' or mode != 0777 |>")).should == "(collect file\n (<| |> (|| (== tag 'foo') (!= mode 0777))))"
- end
-
- it "File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>" do
- astdump(parse("File <| tag == 'foo' or tag == 'bar' and mode != 0777 |>")).should ==
- "(collect file\n (<| |> (|| (== tag 'foo') (&& (== tag 'bar') (!= mode 0777)))))"
- end
-
- it "File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>" do
- astdump(parse("File <| (tag == 'foo' or tag == 'bar') and mode != 0777 |>")).should ==
- "(collect file\n (<| |> (&& (|| (== tag 'foo') (== tag 'bar')) (!= mode 0777))))"
- end
- end
- end
-end
diff --git a/spec/unit/pops/types/type_calculator_spec.rb b/spec/unit/pops/types/type_calculator_spec.rb
index 3c4ea77ca..0bd475263 100644
--- a/spec/unit/pops/types/type_calculator_spec.rb
+++ b/spec/unit/pops/types/type_calculator_spec.rb
@@ -78,8 +78,13 @@ describe 'The type calculator' do
Puppet::Pops::Types::TypeFactory.struct(type_hash)
end
- def optional_object_t
- Puppet::Pops::Types::TypeFactory.optional_object()
+ def object_t
+ Puppet::Pops::Types::TypeFactory.any()
+ end
+
+ def unit_t
+ # Cannot be created via factory, the type is private to the type system
+ Puppet::Pops::Types::PUnitType.new
end
def types
@@ -88,8 +93,9 @@ describe 'The type calculator' do
shared_context "types_setup" do
+ # Do not include the special type Unit in this list
def all_types
- [ Puppet::Pops::Types::PObjectType,
+ [ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PNilType,
Puppet::Pops::Types::PDataType,
Puppet::Pops::Types::PScalarType,
@@ -102,7 +108,7 @@ describe 'The type calculator' do
Puppet::Pops::Types::PCollectionType,
Puppet::Pops::Types::PArrayType,
Puppet::Pops::Types::PHashType,
- Puppet::Pops::Types::PRubyType,
+ Puppet::Pops::Types::PRuntimeType,
Puppet::Pops::Types::PHostClassType,
Puppet::Pops::Types::PResourceType,
Puppet::Pops::Types::PPatternType,
@@ -111,6 +117,9 @@ describe 'The type calculator' do
Puppet::Pops::Types::PStructType,
Puppet::Pops::Types::PTupleType,
Puppet::Pops::Types::PCallableType,
+ Puppet::Pops::Types::PType,
+ Puppet::Pops::Types::POptionalType,
+ Puppet::Pops::Types::PDefaultType,
]
end
@@ -215,17 +224,18 @@ describe 'The type calculator' do
calculator.infer(nil).class.should == Puppet::Pops::Types::PNilType
end
- it ':undef translates to PNilType' do
- calculator.infer(:undef).class.should == Puppet::Pops::Types::PNilType
+ it ':undef translates to PRuntimeType' do
+ calculator.infer(:undef).class.should == Puppet::Pops::Types::PRuntimeType
end
- it 'an instance of class Foo translates to PRubyType[Foo]' do
+ it 'an instance of class Foo translates to PRuntimeType[ruby, Foo]' do
class Foo
end
t = calculator.infer(Foo.new)
- t.class.should == Puppet::Pops::Types::PRubyType
- t.ruby_class.should == 'Foo'
+ t.class.should == Puppet::Pops::Types::PRuntimeType
+ t.runtime.should == :ruby
+ t.runtime_type_name.should == 'Foo'
end
context 'array' do
@@ -278,8 +288,8 @@ describe 'The type calculator' do
calculator.infer(['one', /two/]).element_type.class.should == Puppet::Pops::Types::PScalarType
end
- it 'with string and symbol values translates to PArrayType[PObjectType]' do
- calculator.infer(['one', :two]).element_type.class.should == Puppet::Pops::Types::PObjectType
+ it 'with string and symbol values translates to PArrayType[PAnyType]' do
+ calculator.infer(['one', :two]).element_type.class.should == Puppet::Pops::Types::PAnyType
end
it 'with fixnum and nil values translates to PArrayType[PIntegerType]' do
@@ -328,17 +338,18 @@ describe 'The type calculator' do
calculator.infer({:first => 1, :second => 2}).class.should == Puppet::Pops::Types::PHashType
end
- it 'with symbolic keys translates to PHashType[PRubyType[Symbol],value]' do
+ it 'with symbolic keys translates to PHashType[PRuntimeType[ruby, Symbol], value]' do
k = calculator.infer({:first => 1, :second => 2}).key_type
- k.class.should == Puppet::Pops::Types::PRubyType
- k.ruby_class.should == 'Symbol'
+ k.class.should == Puppet::Pops::Types::PRuntimeType
+ k.runtime.should == :ruby
+ k.runtime_type_name.should == 'Symbol'
end
- it 'with string keys translates to PHashType[PStringType,value]' do
+ it 'with string keys translates to PHashType[PStringType, value]' do
calculator.infer({'first' => 1, 'second' => 2}).key_type.class.should == Puppet::Pops::Types::PStringType
end
- it 'with fixnum values translates to PHashType[key,PIntegerType]' do
+ it 'with fixnum values translates to PHashType[key, PIntegerType]' do
calculator.infer({:first => 1, :second => 2}).element_type.class.should == Puppet::Pops::Types::PIntegerType
end
end
@@ -457,14 +468,14 @@ describe 'The type calculator' do
expect(common_t.block_type).to be_nil
end
- it 'compatible instances => the least specific' do
+ it 'compatible instances => the most specific' do
t1 = callable_t(String)
scalar_t = Puppet::Pops::Types::PScalarType.new
t2 = callable_t(scalar_t)
common_t = calculator.common_type(t1, t2)
expect(common_t.class).to be(Puppet::Pops::Types::PCallableType)
expect(common_t.param_types.class).to be(Puppet::Pops::Types::PTupleType)
- expect(common_t.param_types.types).to eql([scalar_t])
+ expect(common_t.param_types.types).to eql([string_t])
expect(common_t.block_type).to be_nil
end
@@ -491,15 +502,33 @@ describe 'The type calculator' do
context 'computes assignability' do
include_context "types_setup"
- context "for Object, such that" do
- it 'all types are assignable to Object' do
- t = Puppet::Pops::Types::PObjectType.new()
+ context 'for Unit, such that' do
+ it 'all types are assignable to Unit' do
+ t = Puppet::Pops::Types::PUnitType.new()
+ all_types.each { |t2| t2.new.should be_assignable_to(t) }
+ end
+
+ it 'Unit is assignable to all other types' do
+ t = Puppet::Pops::Types::PUnitType.new()
+ all_types.each { |t2| t.should be_assignable_to(t2.new) }
+ end
+
+ it 'Unit is assignable to Unit' do
+ t = Puppet::Pops::Types::PUnitType.new()
+ t2 = Puppet::Pops::Types::PUnitType.new()
+ t.should be_assignable_to(t2)
+ end
+ end
+
+ context "for Any, such that" do
+ it 'all types are assignable to Any' do
+ t = Puppet::Pops::Types::PAnyType.new()
all_types.each { |t2| t2.new.should be_assignable_to(t) }
end
- it 'Object is not assignable to anything but Object' do
- tested_types = all_types() - [Puppet::Pops::Types::PObjectType]
- t = Puppet::Pops::Types::PObjectType.new()
+ it 'Any is not assignable to anything but Any' do
+ tested_types = all_types() - [Puppet::Pops::Types::PAnyType]
+ t = Puppet::Pops::Types::PAnyType.new()
tested_types.each { |t2| t.should_not be_assignable_to(t2.new) }
end
end
@@ -530,7 +559,7 @@ describe 'The type calculator' do
end
it 'Data is not assignable to any disjunct type' do
- tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - scalar_types
+ tested_types = all_types - [Puppet::Pops::Types::PAnyType, Puppet::Pops::Types::PDataType] - scalar_types
t = Puppet::Pops::Types::PDataType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
end
@@ -549,7 +578,7 @@ describe 'The type calculator' do
end
it 'Scalar is not assignable to any disjunct type' do
- tested_types = all_types - [Puppet::Pops::Types::PObjectType, Puppet::Pops::Types::PDataType] - scalar_types
+ tested_types = all_types - [Puppet::Pops::Types::PAnyType, Puppet::Pops::Types::PDataType] - scalar_types
t = Puppet::Pops::Types::PScalarType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
end
@@ -569,7 +598,7 @@ describe 'The type calculator' do
it 'Numeric is not assignable to any disjunct type' do
tested_types = all_types - [
- Puppet::Pops::Types::PObjectType,
+ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PDataType,
Puppet::Pops::Types::PScalarType,
] - numeric_types
@@ -591,7 +620,7 @@ describe 'The type calculator' do
end
it 'Collection is not assignable to any disjunct type' do
- tested_types = all_types - [Puppet::Pops::Types::PObjectType] - collection_types
+ tested_types = all_types - [Puppet::Pops::Types::PAnyType] - collection_types
t = Puppet::Pops::Types::PCollectionType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
end
@@ -609,7 +638,7 @@ describe 'The type calculator' do
it 'Array is not assignable to any disjunct type' do
tested_types = all_types - [
- Puppet::Pops::Types::PObjectType,
+ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PDataType] - collection_types
t = Puppet::Pops::Types::PArrayType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
@@ -628,7 +657,7 @@ describe 'The type calculator' do
it 'Hash is not assignable to any disjunct type' do
tested_types = all_types - [
- Puppet::Pops::Types::PObjectType,
+ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PDataType] - collection_types
t = Puppet::Pops::Types::PHashType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
@@ -647,7 +676,7 @@ describe 'The type calculator' do
it 'Tuple is not assignable to any disjunct type' do
tested_types = all_types - [
- Puppet::Pops::Types::PObjectType,
+ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PDataType] - collection_types
t = Puppet::Pops::Types::PTupleType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
@@ -666,7 +695,7 @@ describe 'The type calculator' do
it 'Struct is not assignable to any disjunct type' do
tested_types = all_types - [
- Puppet::Pops::Types::PObjectType,
+ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PDataType] - collection_types
t = Puppet::Pops::Types::PStructType.new()
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
@@ -678,7 +707,7 @@ describe 'The type calculator' do
t = Puppet::Pops::Types::PCallableType.new()
tested_types = all_types - [
Puppet::Pops::Types::PCallableType,
- Puppet::Pops::Types::PObjectType]
+ Puppet::Pops::Types::PAnyType]
tested_types.each {|t2| t.should_not be_assignable_to(t2.new) }
end
end
@@ -793,9 +822,50 @@ describe 'The type calculator' do
calculator.assignable?(pattern, enum).should == true
end
+ it 'pattern should accept a variant where all variants are acceptable' do
+ pattern = pattern_t(/^\w+$/)
+ calculator.assignable?(pattern, variant_t(string_t('a'), string_t('b'))).should == true
+ end
+
+ end
+
+ context 'when dealing with enums' do
+ it 'should accept a string with matching content' do
+ calculator.assignable?(enum_t('a', 'b'), string_t('a')).should == true
+ calculator.assignable?(enum_t('a', 'b'), string_t('b')).should == true
+ calculator.assignable?(enum_t('a', 'b'), string_t('c')).should == false
+ end
+
+ it 'should accept an enum with matching enum' do
+ calculator.assignable?(enum_t('a', 'b'), enum_t('a', 'b')).should == true
+ calculator.assignable?(enum_t('a', 'b'), enum_t('a')).should == true
+ calculator.assignable?(enum_t('a', 'b'), enum_t('c')).should == false
+ end
+
+ it 'enum should accept a variant where all variants are acceptable' do
+ enum = enum_t('a', 'b')
+ calculator.assignable?(enum, variant_t(string_t('a'), string_t('b'))).should == true
+ end
end
context 'when dealing with tuples' do
+ it 'matches empty tuples' do
+ tuple1 = tuple_t()
+ tuple2 = tuple_t()
+
+ calculator.assignable?(tuple1, tuple2).should == true
+ calculator.assignable?(tuple2, tuple1).should == true
+ end
+
+ it 'accepts an empty tuple as assignable to a tuple with a min size of 0' do
+ tuple1 = tuple_t(Object)
+ factory.constrain_size(tuple1, 0, :default)
+ tuple2 = tuple_t()
+
+ calculator.assignable?(tuple1, tuple2).should == true
+ calculator.assignable?(tuple2, tuple1).should == false
+ end
+
it 'should accept matching tuples' do
tuple1 = tuple_t(1,2)
tuple2 = tuple_t(Integer,Integer)
@@ -859,6 +929,17 @@ describe 'The type calculator' do
calculator.assignable?(tuple1, array).should == true
calculator.assignable?(array, tuple1).should == true
end
+
+ it 'should accept empty array when tuple allows min of 0' do
+ tuple1 = tuple_t(Integer)
+ factory.constrain_size(tuple1, 0, 1)
+
+ array = array_t(Integer)
+ factory.constrain_size(array, 0, 0)
+
+ calculator.assignable?(tuple1, array).should == true
+ calculator.assignable?(array, tuple1).should == false
+ end
end
context 'when dealing with structs' do
@@ -960,21 +1041,48 @@ describe 'The type calculator' do
context 'when testing if x is instance of type t' do
include_context "types_setup"
- it 'should consider undef to be instance of Object and NilType' do
+ it 'should consider undef to be instance of Any, NilType, and optional' do
calculator.instance?(Puppet::Pops::Types::PNilType.new(), nil).should == true
- calculator.instance?(Puppet::Pops::Types::PObjectType.new(), nil).should == true
+ calculator.instance?(Puppet::Pops::Types::PAnyType.new(), nil).should == true
+ calculator.instance?(Puppet::Pops::Types::POptionalType.new(), nil).should == true
end
- it 'should not consider undef to be an instance of any other type than Object and NilType and Data' do
- types_to_test = all_types - [
- Puppet::Pops::Types::PObjectType,
+ it 'all types should be (ruby) instance of PAnyType' do
+ all_types.each do |t|
+ t.new.is_a?(Puppet::Pops::Types::PAnyType).should == true
+ end
+ end
+
+ it "should consider :undef to be instance of Runtime['ruby', 'Symbol]" do
+ calculator.instance?(Puppet::Pops::Types::PRuntimeType.new(:runtime => :ruby, :runtime_type_name => 'Symbol'), :undef).should == true
+ end
+
+ it 'should not consider undef to be an instance of any other type than Any, NilType and Data' do
+ types_to_test = all_types - [
+ Puppet::Pops::Types::PAnyType,
Puppet::Pops::Types::PNilType,
- Puppet::Pops::Types::PDataType]
+ Puppet::Pops::Types::PDataType,
+ Puppet::Pops::Types::POptionalType,
+ ]
types_to_test.each {|t| calculator.instance?(t.new, nil).should == false }
types_to_test.each {|t| calculator.instance?(t.new, :undef).should == false }
end
+ it 'should consider default to be instance of Default and Any' do
+ calculator.instance?(Puppet::Pops::Types::PDefaultType.new(), :default).should == true
+ calculator.instance?(Puppet::Pops::Types::PAnyType.new(), :default).should == true
+ end
+
+ it 'should not consider "default" to be an instance of anything but Default, and Any' do
+ types_to_test = all_types - [
+ Puppet::Pops::Types::PAnyType,
+ Puppet::Pops::Types::PDefaultType,
+ ]
+
+ types_to_test.each {|t| calculator.instance?(t.new, :default).should == false }
+ end
+
it 'should consider fixnum instanceof PIntegerType' do
calculator.instance?(Puppet::Pops::Types::PIntegerType.new(), 1).should == true
end
@@ -1075,7 +1183,7 @@ describe 'The type calculator' do
context 'and t is Data' do
it 'undef should be considered instance of Data' do
- calculator.instance?(data_t, :undef).should == true
+ calculator.instance?(data_t, nil).should == true
end
it 'other symbols should not be considered instance of Data' do
@@ -1092,21 +1200,18 @@ describe 'The type calculator' do
it 'a hash with nil/undef data should be considered instance of Data' do
calculator.instance?(data_t, {'a' => nil}).should == true
- calculator.instance?(data_t, {'a' => :undef}).should == true
end
- it 'a hash with nil/undef key should not considered instance of Data' do
+ it 'a hash with nil/default key should not considered instance of Data' do
calculator.instance?(data_t, {nil => 10}).should == false
- calculator.instance?(data_t, {:undef => 10}).should == false
+ calculator.instance?(data_t, {:default => 10}).should == false
end
- it 'an array with undef entries should be considered instance of Data' do
- calculator.instance?(data_t, [:undef]).should == true
+ it 'an array with nil entries should be considered instance of Data' do
calculator.instance?(data_t, [nil]).should == true
end
- it 'an array with undef / data entries should be considered instance of Data' do
- calculator.instance?(data_t, [1, :undef, 'a']).should == true
+ it 'an array with nil + data entries should be considered instance of Data' do
calculator.instance?(data_t, [1, nil, 'a']).should == true
end
end
@@ -1119,10 +1224,8 @@ describe 'The type calculator' do
the_block = factory.LAMBDA(params,factory.literal(42))
the_closure = Puppet::Pops::Evaluator::Closure.new(:fake_evaluator, the_block, :fake_scope)
expect(calculator.instance?(all_callables_t, the_closure)).to be_true
- # TODO: lambdas are currently unttypes, anything can be given if arg count is correct
- expect(calculator.instance?(callable_t(optional_object_t), the_closure)).to be_true
- # Arg count is wrong
- expect(calculator.instance?(callable_t(optional_object_t, optional_object_t), the_closure)).to be_false
+ expect(calculator.instance?(callable_t(object_t), the_closure)).to be_true
+ expect(calculator.instance?(callable_t(object_t, object_t), the_closure)).to be_false
end
it 'a Function instance should be considered a Callable' do
@@ -1192,8 +1295,8 @@ describe 'The type calculator' do
calculator.string(Puppet::Pops::Types::PType.new()).should == 'Type'
end
- it 'should yield \'Object\' for PObjectType' do
- calculator.string(Puppet::Pops::Types::PObjectType.new()).should == 'Object'
+ it 'should yield \'Object\' for PAnyType' do
+ calculator.string(Puppet::Pops::Types::PAnyType.new()).should == 'Any'
end
it 'should yield \'Scalar\' for PScalarType' do
@@ -1397,20 +1500,29 @@ describe 'The type calculator' do
expect(calculator.string(callable_t(String, Integer))).to eql("Callable[String, Integer]")
end
- it "should yield 'Callable[t,min.max]' for callable with size constraint (infinite max)" do
+ it "should yield 'Callable[t,min,max]' for callable with size constraint (infinite max)" do
expect(calculator.string(callable_t(String, 0))).to eql("Callable[String, 0, default]")
end
- it "should yield 'Callable[t,min.max]' for callable with size constraint (capped max)" do
+ it "should yield 'Callable[t,min,max]' for callable with size constraint (capped max)" do
expect(calculator.string(callable_t(String, 0, 3))).to eql("Callable[String, 0, 3]")
end
+ it "should yield 'Callable[min,max]' callable with size > 0" do
+ expect(calculator.string(callable_t(0, 0))).to eql("Callable[0, 0]")
+ expect(calculator.string(callable_t(0, 1))).to eql("Callable[0, 1]")
+ expect(calculator.string(callable_t(0, :default))).to eql("Callable[0, default]")
+ end
+
it "should yield 'Callable[Callable]' for callable with block" do
expect(calculator.string(callable_t(all_callables_t))).to eql("Callable[0, 0, Callable]")
expect(calculator.string(callable_t(string_t, all_callables_t))).to eql("Callable[String, Callable]")
expect(calculator.string(callable_t(string_t, 1,1, all_callables_t))).to eql("Callable[String, 1, 1, Callable]")
end
+ it "should yield Unit for a Unit type" do
+ expect(calculator.string(unit_t)).to eql('Unit')
+ end
end
context 'when processing meta type' do
@@ -1428,7 +1540,7 @@ describe 'The type calculator' do
calculator.infer(Puppet::Pops::Types::PCollectionType.new()).is_a?(ptype).should() == true
calculator.infer(Puppet::Pops::Types::PArrayType.new() ).is_a?(ptype).should() == true
calculator.infer(Puppet::Pops::Types::PHashType.new() ).is_a?(ptype).should() == true
- calculator.infer(Puppet::Pops::Types::PRubyType.new() ).is_a?(ptype).should() == true
+ calculator.infer(Puppet::Pops::Types::PRuntimeType.new() ).is_a?(ptype).should() == true
calculator.infer(Puppet::Pops::Types::PHostClassType.new() ).is_a?(ptype).should() == true
calculator.infer(Puppet::Pops::Types::PResourceType.new() ).is_a?(ptype).should() == true
calculator.infer(Puppet::Pops::Types::PEnumType.new() ).is_a?(ptype).should() == true
@@ -1453,7 +1565,7 @@ describe 'The type calculator' do
calculator.string(calculator.infer(Puppet::Pops::Types::PCollectionType.new())).should == "Type[Collection]"
calculator.string(calculator.infer(Puppet::Pops::Types::PArrayType.new() )).should == "Type[Array[?]]"
calculator.string(calculator.infer(Puppet::Pops::Types::PHashType.new() )).should == "Type[Hash[?, ?]]"
- calculator.string(calculator.infer(Puppet::Pops::Types::PRubyType.new() )).should == "Type[Ruby[?]]"
+ calculator.string(calculator.infer(Puppet::Pops::Types::PRuntimeType.new() )).should == "Type[Runtime[?, ?]]"
calculator.string(calculator.infer(Puppet::Pops::Types::PHostClassType.new() )).should == "Type[Class]"
calculator.string(calculator.infer(Puppet::Pops::Types::PResourceType.new() )).should == "Type[Resource]"
calculator.string(calculator.infer(Puppet::Pops::Types::PEnumType.new() )).should == "Type[Enum]"
@@ -1462,6 +1574,10 @@ describe 'The type calculator' do
calculator.string(calculator.infer(Puppet::Pops::Types::PTupleType.new() )).should == "Type[Tuple]"
calculator.string(calculator.infer(Puppet::Pops::Types::POptionalType.new() )).should == "Type[Optional]"
calculator.string(calculator.infer(Puppet::Pops::Types::PCallableType.new() )).should == "Type[Callable]"
+
+ calculator.infer(Puppet::Pops::Types::PResourceType.new(:type_name => 'foo::fee::fum')).to_s.should == "Type[Foo::Fee::Fum]"
+ calculator.string(calculator.infer(Puppet::Pops::Types::PResourceType.new(:type_name => 'foo::fee::fum'))).should == "Type[Foo::Fee::Fum]"
+ calculator.infer(Puppet::Pops::Types::PResourceType.new(:type_name => 'Foo::Fee::Fum')).to_s.should == "Type[Foo::Fee::Fum]"
end
it "computes the common type of PType's type parameter" do
@@ -1584,6 +1700,87 @@ describe 'The type calculator' do
end
end
+ context 'when determening callability' do
+ context 'and given is exact' do
+ it 'with callable' do
+ required = callable_t(string_t)
+ given = callable_t(string_t)
+ calculator.callable?(required, given).should == true
+ end
+
+ it 'with args tuple' do
+ required = callable_t(string_t)
+ given = tuple_t(string_t)
+ calculator.callable?(required, given).should == true
+ end
+
+ it 'with args tuple having a block' do
+ required = callable_t(string_t, callable_t(string_t))
+ given = tuple_t(string_t, callable_t(string_t))
+ calculator.callable?(required, given).should == true
+ end
+
+ it 'with args array' do
+ required = callable_t(string_t)
+ given = array_t(string_t)
+ factory.constrain_size(given, 1, 1)
+ calculator.callable?(required, given).should == true
+ end
+ end
+
+ context 'and given is more generic' do
+ it 'with callable' do
+ required = callable_t(string_t)
+ given = callable_t(object_t)
+ calculator.callable?(required, given).should == true
+ end
+
+ it 'with args tuple' do
+ required = callable_t(string_t)
+ given = tuple_t(object_t)
+ calculator.callable?(required, given).should == false
+ end
+
+ it 'with args tuple having a block' do
+ required = callable_t(string_t, callable_t(string_t))
+ given = tuple_t(string_t, callable_t(object_t))
+ calculator.callable?(required, given).should == true
+ end
+
+ it 'with args tuple having a block with captures rest' do
+ required = callable_t(string_t, callable_t(string_t))
+ given = tuple_t(string_t, callable_t(object_t, 0, :default))
+ calculator.callable?(required, given).should == true
+ end
+ end
+
+ context 'and given is more specific' do
+ it 'with callable' do
+ required = callable_t(object_t)
+ given = callable_t(string_t)
+ calculator.callable?(required, given).should == false
+ end
+
+ it 'with args tuple' do
+ required = callable_t(object_t)
+ given = tuple_t(string_t)
+ calculator.callable?(required, given).should == true
+ end
+
+ it 'with args tuple having a block' do
+ required = callable_t(string_t, callable_t(object_t))
+ given = tuple_t(string_t, callable_t(string_t))
+ calculator.callable?(required, given).should == false
+ end
+
+ it 'with args tuple having a block with captures rest' do
+ required = callable_t(string_t, callable_t(object_t))
+ given = tuple_t(string_t, callable_t(string_t, 0, :default))
+ calculator.callable?(required, given).should == false
+ end
+ end
+ end
+
matcher :be_assignable_to do |type|
calc = Puppet::Pops::Types::TypeCalculator.new
diff --git a/spec/unit/pops/types/type_factory_spec.rb b/spec/unit/pops/types/type_factory_spec.rb
index a5b949640..cca19a75c 100644
--- a/spec/unit/pops/types/type_factory_spec.rb
+++ b/spec/unit/pops/types/type_factory_spec.rb
@@ -67,6 +67,10 @@ describe 'The type factory' do
Puppet::Pops::Types::TypeFactory.undef().class().should == Puppet::Pops::Types::PNilType
end
+ it 'default() returns PDefaultType' do
+ Puppet::Pops::Types::TypeFactory.default().class().should == Puppet::Pops::Types::PDefaultType
+ end
+
it 'range(to, from) returns PIntegerType' do
t = Puppet::Pops::Types::TypeFactory.range(1,2)
t.class().should == Puppet::Pops::Types::PIntegerType
@@ -150,10 +154,11 @@ describe 'The type factory' do
ht.element_type.class.should == Puppet::Pops::Types::PDataType
end
- it 'ruby(1) returns PRubyType[\'Fixnum\']' do
+ it 'ruby(1) returns PRuntimeType[ruby, \'Fixnum\']' do
ht = Puppet::Pops::Types::TypeFactory.ruby(1)
- ht.class().should == Puppet::Pops::Types::PRubyType
- ht.ruby_class.should == 'Fixnum'
+ ht.class().should == Puppet::Pops::Types::PRuntimeType
+ ht.runtime.should == :ruby
+ ht.runtime_type_name.should == 'Fixnum'
end
it 'a size constrained collection can be created from array' do
diff --git a/spec/unit/pops/types/type_parser_spec.rb b/spec/unit/pops/types/type_parser_spec.rb
index 95595e55d..b67b7b6cd 100644
--- a/spec/unit/pops/types/type_parser_spec.rb
+++ b/spec/unit/pops/types/type_parser_spec.rb
@@ -5,7 +5,8 @@ describe Puppet::Pops::Types::TypeParser do
extend RSpec::Matchers::DSL
let(:parser) { Puppet::Pops::Types::TypeParser.new }
- let(:types) { Puppet::Pops::Types::TypeFactory }
+ let(:types) { Puppet::Pops::Types::TypeFactory }
+
it "rejects a puppet expression" do
expect { parser.parse("1 + 1") }.to raise_error(Puppet::ParseError, /The expression <1 \+ 1> is not a valid type specification/)
@@ -30,7 +31,7 @@ describe Puppet::Pops::Types::TypeParser do
end
[
- 'Object', 'Data', 'CatalogEntry', 'Boolean', 'Scalar', 'Undef', 'Numeric',
+ 'Any', 'Data', 'CatalogEntry', 'Boolean', 'Scalar', 'Undef', 'Numeric', 'Default'
].each do |name|
it "does not support parameterizing unparameterized type <#{name}>" do
expect { parser.parse("#{name}[Integer]") }.to raise_unparameterized_error_for(name)
@@ -38,7 +39,7 @@ describe Puppet::Pops::Types::TypeParser do
end
it "parses a simple, unparameterized type into the type object" do
- expect(the_type_parsed_from(types.object)).to be_the_type(types.object)
+ expect(the_type_parsed_from(types.any)).to be_the_type(types.any)
expect(the_type_parsed_from(types.integer)).to be_the_type(types.integer)
expect(the_type_parsed_from(types.float)).to be_the_type(types.float)
expect(the_type_parsed_from(types.string)).to be_the_type(types.string)
@@ -50,6 +51,7 @@ describe Puppet::Pops::Types::TypeParser do
expect(the_type_parsed_from(types.tuple)).to be_the_type(types.tuple)
expect(the_type_parsed_from(types.struct)).to be_the_type(types.struct)
expect(the_type_parsed_from(types.optional)).to be_the_type(types.optional)
+ expect(the_type_parsed_from(types.default)).to be_the_type(types.default)
end
it "interprets an unparameterized Array as an Array of Data" do
@@ -113,6 +115,18 @@ describe Puppet::Pops::Types::TypeParser do
expect(the_type_parsed_from(struct_t)).to be_the_type(struct_t)
end
+ describe "handles parsing of patterns and regexp" do
+ { 'Pattern[/([a-z]+)([1-9]+)/]' => [:pattern, [/([a-z]+)([1-9]+)/]],
+ 'Pattern["([a-z]+)([1-9]+)"]' => [:pattern, [/([a-z]+)([1-9]+)/]],
+ 'Regexp[/([a-z]+)([1-9]+)/]' => [:regexp, [/([a-z]+)([1-9]+)/]],
+ 'Pattern[/x9/, /([a-z]+)([1-9]+)/]' => [:pattern, [/x9/, /([a-z]+)([1-9]+)/]],
+ }.each do |source, type|
+ it "such that the source '#{source}' yields the type #{type.to_s}" do
+ expect(parser.parse(source)).to be_the_type(Puppet::Pops::Types::TypeFactory.send(type[0], *type[1]))
+ end
+ end
+ end
+
it "rejects an collection spec with the wrong number of parameters" do
expect { parser.parse("Array[Integer, 1,2,3]") }.to raise_the_parameter_error("Array", "1 to 3", 4)
expect { parser.parse("Hash[Integer, Integer, 1,2,3]") }.to raise_the_parameter_error("Hash", "1 to 4", 5)
@@ -159,7 +173,7 @@ describe Puppet::Pops::Types::TypeParser do
end
it 'parses a ruby type' do
- expect(parser.parse("Ruby['Integer']")).to be_the_type(types.ruby_type('Integer'))
+ expect(parser.parse("Runtime[ruby, 'Integer']")).to be_the_type(types.ruby_type('Integer'))
end
it 'parses a callable type' do
@@ -178,12 +192,19 @@ describe Puppet::Pops::Types::TypeParser do
expect(parser.parse("Callable[String, Callable[Boolean]]")).to be_the_type(types.callable(String, types.callable(true)))
end
- it 'parses a parameterized callable type with only min/max' do
+ it 'parses a parameterized callable type with 0 min/max' do
t = parser.parse("Callable[0,0]")
expect(t).to be_the_type(types.callable())
expect(t.param_types.types).to be_empty
end
+ it 'parses a parameterized callable type with >0 min/max' do
+ t = parser.parse("Callable[0,1]")
+ expect(t).to be_the_type(types.callable(0,1))
+ # Contains a Unit type to indicate "called with what you accept"
+ expect(t.param_types.types[0]).to be_the_type(Puppet::Pops::Types::PUnitType.new())
+ end
+
matcher :be_the_type do |type|
calc = Puppet::Pops::Types::TypeCalculator.new
diff --git a/spec/unit/pops/validator/validator_spec.rb b/spec/unit/pops/validator/validator_spec.rb
index 1d865de5f..31defdd6b 100644
--- a/spec/unit/pops/validator/validator_spec.rb
+++ b/spec/unit/pops/validator/validator_spec.rb
@@ -6,12 +6,12 @@ require 'puppet_spec/pops'
# relative to this spec file (./) does not work as this file is loaded by rspec
require File.join(File.dirname(__FILE__), '../parser/parser_rspec_helper')
-describe "validating 3x" do
+describe "validating 4x" do
include ParserRspecHelper
include PuppetSpec::Pops
let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() }
- let(:validator) { Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) }
+ let(:validator) { Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) }
def validate(model)
validator.validate(model)
@@ -25,44 +25,160 @@ describe "validating 3x" do
end
it 'should raise error for illegal variable names' do
- pending "validation was too strict, now too relaxed - validation missing"
- expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
- expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
+ expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
+ expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
+ expect(validate(fqn('aaa::_aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
end
- it 'should raise error for -= assignment' do
- expect(validate(fqn('aaa').minus_set(2))).to have_issue(Puppet::Pops::Issues::UNSUPPORTED_OPERATOR)
+ it 'should not raise error for variable name with underscore first in first name segment' do
+ expect(validate(fqn('_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
+ expect(validate(fqn('::_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
end
-end
+ context 'for non productive expressions' do
+ [ '1',
+ '3.14',
+ "'a'",
+ '"a"',
+ '"${$a=10}"', # interpolation with side effect
+ 'false',
+ 'true',
+ 'default',
+ 'undef',
+ '[1,2,3]',
+ '{a=>10}',
+ 'if 1 {2}',
+ 'if 1 {2} else {3}',
+ 'if 1 {2} elsif 3 {4}',
+ 'unless 1 {2}',
+ 'unless 1 {2} else {3}',
+ '1 ? 2 => 3',
+ '1 ? { 2 => 3}',
+ '-1',
+ '-foo()', # unary minus on productive
+ '1+2',
+ '1<2',
+ '(1<2)',
+ '!true',
+ '!foo()', # not on productive
+ '$a',
+ '$a[1]',
+ 'name',
+ 'Type',
+ 'Type[foo]'
+ ].each do |expr|
+ it "produces error for non productive: #{expr}" do
+ source = "#{expr}; $a = 10"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::IDEM_EXPRESSION_NOT_LAST)
+ end
-describe "validating 4x" do
- include ParserRspecHelper
- include PuppetSpec::Pops
+ it "does not produce error when last for non productive: #{expr}" do
+ source = " $a = 10; #{expr}"
+ expect(validate(parse(source))).to_not have_issue(Puppet::Pops::Issues::IDEM_EXPRESSION_NOT_LAST)
+ end
+ end
- let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() }
- let(:validator) { Puppet::Pops::Validation::ValidatorFactory_4_0.new().validator(acceptor) }
+ [
+ 'if 1 {$a = 1}',
+ 'if 1 {2} else {$a=1}',
+ 'if 1 {2} elsif 3 {$a=1}',
+ 'unless 1 {$a=1}',
+ 'unless 1 {2} else {$a=1}',
+ '$a = 1 ? 2 => 3',
+ '$a = 1 ? { 2 => 3}',
+ 'Foo[a] -> Foo[b]',
+ '($a=1)',
+ 'foo()',
+ '$a.foo()'
+ ].each do |expr|
- def validate(model)
- validator.validate(model)
- acceptor
+ it "does not produce error when for productive: #{expr}" do
+ source = "#{expr}; $x = 1"
+ expect(validate(parse(source))).to_not have_issue(Puppet::Pops::Issues::IDEM_EXPRESSION_NOT_LAST)
+ end
+ end
+
+ ['class', 'define', 'node'].each do |type|
+ it "flags non productive expression last in #{type}" do
+ source = <<-SOURCE
+ #{type} nope {
+ 1
+ }
+ end
+ SOURCE
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::IDEM_NOT_ALLOWED_LAST)
+ end
+ end
end
- it 'should raise error for illegal names' do
- pending "validation was too strict, now too relaxed - validation missing"
- expect(validate(fqn('Aaa'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
- expect(validate(fqn('AAA'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
+ context 'for reserved words' do
+ ['function', 'private', 'type', 'attr'].each do |word|
+ it "produces an error for the word '#{word}'" do
+ source = "$a = #{word}"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_WORD)
+ end
+ end
end
- it 'should raise error for illegal variable names' do
- expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
- expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
- expect(validate(fqn('aaa::_aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
+ context 'for reserved type names' do
+ [# type/Type, is a reserved name but results in syntax error because it is a keyword in lower case form
+ 'any',
+ 'unit',
+ 'scalar',
+ 'boolean',
+ 'numeric',
+ 'integer',
+ 'float',
+ 'collection',
+ 'array',
+ 'hash',
+ 'tuple',
+ 'struct',
+ 'variant',
+ 'optional',
+ 'enum',
+ 'regexp',
+ 'pattern',
+ 'runtime',
+ ].each do |name|
+
+ it "produces an error for 'class #{name}'" do
+ source = "class #{name} {}"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_TYPE_NAME)
+ end
+
+ it "produces an error for 'define #{name}'" do
+ source = "define #{name} {}"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_TYPE_NAME)
+ end
+ end
end
- it 'should not raise error for variable name with underscore first in first name segment' do
- expect(validate(fqn('_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
- expect(validate(fqn('::_aa').var())).to_not have_issue(Puppet::Pops::Issues::ILLEGAL_VAR_NAME)
+ context 'for reserved parameter names' do
+ ['name', 'title'].each do |word|
+ it "produces an error when $#{word} is used as a parameter in a class" do
+ source = "class x ($#{word}) {}"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_PARAMETER)
+ end
+
+ it "produces an error when $#{word} is used as a parameter in a define" do
+ source = "define x ($#{word}) {}"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::RESERVED_PARAMETER)
+ end
+ end
+
+ end
+
+ context 'for numeric parameter names' do
+ ['1', '0x2', '03'].each do |word|
+ it "produces an error when $#{word} is used as a parameter in a class" do
+ source = "class x ($#{word}) {}"
+ expect(validate(parse(source))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NUMERIC_PARAMETER)
+ end
+ end
end
+ def parse(source)
+ Puppet::Pops::Parser::Parser.new().parse_string(source)
+ end
end
diff --git a/spec/unit/provider/exec/posix_spec.rb b/spec/unit/provider/exec/posix_spec.rb
index 7c4982fcc..02c338b69 100755
--- a/spec/unit/provider/exec/posix_spec.rb
+++ b/spec/unit/provider/exec/posix_spec.rb
@@ -1,19 +1,18 @@
#! /usr/bin/env ruby
require 'spec_helper'
-describe Puppet::Type.type(:exec).provider(:posix) do
+describe Puppet::Type.type(:exec).provider(:posix), :if => Puppet.features.posix? do
include PuppetSpec::Files
def make_exe
cmdpath = tmpdir('cmdpath')
exepath = tmpfile('my_command', cmdpath)
- exepath = exepath + ".exe" if Puppet.features.microsoft_windows?
FileUtils.touch(exepath)
File.chmod(0755, exepath)
exepath
end
- let(:resource) { Puppet::Type.type(:exec).new(:title => File.expand_path('/foo'), :provider => :posix) }
+ let(:resource) { Puppet::Type.type(:exec).new(:title => '/foo', :provider => :posix) }
let(:provider) { described_class.new(resource) }
describe "#validatecmd" do
@@ -31,7 +30,7 @@ describe Puppet::Type.type(:exec).provider(:posix) do
it "should pass if command is fully qualifed" do
provider.resource[:path] = ['/bogus/bin']
- provider.validatecmd(File.expand_path("/bin/blah/foo"))
+ provider.validatecmd("/bin/blah/foo")
end
end
@@ -64,7 +63,7 @@ describe Puppet::Type.type(:exec).provider(:posix) do
provider.resource[:path] = [File.dirname(command)]
filename = File.basename(command)
- Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == filename) && (arguments.is_a? Hash) }.returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
+ Puppet::Util::Execution.expects(:execute).with(filename, instance_of(Hash)).returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(filename)
end
@@ -91,17 +90,28 @@ describe Puppet::Type.type(:exec).provider(:posix) do
expect { provider.run("cd ..") }.to raise_error(ArgumentError, "Could not find command 'cd'")
end
+ it "does not override the user when it is already the requested user" do
+ Etc.stubs(:getpwuid).returns(Struct::Passwd.new('testing'))
+ provider.resource[:user] = 'testing'
+ command = make_exe
+
+ Puppet::Util::Execution.expects(:execute).with(anything(), has_entry(:uid, nil)).returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
+
+ provider.run(command)
+ 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) }.returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
+ Puppet::Util::Execution.expects(:execute).with("#{command} bar --sillyarg=true --blah", instance_of(Hash)).returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
+
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"
+ command = "/foo bar --sillyarg=true --blah"
expect { provider.run(%Q["#{command}"]) }.to raise_error(ArgumentError, "Could not find command '#{command}'")
end
@@ -110,8 +120,10 @@ describe Puppet::Type.type(:exec).provider(:posix) 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) }.returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
+ Puppet::Util::Execution.expects(:execute).with(command, instance_of(Hash)).returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
+
provider.run(command)
+
@logs.map {|l| "#{l.level}: #{l.message}" }.should == ["warning: Overriding environment setting 'WHATEVER' with '/foo'"]
end
@@ -121,7 +133,7 @@ describe Puppet::Type.type(:exec).provider(:posix) do
provider.run(provider.resource[:command])
end
- describe "posix locale settings", :unless => Puppet.features.microsoft_windows? do
+ describe "posix locale settings" 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 = "en_US.UTF-8"
@@ -160,7 +172,7 @@ describe Puppet::Type.type(:exec).provider(:posix) do
end
end
- describe "posix user-related environment vars", :unless => Puppet.features.microsoft_windows? do
+ describe "posix user-related environment vars" 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 = {}
@@ -202,10 +214,6 @@ describe Puppet::Type.type(:exec).provider(:posix) do
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 0f0faa594..932f46b6a 100755
--- a/spec/unit/provider/exec/shell_spec.rb
+++ b/spec/unit/provider/exec/shell_spec.rb
@@ -2,8 +2,8 @@
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
+ let(:resource) { Puppet::Type.type(:exec).new(:title => 'foo', :provider => 'shell') }
+ let(:provider) { described_class.new(resource) }
describe "#run" do
it "should be able to run builtin shell commands" do
diff --git a/spec/unit/provider/file/windows_spec.rb b/spec/unit/provider/file/windows_spec.rb
index a0e9d3a4e..f6d7ef0e7 100755
--- a/spec/unit/provider/file/windows_spec.rb
+++ b/spec/unit/provider/file/windows_spec.rb
@@ -49,22 +49,22 @@ describe Puppet::Type.type(:file).provider(:windows), :if => Puppet.features.mic
describe "#id2name" do
it "should return the name of the user identified by the sid" do
- Puppet::Util::Windows::Security.expects(:valid_sid?).with(sid).returns(true)
- Puppet::Util::Windows::Security.expects(:sid_to_name).with(sid).returns(account)
+ Puppet::Util::Windows::SID.expects(:valid_sid?).with(sid).returns(true)
+ Puppet::Util::Windows::SID.expects(:sid_to_name).with(sid).returns(account)
provider.id2name(sid).should == account
end
it "should return the argument if it's already a name" do
- Puppet::Util::Windows::Security.expects(:valid_sid?).with(account).returns(false)
- Puppet::Util::Windows::Security.expects(:sid_to_name).never
+ Puppet::Util::Windows::SID.expects(:valid_sid?).with(account).returns(false)
+ Puppet::Util::Windows::SID.expects(:sid_to_name).never
provider.id2name(account).should == account
end
it "should return nil if the user doesn't exist" do
- Puppet::Util::Windows::Security.expects(:valid_sid?).with(sid).returns(true)
- Puppet::Util::Windows::Security.expects(:sid_to_name).with(sid).returns(nil)
+ Puppet::Util::Windows::SID.expects(:valid_sid?).with(sid).returns(true)
+ Puppet::Util::Windows::SID.expects(:sid_to_name).with(sid).returns(nil)
provider.id2name(sid).should == nil
end
@@ -72,7 +72,7 @@ describe Puppet::Type.type(:file).provider(:windows), :if => Puppet.features.mic
describe "#name2id" do
it "should delegate to name_to_sid" do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with(account).returns(sid)
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with(account).returns(sid)
provider.name2id(account).should == sid
end
diff --git a/spec/unit/provider/group/windows_adsi_spec.rb b/spec/unit/provider/group/windows_adsi_spec.rb
index a7de859da..7c9366f72 100644
--- a/spec/unit/provider/group/windows_adsi_spec.rb
+++ b/spec/unit/provider/group/windows_adsi_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Puppet::Type.type(:group).provider(:windows_adsi) do
+describe Puppet::Type.type(:group).provider(:windows_adsi), :if => Puppet.features.microsoft_windows? do
let(:resource) do
Puppet::Type.type(:group).new(
:title => 'testers',
@@ -15,8 +15,8 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
let(:connection) { stub 'connection' }
before :each do
- Puppet::Util::ADSI.stubs(:computer_name).returns('testcomputername')
- Puppet::Util::ADSI.stubs(:connect).returns connection
+ Puppet::Util::Windows::ADSI.stubs(:computer_name).returns('testcomputername')
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns connection
end
describe ".instances" do
@@ -30,14 +30,14 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
end
end
- describe "group type :members property helpers", :if => Puppet.features.microsoft_windows? do
+ describe "group type :members property helpers" do
let(:user1) { stub(:account => 'user1', :domain => '.', :to_s => 'user1sid') }
let(:user2) { stub(:account => 'user2', :domain => '.', :to_s => 'user2sid') }
before :each do
- Puppet::Util::Windows::Security.stubs(:name_to_sid_object).with('user1').returns(user1)
- Puppet::Util::Windows::Security.stubs(:name_to_sid_object).with('user2').returns(user2)
+ Puppet::Util::Windows::SID.stubs(:name_to_sid_object).with('user1').returns(user1)
+ Puppet::Util::Windows::SID.stubs(:name_to_sid_object).with('user2').returns(user2)
end
describe "#members_insync?" do
@@ -89,7 +89,7 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
provider.members.should =~ ['user1', 'user2', 'user3']
end
- it "should be able to set group members", :if => Puppet.features.microsoft_windows? do
+ it "should be able to set group members" do
provider.group.stubs(:members).returns ['user1', 'user2']
member_sids = [
@@ -100,8 +100,8 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
provider.group.stubs(:member_sids).returns(member_sids[0..1])
- Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('user2').returns(member_sids[1])
- Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('user3').returns(member_sids[2])
+ Puppet::Util::Windows::SID.expects(:name_to_sid_object).with('user2').returns(member_sids[1])
+ Puppet::Util::Windows::SID.expects(:name_to_sid_object).with('user3').returns(member_sids[2])
provider.group.expects(:remove_member_sids).with(member_sids[0])
provider.group.expects(:add_member_sids).with(member_sids[2])
@@ -115,7 +115,7 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
resource[:members] = ['user1', 'user2']
group = stub 'group'
- Puppet::Util::ADSI::Group.expects(:create).with('testers').returns group
+ Puppet::Util::Windows::ADSI::Group.expects(:create).with('testers').returns group
create = sequence('create')
group.expects(:commit).in_sequence(create)
@@ -125,7 +125,7 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
end
it 'should not create a group if a user by the same name exists' do
- Puppet::Util::ADSI::Group.expects(:create).with('testers').raises( Puppet::Error.new("Cannot create group if user 'testers' exists.") )
+ Puppet::Util::Windows::ADSI::Group.expects(:create).with('testers').raises( Puppet::Error.new("Cannot create group if user 'testers' exists.") )
expect{ provider.create }.to raise_error( Puppet::Error,
/Cannot create group if user 'testers' exists./ )
end
@@ -138,11 +138,11 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
end
it "should be able to test whether a group exists" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI.stubs(:connect).returns stub('connection')
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns stub('connection')
provider.should be_exists
- Puppet::Util::ADSI.stubs(:connect).returns nil
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns nil
provider.should_not be_exists
end
@@ -152,8 +152,8 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
provider.delete
end
- it "should report the group's SID as gid", :if => Puppet.features.microsoft_windows? do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('testers').returns('S-1-5-32-547')
+ it "should report the group's SID as gid" do
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('testers').returns('S-1-5-32-547')
provider.gid.should == 'S-1-5-32-547'
end
@@ -162,7 +162,7 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
provider.send(:gid=, 500)
end
- it "should prefer the domain component from the resolved SID", :if => Puppet.features.microsoft_windows? do
+ it "should prefer the domain component from the resolved SID" do
provider.members_to_s(['.\Administrators']).should == 'BUILTIN\Administrators'
end
end
diff --git a/spec/unit/provider/package/gem_spec.rb b/spec/unit/provider/package/gem_spec.rb
index f382335cd..23aa2798d 100755
--- a/spec/unit/provider/package/gem_spec.rb
+++ b/spec/unit/provider/package/gem_spec.rb
@@ -174,4 +174,14 @@ describe provider_class do
{:provider=>:gem, :ensure=>["1.11.3.3"], :name=>"rvm"}]
end
end
+
+ describe "listing gems" do
+ describe "searching for a single package" do
+ it "searches for an exact match" do
+ provider_class.expects(:execute).with(includes('^bundler$')).returns(File.read(my_fixture('gem-list-single-package')))
+ expected = {:name => 'bundler', :ensure => %w[1.6.2], :provider => :gem}
+ expect(provider_class.gemlist({:justme => 'bundler'})).to eq(expected)
+ end
+ end
+ end
end
diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb
index 8d4f079fe..712f9cfda 100755
--- a/spec/unit/provider/package/openbsd_spec.rb
+++ b/spec/unit/provider/package/openbsd_spec.rb
@@ -17,7 +17,7 @@ describe provider_class do
def expect_pkgadd_with_source(source)
provider.expects(:pkgadd).with do |fullname|
ENV.should_not be_key('PKG_PATH')
- fullname.should == source
+ fullname.should == [source]
end
end
@@ -28,7 +28,7 @@ describe provider_class do
ENV.should be_key('PKG_PATH')
ENV['PKG_PATH'].should == source
- fullname.should == provider.resource[:name]
+ fullname.should == [provider.resource[:name]]
end
provider.expects(:execpipe).with(['/bin/pkg_info', '-I', provider.resource[:name]]).yields('')
@@ -37,6 +37,15 @@ describe provider_class do
ENV.should_not be_key('PKG_PATH')
end
+ describe 'provider features' do
+ it { should be_installable }
+ it { should be_install_options }
+ it { should be_uninstallable }
+ it { should be_uninstall_options }
+ it { should be_upgradeable }
+ it { should be_versionable }
+ end
+
before :each do
# Stub some provider methods to avoid needing the actual software
# installed, so we can test on whatever platform we want.
@@ -45,7 +54,7 @@ describe provider_class do
provider_class.stubs(:command).with(:pkgdelete).returns('/bin/pkg_delete')
end
- context "::instances" do
+ context "#instances" do
it "should return nil if execution failed" do
provider_class.expects(:execpipe).raises(Puppet::ExecutionFailure, 'wawawa')
provider_class.instances.should be_nil
@@ -69,7 +78,7 @@ describe provider_class do
instances = provider_class.instances.map {|p| {:name => p.get(:name),
:ensure => p.get(:ensure), :flavor => p.get(:flavor)}}
instances.size.should == 2
- instances[0].should == {:name => 'bash', :ensure => '3.1.17', :flavor => 'static'}
+ instances[0].should == {:name => 'bash', :ensure => '3.1.17', :flavor => 'static'}
instances[1].should == {:name => 'vim', :ensure => '7.0.42', :flavor => 'no_x11'}
end
end
@@ -100,7 +109,7 @@ describe provider_class do
end
it "should install correctly when given a directory-unlike source" do
- source = '/whatever.pkg'
+ source = '/whatever.tgz'
provider.resource[:source] = source
expect_pkgadd_with_source(source)
@@ -224,6 +233,46 @@ describe provider_class do
}.to raise_error(Puppet::Error, /No valid installpath found in \/etc\/pkg\.conf and no source was set/)
end
end
+
+ it 'should use install_options as Array' do
+ provider.resource[:source] = '/tma1/'
+ provider.resource[:install_options] = ['-r', '-z']
+ provider.expects(:pkgadd).with(['-r', '-z', 'bash'])
+ provider.install
+ end
+ end
+
+ context "#latest" do
+ before do
+ provider.resource[:source] = '/tmp/tcsh.tgz'
+ provider.resource[:name] = 'tcsh'
+ provider.stubs(:pkginfo).with('tcsh')
+ end
+
+ it "should return the ensure value if the package is already installed" do
+ provider.stubs(:properties).returns({:ensure => '4.2.45'})
+ provider.stubs(:pkginfo).with('-Q', 'tcsh')
+ provider.latest.should == '4.2.45'
+ end
+
+ it "should recognize a new version" do
+ pkginfo_query = 'tcsh-6.18.01p1'
+ provider.stubs(:pkginfo).with('-Q', 'tcsh').returns(pkginfo_query)
+ provider.latest.should == '6.18.01p1'
+ end
+
+ it "should recognize a newer version" do
+ provider.stubs(:properties).returns({:ensure => '1.6.8'})
+ pkginfo_query = 'tcsh-1.6.10'
+ provider.stubs(:pkginfo).with('-Q', 'tcsh').returns(pkginfo_query)
+ provider.latest.should == '1.6.10'
+ end
+
+ it "should recognize a package that is already the newest" do
+ pkginfo_query = 'tcsh-6.18.01p0 (installed)'
+ provider.stubs(:pkginfo).with('-Q', 'tcsh').returns(pkginfo_query)
+ provider.latest.should == '6.18.01p0'
+ end
end
context "#get_version" do
@@ -233,8 +282,8 @@ describe provider_class do
end
it "should return the package version if in the output" do
- fixture = File.read(my_fixture('pkginfo.list'))
- provider.expects(:execpipe).with(%w{/bin/pkg_info -I bash}).yields(fixture)
+ output = 'bash-3.1.17 GNU Bourne Again Shell'
+ provider.expects(:execpipe).with(%w{/bin/pkg_info -I bash}).yields(output)
provider.get_version.should == '3.1.17'
end
@@ -279,7 +328,7 @@ describe provider_class do
provider.install_options.should == ['-Darch=vax']
end
end
-
+
context "#uninstall_options" do
it "should return nill by default" do
provider.uninstall_options.should be_nil
@@ -300,7 +349,7 @@ describe provider_class do
provider.uninstall_options.should == ['-Dbaddepend=1']
end
end
-
+
context "#uninstall" do
describe 'when uninstalling' do
it 'should use erase to purge' do
@@ -308,5 +357,13 @@ describe provider_class do
provider.purge
end
end
+
+ describe 'with uninstall_options' do
+ it 'should use uninstall_options as Array' do
+ provider.resource[:uninstall_options] = ['-q', '-c']
+ provider.expects(:pkgdelete).with(['-q', '-c'], 'bash')
+ provider.uninstall
+ end
+ end
end
end
diff --git a/spec/unit/provider/package/pacman_spec.rb b/spec/unit/provider/package/pacman_spec.rb
index 789fd88fb..aca7c5d64 100755
--- a/spec/unit/provider/package/pacman_spec.rb
+++ b/spec/unit/provider/package/pacman_spec.rb
@@ -2,58 +2,73 @@
require 'spec_helper'
require 'stringio'
-provider = Puppet::Type.type(:package).provider(:pacman)
-describe provider do
+describe Puppet::Type.type(:package).provider(:pacman) do
let(:no_extra_options) { { :failonfail => true, :combine => true, :custom_environment => {} } }
let(:executor) { Puppet::Util::Execution }
let(:resolver) { Puppet::Util }
+ let(:resource) { Puppet::Type.type(:package).new(:name => 'package', :provider => 'pacman') }
+ let(:provider) { described_class.new(resource) }
+
before do
resolver.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman')
- provider.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman')
+ described_class.stubs(:which).with('/usr/bin/pacman').returns('/usr/bin/pacman')
resolver.stubs(:which).with('/usr/bin/yaourt').returns('/usr/bin/yaourt')
- provider.stubs(:which).with('/usr/bin/yaourt').returns('/usr/bin/yaourt')
- @resource = Puppet::Type.type(:package).new(:name => 'package')
- @provider = provider.new(@resource)
+ described_class.stubs(:which).with('/usr/bin/yaourt').returns('/usr/bin/yaourt')
end
describe "when installing" do
before do
- @provider.stubs(:query).returns({
+ provider.stubs(:query).returns({
:ensure => '1.0'
})
end
- it "should call pacman to install the right package quietly" do
-
- if @provider.yaourt?
- args = ['/usr/bin/yaourt', '--noconfirm', '-S', @resource[:name]]
- else
- args = ['/usr/bin/pacman', '--noconfirm', '--noprogressbar', '-Sy', @resource[:name]]
- end
-
- executor.
- expects(:execute).
- at_least_once.
- with(args, no_extra_options).
- returns ''
+ it "should call pacman to install the right package quietly when yaourt is not installed" do
+ provider.stubs(:yaourt?).returns(false)
+ args = ['--noconfirm', '--noprogressbar', '-Sy', resource[:name]]
+ provider.expects(:pacman).at_least_once.with(*args).returns ''
+ provider.install
+ end
- @provider.install
+ it "should call yaourt to install the right package quietly when yaourt is installed" do
+ provider.stubs(:yaourt?).returns(true)
+ args = ['--noconfirm', '-S', resource[:name]]
+ provider.expects(:yaourt).at_least_once.with(*args).returns ''
+ provider.install
end
it "should raise an ExecutionFailure if the installation failed" do
executor.stubs(:execute).returns("")
- @provider.expects(:query).returns(nil)
+ provider.expects(:query).returns(nil)
- lambda { @provider.install }.should raise_exception(Puppet::ExecutionFailure)
+ lambda { provider.install }.should raise_exception(Puppet::ExecutionFailure)
end
- context "when :source is specified" do
- before :each do
- @install = sequence("install")
+ describe "and install_options are given" do
+ before do
+ resource[:install_options] = ['-x', {'--arg' => 'value'}]
+ end
+
+ it "should call pacman to install the right package quietly when yaourt is not installed" do
+ provider.stubs(:yaourt?).returns(false)
+ args = ['--noconfirm', '--noprogressbar', '-x', '--arg=value', '-Sy', resource[:name]]
+ provider.expects(:pacman).at_least_once.with(*args).returns ''
+ provider.install
end
+ it "should call yaourt to install the right package quietly when yaourt is installed" do
+ provider.stubs(:yaourt?).returns(true)
+ args = ['--noconfirm', '-x', '--arg=value', '-S', resource[:name]]
+ provider.expects(:yaourt).at_least_once.with(*args).returns ''
+ provider.install
+ end
+ end
+
+ context "when :source is specified" do
+ let(:install_seq) { sequence("install") }
+
context "recognizable by pacman" do
%w{
/some/package/file
@@ -61,28 +76,28 @@ describe provider do
ftp://some.package.in/the/air
}.each do |source|
it "should install #{source} directly" do
- @resource[:source] = source
+ resource[:source] = source
executor.expects(:execute).
with(all_of(includes("-Sy"), includes("--noprogressbar")), no_extra_options).
- in_sequence(@install).
+ in_sequence(install_seq).
returns("")
executor.expects(:execute).
with(all_of(includes("-U"), includes(source)), no_extra_options).
- in_sequence(@install).
+ in_sequence(install_seq).
returns("")
- @provider.install
+ provider.install
end
end
end
context "as a file:// URL" do
+ let(:actual_file_path) { "/some/package/file" }
+
before do
- @package_file = "file:///some/package/file"
- @actual_file_path = "/some/package/file"
- @resource[:source] = @package_file
+ resource[:source] = "file:///some/package/file"
end
it "should install from the path segment of the URL" do
@@ -91,35 +106,35 @@ describe provider do
includes("--noprogressbar"),
includes("--noconfirm")),
no_extra_options).
- in_sequence(@install).
+ in_sequence(install_seq).
returns("")
executor.expects(:execute).
- with(all_of(includes("-U"), includes(@actual_file_path)), no_extra_options).
- in_sequence(@install).
+ with(all_of(includes("-U"), includes(actual_file_path)), no_extra_options).
+ in_sequence(install_seq).
returns("")
- @provider.install
+ provider.install
end
end
context "as a puppet URL" do
before do
- @resource[:source] = "puppet://server/whatever"
+ resource[:source] = "puppet://server/whatever"
end
it "should fail" do
- lambda { @provider.install }.should raise_error(Puppet::Error)
+ lambda { provider.install }.should raise_error(Puppet::Error)
end
end
context "as a malformed URL" do
before do
- @resource[:source] = "blah://"
+ resource[:source] = "blah://"
end
it "should fail" do
- lambda { @provider.install }.should raise_error(Puppet::Error)
+ lambda { provider.install }.should raise_error(Puppet::Error)
end
end
end
@@ -127,19 +142,23 @@ describe provider do
describe "when updating" do
it "should call install" do
- @provider.expects(:install).returns("install return value")
- @provider.update.should == "install return value"
+ 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 ""
+ args = ["/usr/bin/pacman", "--noconfirm", "--noprogressbar", "-R", resource[:name]]
+ executor.expects(:execute).with(args, no_extra_options).returns ""
+ provider.uninstall
+ end
- @provider.uninstall
+ it "adds any uninstall_options" do
+ resource[:uninstall_options] = ['-x', {'--arg' => 'value'}]
+ args = ["/usr/bin/pacman", "--noconfirm", "--noprogressbar", "-x", "--arg=value", "-R", resource[:name]]
+ executor.expects(:execute).with(args, no_extra_options).returns ""
+ provider.uninstall
end
end
@@ -147,8 +166,8 @@ describe provider do
it "should query pacman" do
executor.
expects(:execute).
- with(["/usr/bin/pacman", "-Qi", @resource[:name]], no_extra_options)
- @provider.query
+ with(["/usr/bin/pacman", "-Qi", resource[:name]], no_extra_options)
+ provider.query
end
it "should return the version" do
@@ -176,20 +195,20 @@ Description : A library-based package manager with dependency support
EOF
executor.expects(:execute).returns(query_output)
- @provider.query.should == {:ensure => "1.01.3-2"}
+ 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
+ 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 == {
+ provider.query.should == {
:ensure => :purged,
:status => 'missing',
- :name => @resource[:name],
+ :name => resource[:name],
:error => 'ok',
}
end
@@ -199,18 +218,18 @@ EOF
describe "when fetching a package list" do
it "should retrieve installed packages" do
- provider.expects(:execpipe).with(["/usr/bin/pacman", '-Q'])
- provider.installedpkgs
+ described_class.expects(:execpipe).with(["/usr/bin/pacman", '-Q'])
+ described_class.installedpkgs
end
it "should retrieve installed package groups" do
- provider.expects(:execpipe).with(["/usr/bin/pacman", '-Qg'])
- provider.installedgroups
+ described_class.expects(:execpipe).with(["/usr/bin/pacman", '-Qg'])
+ described_class.installedgroups
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.installedpkgs
+ described_class.expects(:execpipe).yields(StringIO.new("package1 1.23-4\npackage2 2.00\n"))
+ packages = described_class.installedpkgs
packages.length.should == 2
@@ -228,8 +247,8 @@ EOF
end
it "should return installed groups with a dummy version" do
- provider.expects(:execpipe).yields(StringIO.new("group1 pkg1\ngroup1 pkg2"))
- groups = provider.installedgroups
+ described_class.expects(:execpipe).yields(StringIO.new("group1 pkg1\ngroup1 pkg2"))
+ groups = described_class.installedgroups
groups.length.should == 1
@@ -241,14 +260,14 @@ EOF
end
it "should return nil on error" do
- provider.expects(:execpipe).twice.raises(Puppet::ExecutionFailure.new("ERROR!"))
- provider.instances.should be_nil
+ described_class.expects(:execpipe).twice.raises(Puppet::ExecutionFailure.new("ERROR!"))
+ described_class.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.installedpkgs == []
+ described_class.expects(:execpipe).yields(StringIO.new("blah"))
+ described_class.expects(:warning).with("Failed to match line blah")
+ described_class.installedpkgs == []
end
end
@@ -265,7 +284,7 @@ EOF
in_sequence(get_latest_version).
returns("")
- @provider.latest
+ provider.latest
end
it "should get query pacman for the latest version" do
@@ -277,10 +296,10 @@ EOF
executor.
expects(:execute).
in_sequence(get_latest_version).
- with(['/usr/bin/pacman', '-Sp', '--print-format', '%v', @resource[:name]], no_extra_options).
+ with(['/usr/bin/pacman', '-Sp', '--print-format', '%v', resource[:name]], no_extra_options).
returns("")
- @provider.latest
+ provider.latest
end
it "should return the version number from pacman" do
@@ -289,7 +308,7 @@ EOF
at_least_once().
returns("1.00.2-3\n")
- @provider.latest.should == "1.00.2-3"
+ provider.latest.should == "1.00.2-3"
end
end
end
diff --git a/spec/unit/provider/package/windows/package_spec.rb b/spec/unit/provider/package/windows/package_spec.rb
index 7466be1e9..632fa13a6 100755
--- a/spec/unit/provider/package/windows/package_spec.rb
+++ b/spec/unit/provider/package/windows/package_spec.rb
@@ -32,7 +32,7 @@ describe Puppet::Provider::Package::Windows::Package do
end
end
- context '::with_key' do
+ context '::with_key', :if => Puppet.features.microsoft_windows? do
it 'should search HKLM (64 & 32) and HKCU (64 & 32)' do
seq = sequence('reg')
@@ -44,8 +44,8 @@ describe Puppet::Provider::Package::Windows::Package do
subject.with_key { |key, values| }
end
- it 'should ignore file not found exceptions', :if => Puppet.features.microsoft_windows? do
- ex = Puppet::Util::Windows::Error.new('Failed to open registry key', Windows::Error::ERROR_FILE_NOT_FOUND)
+ it 'should ignore file not found exceptions' do
+ ex = Puppet::Util::Windows::Error.new('Failed to open registry key', Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND)
# make sure we don't stop after the first exception
subject.expects(:open).times(4).raises(ex)
@@ -55,13 +55,13 @@ describe Puppet::Provider::Package::Windows::Package do
keys.should be_empty
end
- it 'should raise other types of exceptions', :if => Puppet.features.microsoft_windows? do
- ex = Puppet::Util::Windows::Error.new('Failed to open registry key', Windows::Error::ERROR_ACCESS_DENIED)
+ it 'should raise other types of exceptions' do
+ ex = Puppet::Util::Windows::Error.new('Failed to open registry key', Puppet::Util::Windows::Error::ERROR_ACCESS_DENIED)
subject.expects(:open).raises(ex)
expect {
subject.with_key{ |key, values| }
- }.to raise_error(Puppet::Error, /Access is denied/)
+ }.to raise_error(Puppet::Util::Windows::Error, /Access is denied/)
end
end
@@ -103,6 +103,21 @@ describe Puppet::Provider::Package::Windows::Package do
end
end
+ context '::munge' do
+ it 'should shell quote strings with spaces and fix forward slashes' do
+ subject.munge('c:/windows/the thing').should == '"c:\windows\the thing"'
+ end
+ it 'should leave properly formatted paths alone' do
+ subject.munge('c:\windows\thething').should == 'c:\windows\thething'
+ end
+ end
+
+ context '::replace_forward_slashes' do
+ it 'should replace forward with back slashes' do
+ subject.replace_forward_slashes('c:/windows/thing/stuff').should == 'c:\windows\thing\stuff'
+ end
+ end
+
context '::quote' do
it 'should shell quote strings with spaces' do
subject.quote('foo bar').should == '"foo bar"'
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
index d7130ee77..20a523be9 100755
--- a/spec/unit/provider/package/yum_spec.rb
+++ b/spec/unit/provider/package/yum_spec.rb
@@ -81,6 +81,7 @@ describe provider_class do
resource[:ensure] = :installed
resource[:install_options] = ['-t', {'-x' => 'expackage'}]
+ provider.expects(:yum).with('-d', '0', '-e', '0', '-y', ['-t', '-x=expackage'], :list, name)
provider.expects(:yum).with('-d', '0', '-e', '0', '-y', ['-t', '-x=expackage'], :install, name)
provider.install
end
diff --git a/spec/unit/provider/parsedfile_spec.rb b/spec/unit/provider/parsedfile_spec.rb
index f8a1773de..b814bc7ee 100755
--- a/spec/unit/provider/parsedfile_spec.rb
+++ b/spec/unit/provider/parsedfile_spec.rb
@@ -108,7 +108,7 @@ describe Puppet::Provider::ParsedFile do
provider.prefetch
@filetype = Puppet::Util::FileType.filetype(:flat).new("/my/file")
- Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file").returns @filetype
+ Puppet::Util::FileType.filetype(:flat).stubs(:new).with("/my/file",nil).returns @filetype
@filetype.stubs(:write)
end
diff --git a/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb b/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb
index 4a950dad3..3d37956c5 100644
--- a/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb
+++ b/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb
@@ -1,7 +1,7 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'win32/taskscheduler' if Puppet.features.microsoft_windows?
+require 'puppet/util/windows/taskscheduler' if Puppet.features.microsoft_windows?
shared_examples_for "a trigger that handles start_date and start_time" do
let(:trigger) do
@@ -569,27 +569,27 @@ describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if
let(:resource) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') }
it 'should consider the user as in sync if the name matches' do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('joe').twice.returns('SID A')
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('joe').twice.returns('SID A')
resource.should be_user_insync('joe', ['joe'])
end
it 'should consider the user as in sync if the current user is fully qualified' do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('joe').returns('SID A')
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('MACHINE\joe').returns('SID A')
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('joe').returns('SID A')
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('MACHINE\joe').returns('SID A')
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
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('system').twice.returns('SYSTEM SID')
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('system').twice.returns('SYSTEM SID')
resource.should be_user_insync('', ['system'])
end
it 'should consider different users as being different' do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('joe').returns('SID A')
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('bob').returns('SID B')
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('joe').returns('SID A')
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('bob').returns('SID B')
resource.should_not be_user_insync('joe', ['bob'])
end
@@ -1469,7 +1469,7 @@ describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if
end
it 'should use nil for user and password when setting the user to the SYSTEM account' do
- Puppet::Util::Windows::Security.stubs(:name_to_sid).with('system').returns('SYSTEM SID')
+ Puppet::Util::Windows::SID.stubs(:name_to_sid).with('system').returns('SYSTEM SID')
resource = Puppet::Type.type(:scheduled_task).new(
:name => 'Test Task',
@@ -1483,7 +1483,7 @@ describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if
end
it 'should use the specified user and password when setting the user to anything other than SYSTEM' do
- Puppet::Util::Windows::Security.stubs(:name_to_sid).with('my_user_name').returns('SID A')
+ Puppet::Util::Windows::SID.stubs(:name_to_sid).with('my_user_name').returns('SID A')
resource = Puppet::Type.type(:scheduled_task).new(
:name => 'Test Task',
diff --git a/spec/unit/provider/service/openbsd_spec.rb b/spec/unit/provider/service/openbsd_spec.rb
index e7ba4a4db..ad1e50d18 100644..100755
--- a/spec/unit/provider/service/openbsd_spec.rb
+++ b/spec/unit/provider/service/openbsd_spec.rb
@@ -173,13 +173,13 @@ describe provider_class do
it "can append to the package_scripts array and return the result" do
provider = described_class.new(Puppet::Type.type(:service).new(:name => 'cupsd'))
provider.expects(:load_rcconf_local_array).returns ['pkg_scripts="dbus_daemon"']
- expect(provider.pkg_scripts_append).to match_array(['dbus_daemon', 'cupsd'])
+ provider.pkg_scripts_append.should === ['dbus_daemon', 'cupsd']
end
it "should not duplicate the script name" do
provider = described_class.new(Puppet::Type.type(:service).new(:name => 'cupsd'))
provider.expects(:load_rcconf_local_array).returns ['pkg_scripts="cupsd dbus_daemon"']
- expect(provider.pkg_scripts_append).to match_array(['dbus_daemon', 'cupsd'])
+ provider.pkg_scripts_append.should === ['cupsd', 'dbus_daemon']
end
end
@@ -210,6 +210,22 @@ describe provider_class do
output = provider.set_content_flags(content,"-d")
output.should match_array(['cupsd_flags="-d"'])
end
+
+ it "does not set empty flags for package scripts" do
+ content = []
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'cupsd'))
+ provider.expects(:in_base?).returns(false)
+ output = provider.set_content_flags(content,'')
+ output.should match_array([nil])
+ end
+
+ it "does set empty flags for base scripts" do
+ content = []
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'ntpd'))
+ provider.expects(:in_base?).returns(true)
+ output = provider.set_content_flags(content,'')
+ output.should match_array(['ntpd_flags=""'])
+ end
end
describe "#remove_content_flags" do
@@ -229,4 +245,12 @@ describe provider_class do
provider.set_content_scripts(content,scripts).should match_array(['pkg_scripts="dbus_daemon cupsd"'])
end
end
+
+ describe "#in_base?" do
+ it "should true if in base" do
+ File.stubs(:readlines).with('/etc/rc.conf').returns(['sshd_flags=""'])
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.in_base?.should be_true
+ end
+ end
end
diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb
index 06be70acb..d31511626 100755
--- a/spec/unit/provider/service/upstart_spec.rb
+++ b/spec/unit/provider/service/upstart_spec.rb
@@ -22,6 +22,11 @@ describe Puppet::Type.type(:service).provider(:upstart) do
provider_class.stubs(:which).with("/sbin/initctl").returns("/sbin/initctl")
end
+ it "should be the default provider on Ubuntu" do
+ Facter.expects(:value).with(:operatingsystem).returns("Ubuntu")
+ described_class.default?.should be_true
+ end
+
describe "excluding services" do
it "ignores tty and serial on Redhat systems" do
Facter.stubs(:value).with(:osfamily).returns('RedHat')
@@ -50,7 +55,13 @@ describe Puppet::Type.type(:service).provider(:upstart) do
end
it "should not find excluded services" do
- processes = "wait-for-state stop/waiting\nportmap-wait start/running\nidmapd-mounting stop/waiting\nstartpar-bridge start/running"
+ processes = "wait-for-state stop/waiting"
+ processes += "\nportmap-wait start/running"
+ processes += "\nidmapd-mounting stop/waiting"
+ processes += "\nstartpar-bridge start/running"
+ processes += "\ncryptdisks-udev stop/waiting"
+ processes += "\nstatd-mounting stop/waiting"
+ processes += "\ngssd-mounting stop/waiting"
provider_class.stubs(:execpipe).yields(processes)
provider_class.instances.should be_empty
end
diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb
index d0cd4e850..2e88c57df 100755
--- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb
+++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb
@@ -99,6 +99,12 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do
@provider_class.parse_options(optionstr).should == options
end
+ it "should parse quoted options" do
+ line = 'command="/usr/local/bin/mybin \"$SSH_ORIGINAL_COMMAND\"" ssh-rsa xxx mykey'
+
+ @provider_class.parse(line)[0][:options][0].should == 'command="/usr/local/bin/mybin \"$SSH_ORIGINAL_COMMAND\""'
+ 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=="
diff --git a/spec/unit/provider/user/user_role_add_spec.rb b/spec/unit/provider/user/user_role_add_spec.rb
index 8dd48e767..42cc4995e 100755
--- a/spec/unit/provider/user/user_role_add_spec.rb
+++ b/spec/unit/provider/user/user_role_add_spec.rb
@@ -317,7 +317,7 @@ EOT
describe "#shadow_entry" do
it "should return the line for the right user" do
File.stubs(:readlines).returns(["someuser:!:10:5:20:7:1::\n", "fakeval:*:20:10:30:7:2::\n", "testuser:*:30:15:40:7:3::\n"])
- provider.shadow_entry.should == ["fakeval", "*", "20", "10", "30", "7", "2"]
+ provider.shadow_entry.should == ["fakeval", "*", "20", "10", "30", "7", "2", "", ""]
end
end
@@ -331,5 +331,27 @@ EOT
File.stubs(:readlines).returns(["fakeval:NP:12345::::::\n"])
provider.password_max_age.should == -1
end
+
+ it "should return -1 for no maximum when failed attempts are present" do
+ File.stubs(:readlines).returns(["fakeval:NP:12345::::::3\n"])
+ provider.password_max_age.should == -1
+ end
+ end
+
+ describe "#password_min_age" do
+ it "should return a minimum age number" do
+ File.stubs(:readlines).returns(["fakeval:NP:12345:10:50::::\n"])
+ provider.password_min_age.should == "10"
+ end
+
+ it "should return -1 for no minimum" do
+ File.stubs(:readlines).returns(["fakeval:NP:12345::::::\n"])
+ provider.password_min_age.should == -1
+ end
+
+ it "should return -1 for no minimum when failed attempts are present" do
+ File.stubs(:readlines).returns(["fakeval:NP:12345::::::3\n"])
+ provider.password_min_age.should == -1
+ end
end
end
diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb
index 8d3ed1d0a..84aa8a74c 100755
--- a/spec/unit/provider/user/windows_adsi_spec.rb
+++ b/spec/unit/provider/user/windows_adsi_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-describe Puppet::Type.type(:user).provider(:windows_adsi) do
+describe Puppet::Type.type(:user).provider(:windows_adsi), :if => Puppet.features.microsoft_windows? do
let(:resource) do
Puppet::Type.type(:user).new(
:title => 'testuser',
@@ -16,8 +16,8 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
let(:connection) { stub 'connection' }
before :each do
- Puppet::Util::ADSI.stubs(:computer_name).returns('testcomputername')
- Puppet::Util::ADSI.stubs(:connect).returns connection
+ Puppet::Util::Windows::ADSI.stubs(:computer_name).returns('testcomputername')
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns connection
end
describe ".instances" do
@@ -30,8 +30,8 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
end
end
- it "should provide access to a Puppet::Util::ADSI::User object" do
- provider.user.should be_a(Puppet::Util::ADSI::User)
+ it "should provide access to a Puppet::Util::Windows::ADSI::User object" do
+ provider.user.should be_a(Puppet::Util::Windows::ADSI::User)
end
describe "when managing groups" do
@@ -68,7 +68,7 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
resource[:home] = 'C:\Users\testuser'
user = stub 'user'
- Puppet::Util::ADSI::User.expects(:create).with('testuser').returns user
+ Puppet::Util::Windows::ADSI::User.expects(:create).with('testuser').returns user
user.stubs(:groups).returns(['group2', 'group3'])
@@ -82,12 +82,12 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
provider.create
end
- it "should load the profile if managehome is set", :if => Puppet.features.microsoft_windows? do
+ it "should load the profile if managehome is set" do
resource[:password] = '0xDeadBeef'
resource[:managehome] = true
user = stub_everything 'user'
- Puppet::Util::ADSI::User.expects(:create).with('testuser').returns user
+ Puppet::Util::Windows::ADSI::User.expects(:create).with('testuser').returns user
Puppet::Util::Windows::User.expects(:load_profile).with('testuser', '0xDeadBeef')
provider.create
@@ -115,18 +115,18 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
end
it 'should not create a user if a group by the same name exists' do
- Puppet::Util::ADSI::User.expects(:create).with('testuser').raises( Puppet::Error.new("Cannot create user if group 'testuser' exists.") )
+ Puppet::Util::Windows::ADSI::User.expects(:create).with('testuser').raises( Puppet::Error.new("Cannot create user if group 'testuser' exists.") )
expect{ provider.create }.to raise_error( Puppet::Error,
/Cannot create user if group 'testuser' exists./ )
end
end
it 'should be able to test whether a user exists' do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI.stubs(:connect).returns stub('connection')
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns stub('connection')
provider.should be_exists
- Puppet::Util::ADSI.stubs(:connect).returns nil
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns nil
provider.should_not be_exists
end
@@ -136,12 +136,12 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
provider.delete
end
- it 'should delete the profile if managehome is set', :if => Puppet.features.microsoft_windows? do
+ it 'should delete the profile if managehome is set' do
resource[:managehome] = true
sid = 'S-A-B-C'
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('testuser').returns(sid)
- Puppet::Util::ADSI::UserProfile.expects(:delete).with(sid)
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('testuser').returns(sid)
+ Puppet::Util::Windows::ADSI::UserProfile.expects(:delete).with(sid)
connection.expects(:Delete).with('user', 'testuser')
provider.delete
@@ -153,8 +153,8 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
provider.flush
end
- it "should return the user's SID as uid", :if => Puppet.features.microsoft_windows? do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('testuser').returns('S-1-5-21-1362942247-2130103807-3279964888-1111')
+ it "should return the user's SID as uid" do
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('testuser').returns('S-1-5-21-1362942247-2130103807-3279964888-1111')
provider.uid.should == 'S-1-5-21-1362942247-2130103807-3279964888-1111'
end
diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb
index 7866571a1..7f94f7d1b 100755
--- a/spec/unit/reports/store_spec.rb
+++ b/spec/unit/reports/store_spec.rb
@@ -30,25 +30,9 @@ describe processor do
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
-
it "rejects invalid hostnames" do
@report.host = ".."
Puppet::FileSystem.expects(:exist?).never
- Tempfile.expects(:new).never
expect { @report.process }.to raise_error(ArgumentError, /Invalid node/)
end
end
diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb
index 741f60ef3..80933dc41 100755
--- a/spec/unit/resource/catalog_spec.rb
+++ b/spec/unit/resource/catalog_spec.rb
@@ -606,7 +606,6 @@ describe Puppet::Resource::Catalog, "when compiling" do
@catalog.apply
end
- after { Puppet.settings.clear }
end
describe "non-host catalogs" do
@@ -627,7 +626,6 @@ describe Puppet::Resource::Catalog, "when compiling" do
@catalog.apply
end
- after { Puppet.settings.clear }
end
end
@@ -659,9 +657,6 @@ describe Puppet::Resource::Catalog, "when compiling" do
@catalog.write_graph(@name)
end
- after do
- Puppet.settings.clear
- end
end
describe "when indirecting" do
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 73c27c1fe..e954a2179 100755
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -592,7 +592,7 @@ describe Puppet::Resource do
text = @resource.render('yaml')
newresource = Puppet::Resource.convert_from('yaml', text)
- newresource.should equal_attributes_of @resource
+ newresource.should equal_resource_attributes_of @resource
end
end
@@ -615,7 +615,7 @@ describe Puppet::Resource do
text = @resource.render('yaml')
newresource = Puppet::Resource.convert_from('yaml', text)
- newresource.should equal_attributes_of @resource
+ newresource.should equal_resource_attributes_of @resource
end
end
diff --git a/spec/unit/settings/array_setting_spec.rb b/spec/unit/settings/array_setting_spec.rb
new file mode 100644
index 000000000..05cc28d6a
--- /dev/null
+++ b/spec/unit/settings/array_setting_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+require 'puppet/settings'
+require 'puppet/settings/array_setting'
+
+describe Puppet::Settings::ArraySetting do
+ subject { described_class.new(:settings => stub('settings'), :desc => "test") }
+
+ it "is of type :array" do
+ expect(subject.type).to eq :array
+ end
+
+ describe "munging the value" do
+ describe "when given a string" do
+ it "splits multiple values into an array" do
+ expect(subject.munge("foo,bar")).to eq %w[foo bar]
+ end
+ it "strips whitespace between elements" do
+ expect(subject.munge("foo , bar")).to eq %w[foo bar]
+ end
+
+ it "creates an array when one item is given" do
+ expect(subject.munge("foo")).to eq %w[foo]
+ end
+ end
+
+ describe "when given an array" do
+ it "returns the array" do
+ expect(subject.munge(%w[foo])).to eq %w[foo]
+ end
+ end
+
+ it "raises an error when given an unexpected object type" do
+ expect {
+ subject.munge({:foo => 'bar'})
+ }.to raise_error(ArgumentError, "Expected an Array or String, got a Hash")
+ end
+ end
+end
diff --git a/spec/unit/settings/autosign_setting_spec.rb b/spec/unit/settings/autosign_setting_spec.rb
index 0c8184c8a..0dbfe4ecb 100644
--- a/spec/unit/settings/autosign_setting_spec.rb
+++ b/spec/unit/settings/autosign_setting_spec.rb
@@ -73,7 +73,7 @@ describe Puppet::Settings::AutosignSetting do
describe "converting the setting to a resource" do
it "converts the file path to a file resource" do
path = File.expand_path('/path/to/autosign.conf')
- settings.stubs(:value).with('autosign').returns(path)
+ settings.stubs(:value).with('autosign', nil, false).returns(path)
Puppet::FileSystem.stubs(:exist?).with(path).returns true
Puppet.stubs(:features).returns(stub(:root? => true, :microsoft_windows? => false))
@@ -91,7 +91,7 @@ describe Puppet::Settings::AutosignSetting do
end
it "returns nil when the setting is a boolean" do
- settings.stubs(:value).with('autosign').returns 'true'
+ settings.stubs(:value).with('autosign', nil, false).returns 'true'
setting.mode = '0664'
setting.owner = 'service'
diff --git a/spec/unit/settings/environment_conf_spec.rb b/spec/unit/settings/environment_conf_spec.rb
index 6a8a6689e..e4a492ae8 100644
--- a/spec/unit/settings/environment_conf_spec.rb
+++ b/spec/unit/settings/environment_conf_spec.rb
@@ -3,31 +3,47 @@ require 'puppet/settings/environment_conf.rb'
describe Puppet::Settings::EnvironmentConf do
+ def setup_environment_conf(config, conf_hash)
+ conf_hash.each do |setting,value|
+ config.expects(:setting).with(setting).returns(
+ mock('setting', :value => value)
+ )
+ end
+ end
+
context "with config" do
- let(:config) { stub(:config) }
+ let(:config) { stub('config') }
let(:envconf) { Puppet::Settings::EnvironmentConf.new("/some/direnv", config, ["/global/modulepath"]) }
it "reads a modulepath from config and does not include global_module_path" do
- config.expects(:setting).with(:modulepath).returns(
- mock('setting', :value => '/some/modulepath')
- )
+ setup_environment_conf(config, :modulepath => '/some/modulepath')
+
expect(envconf.modulepath).to eq(File.expand_path('/some/modulepath'))
end
it "reads a manifest from config" do
- config.expects(:setting).with(:manifest).returns(
- mock('setting', :value => '/some/manifest')
- )
+ setup_environment_conf(config, :manifest => '/some/manifest')
+
expect(envconf.manifest).to eq(File.expand_path('/some/manifest'))
end
it "reads a config_version from config" do
- config.expects(:setting).with(:config_version).returns(
- mock('setting', :value => '/some/version.sh')
- )
+ setup_environment_conf(config, :config_version => '/some/version.sh')
+
expect(envconf.config_version).to eq(File.expand_path('/some/version.sh'))
end
+ it "read an environment_timeout from config" do
+ setup_environment_conf(config, :environment_timeout => '3m')
+
+ expect(envconf.environment_timeout).to eq(180)
+ end
+
+ it "can retrieve raw settings" do
+ setup_environment_conf(config, :manifest => 'manifest.pp')
+
+ expect(envconf.raw_setting(:manifest)).to eq('manifest.pp')
+ end
end
context "without config" do
@@ -47,5 +63,56 @@ describe Puppet::Settings::EnvironmentConf do
it "returns nothing for config_version when config has none" do
expect(envconf.config_version).to be_nil
end
+
+ it "returns a defult of 0 for environment_timeout when config has none" do
+ expect(envconf.environment_timeout).to eq(0)
+ end
+
+ it "can still retrieve raw setting" do
+ expect(envconf.raw_setting(:manifest)).to be_nil
+ end
+ end
+
+ describe "with disable_per_environment_manifest" do
+
+ let(:config) { stub('config') }
+ let(:envconf) { Puppet::Settings::EnvironmentConf.new("/some/direnv", config, ["/global/modulepath"]) }
+
+ context "set true" do
+
+ before(:each) do
+ Puppet[:default_manifest] = File.expand_path('/default/manifest')
+ Puppet[:disable_per_environment_manifest] = true
+ end
+
+ it "ignores environment.conf manifest" do
+ setup_environment_conf(config, :manifest => '/some/manifest.pp')
+
+ expect(envconf.manifest).to eq(File.expand_path('/default/manifest'))
+ end
+
+ it "logs error when environment.conf has manifest set" do
+ setup_environment_conf(config, :manifest => '/some/manifest.pp')
+
+ envconf.manifest
+ expect(@logs.first.to_s).to match(/disable_per_environment_manifest.*true.*environment.conf.*does not match the default_manifest/)
+ end
+
+ it "does not log an error when environment.conf does not have a manifest set" do
+ setup_environment_conf(config, :manifest => nil)
+
+ expect(envconf.manifest).to eq(File.expand_path('/default/manifest'))
+ expect(@logs).to be_empty
+ end
+ end
+
+ it "uses environment.conf when false" do
+ setup_environment_conf(config, :manifest => '/some/manifest.pp')
+
+ Puppet[:default_manifest] = File.expand_path('/default/manifest')
+ Puppet[:disable_per_environment_manifest] = false
+
+ expect(envconf.manifest).to eq(File.expand_path('/some/manifest.pp'))
+ end
end
end
diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb
index b31d0ccb3..c77cc5981 100755
--- a/spec/unit/settings/file_setting_spec.rb
+++ b/spec/unit/settings/file_setting_spec.rb
@@ -129,7 +129,7 @@ describe Puppet::Settings::FileSetting 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
+ @settings.stubs(:value).with(:myfile, nil, false).returns @basepath
end
it "should return :file as its type" do
@@ -146,19 +146,20 @@ describe Puppet::Settings::FileSetting do
it "should manage existent files even if 'create_files' is not enabled" do
@file.expects(:create_files?).returns false
@file.expects(:type).returns :file
+ Puppet::FileSystem.stubs(:exist?)
Puppet::FileSystem.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"
+ @settings.stubs(:value).with(:myfile, nil, false).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
+ @settings.stubs(:value).with(:myfile, nil, false).returns :foo
@file.to_resource.should be_nil
end
@@ -169,7 +170,7 @@ describe Puppet::Settings::FileSetting do
end
it "should fully qualified returned files if necessary (#795)" do
- @settings.stubs(:value).with(:myfile).returns "myfile"
+ @settings.stubs(:value).with(:myfile, nil, false).returns "myfile"
path = File.expand_path('myfile')
@file.to_resource.title.should == path
end
diff --git a/spec/unit/settings/priority_setting_spec.rb b/spec/unit/settings/priority_setting_spec.rb
index d51e39dc4..62cad5def 100755
--- a/spec/unit/settings/priority_setting_spec.rb
+++ b/spec/unit/settings/priority_setting_spec.rb
@@ -53,10 +53,10 @@ describe Puppet::Settings::PrioritySetting do
describe "on a Windows-like platform it", :if => Puppet::Util::Platform.windows? do
it "parses high, normal, low, and idle priorities" do
{
- 'high' => Process::HIGH_PRIORITY_CLASS,
- 'normal' => Process::NORMAL_PRIORITY_CLASS,
- 'low' => Process::BELOW_NORMAL_PRIORITY_CLASS,
- 'idle' => Process::IDLE_PRIORITY_CLASS
+ 'high' => Puppet::Util::Windows::Process::HIGH_PRIORITY_CLASS,
+ 'normal' => Puppet::Util::Windows::Process::NORMAL_PRIORITY_CLASS,
+ 'low' => Puppet::Util::Windows::Process::BELOW_NORMAL_PRIORITY_CLASS,
+ 'idle' => Puppet::Util::Windows::Process::IDLE_PRIORITY_CLASS
}.each do |value, converted_value|
setting.munge(value).should == converted_value
end
diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb
index 66ec67607..0a1cee2bd 100755
--- a/spec/unit/settings_spec.rb
+++ b/spec/unit/settings_spec.rb
@@ -674,7 +674,7 @@ describe Puppet::Settings do
@settings.send(:parse_config_files)
expect(@settings.value(:manifestdir)).to eq("/somewhere/production/manifests")
end
-
+
it "interpolates the set environment when no environment specified" do
text = <<-EOF
[main]
@@ -1003,102 +1003,95 @@ describe Puppet::Settings do
end
describe "deprecations" do
- context "in puppet.conf" do
-
- def assert_puppet_conf_deprecation(setting, matches)
- Puppet.expects(:deprecation_warning).with(regexp_matches(matches), anything)
-
- val = "/you/can/set/this/but/will/get/warning"
- text = "[main]
- #{setting}=#{val}
- "
- Puppet.settings.parse_config(text)
- end
-
- it "warns when manifest is set" do
- assert_puppet_conf_deprecation('manifest', /manifest.*puppet.conf/)
- end
-
- it "warns when modulepath is set" do
- assert_puppet_conf_deprecation('modulepath', /modulepath.*puppet.conf/)
- end
-
- it "warns when config_version is set" do
- assert_puppet_conf_deprecation('config_version', /config_version.*puppet.conf/)
- end
-
- it "warns when manifestdir is set" do
- assert_puppet_conf_deprecation('manifestdir', /Setting manifestdir.*is.*deprecated/)
- end
-
- it "warns when templatedir is set" do
- assert_puppet_conf_deprecation('templatedir', /Setting templatedir.*is.*deprecated/)
- end
+ let(:settings) { Puppet::Settings.new }
+ let(:app_defaults) {
+ {
+ :logdir => "/dev/null",
+ :confdir => "/dev/null",
+ :vardir => "/dev/null",
+ }
+ }
+
+ def assert_accessing_setting_is_deprecated(settings, setting)
+ Puppet.expects(:deprecation_warning).with("Accessing '#{setting}' as a setting is deprecated. See http://links.puppetlabs.com/env-settings-deprecations")
+ Puppet.expects(:deprecation_warning).with("Modifying '#{setting}' as a setting is deprecated. See http://links.puppetlabs.com/env-settings-deprecations")
+ settings[setting.intern] = apath = File.expand_path('foo')
+ expect(settings[setting.intern]).to eq(apath)
end
- context "on the command line" do
- def assert_command_line_deprecation(setting, message)
- Puppet.expects(:deprecation_warning).with(message, anything)
-
- args = ["--#{setting}", "/some/value"]
- Puppet.settings.send(:parse_global_options, args)
- end
-
- def assert_command_line_not_deprecated(setting)
- Puppet.expects(:deprecation_warning).never
-
- args = ["--#{setting}", "/some/value"]
- Puppet.settings.send(:parse_global_options, args)
+ before(:each) do
+ settings.define_settings(:main, {
+ :logdir => { :default => 'a', :desc => 'a' },
+ :confdir => { :default => 'b', :desc => 'b' },
+ :vardir => { :default => 'c', :desc => 'c' },
+ })
+ end
+
+ context "complete" do
+ let(:completely_deprecated_settings) do
+ settings.define_settings(:main, {
+ :manifestdir => {
+ :default => 'foo',
+ :desc => 'a deprecated setting',
+ :deprecated => :completely,
+ }
+ })
+ settings
end
- it "does not warn when manifest is set on command line" do
- assert_command_line_not_deprecated('manifest')
- end
+ it "warns when set in puppet.conf" do
+ Puppet.expects(:deprecation_warning).with(regexp_matches(/manifestdir is deprecated\./), 'setting-manifestdir')
- it "does not warn when modulepath is set on command line" do
- assert_command_line_not_deprecated('modulepath')
+ completely_deprecated_settings.parse_config(<<-CONF)
+ manifestdir='should warn'
+ CONF
+ completely_deprecated_settings.initialize_app_defaults(app_defaults)
end
- it "does not warn when config_version is set on command line" do
- assert_command_line_not_deprecated('config_version')
- end
+ it "warns when set on the commandline" do
+ Puppet.expects(:deprecation_warning).with(regexp_matches(/manifestdir is deprecated\./), 'setting-manifestdir')
- it "warns when manifestdir is set on command line" do
- assert_command_line_deprecation('manifestdir', "Setting manifestdir is deprecated. See http://links.puppetlabs.com/env-settings-deprecations")
+ args = ["--manifestdir", "/some/value"]
+ completely_deprecated_settings.send(:parse_global_options, args)
+ completely_deprecated_settings.initialize_app_defaults(app_defaults)
end
- it "warns when templatedir is set on command line" do
- assert_command_line_deprecation('templatedir', "Setting templatedir is deprecated. See http://links.puppetlabs.com/env-settings-deprecations")
+ it "warns when set in code" do
+ assert_accessing_setting_is_deprecated(completely_deprecated_settings, 'manifestdir')
end
end
- context "as settings in the code base" do
- def assert_accessing_setting_is_deprecated(setting)
- Puppet.expects(:deprecation_warning).with("Accessing '#{setting}' as a setting is deprecated. See http://links.puppetlabs.com/env-settings-deprecations")
- Puppet.expects(:deprecation_warning).with("Modifying '#{setting}' as a setting is deprecated. See http://links.puppetlabs.com/env-settings-deprecations")
- Puppet[setting.intern] = apath = File.expand_path('foo')
- expect(Puppet[setting.intern]).to eq(apath)
+ context "partial" do
+ let(:partially_deprecated_settings) do
+ settings.define_settings(:main, {
+ :modulepath => {
+ :default => 'foo',
+ :desc => 'a partially deprecated setting',
+ :deprecated => :allowed_on_commandline,
+ }
+ })
+ settings
end
- it "warns when attempt to access a 'manifest' setting" do
- assert_accessing_setting_is_deprecated('manifest')
+ it "warns for a deprecated setting allowed on the command line set in puppet.conf" do
+ Puppet.expects(:deprecation_warning).with(regexp_matches(/modulepath is deprecated in puppet\.conf/), 'puppet-conf-setting-modulepath')
+ partially_deprecated_settings.parse_config(<<-CONF)
+ modulepath='should warn'
+ CONF
+ partially_deprecated_settings.initialize_app_defaults(app_defaults)
end
- it "warns when attempt to access a 'modulepath' setting" do
- assert_accessing_setting_is_deprecated('modulepath')
- end
- it "warns when attempt to access a 'config_version' setting" do
- assert_accessing_setting_is_deprecated('config_version')
- end
+ it "does not warn when manifest is set on command line" do
+ Puppet.expects(:deprecation_warning).never
- it "warns when attempt to access a 'manifestdir' setting" do
- assert_accessing_setting_is_deprecated('manifestdir')
+ args = ["--modulepath", "/some/value"]
+ partially_deprecated_settings.send(:parse_global_options, args)
+ partially_deprecated_settings.initialize_app_defaults(app_defaults)
end
- it "warns when attempt to access a 'templatedir' setting" do
- assert_accessing_setting_is_deprecated('templatedir')
+ it "warns when set in code" do
+ assert_accessing_setting_is_deprecated(partially_deprecated_settings, 'modulepath')
end
-
end
end
end
@@ -1366,6 +1359,44 @@ describe Puppet::Settings do
end
end
+ describe "adding default directory environment to the catalog" do
+ let(:tmpenv) { tmpdir("envs") }
+ let(:default_path) { "#{tmpenv}/environments" }
+ before(:each) do
+ @settings.define_settings :main,
+ :environment => { :default => "production", :desc => "env"},
+ :environmentpath => { :type => :path, :default => default_path, :desc => "envpath"}
+ end
+
+ it "adds if environmentpath exists" do
+ envpath = "#{tmpenv}/custom_envpath"
+ @settings[:environmentpath] = envpath
+ Dir.mkdir(envpath)
+ catalog = @settings.to_catalog
+ expect(catalog.resource_keys).to include(["File", "#{envpath}/production"])
+ end
+
+ it "adds the first directory of environmentpath" do
+ envdir = "#{tmpenv}/custom_envpath"
+ envpath = "#{envdir}#{File::PATH_SEPARATOR}/some/other/envdir"
+ @settings[:environmentpath] = envpath
+ Dir.mkdir(envdir)
+ catalog = @settings.to_catalog
+ expect(catalog.resource_keys).to include(["File", "#{envdir}/production"])
+ end
+
+ it "handles a non-existent environmentpath" do
+ catalog = @settings.to_catalog
+ expect(catalog.resource_keys).to be_empty
+ end
+
+ it "handles a default environmentpath" do
+ Dir.mkdir(default_path)
+ catalog = @settings.to_catalog
+ expect(catalog.resource_keys).to include(["File", "#{default_path}/production"])
+ end
+ end
+
describe "when adding users and groups to the catalog" do
before do
Puppet.features.stubs(:root?).returns true
@@ -1475,14 +1506,14 @@ describe Puppet::Settings do
: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 :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}
+ @settings.define_settings :files, :myfile => {:type => :file, :default => make_absolute("/myfile"), :desc => "a", :mode => '0755'}
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)
+ Dir.expects(:mkdir).with(make_absolute("/otherdir"), '0755')
@settings.mkdir(:otherdir)
end
diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb
index ef5a86862..2881b0a1e 100755
--- a/spec/unit/ssl/certificate_authority_spec.rb
+++ b/spec/unit/ssl/certificate_authority_spec.rb
@@ -7,7 +7,6 @@ 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
@@ -937,12 +936,36 @@ describe Puppet::SSL::CertificateAuthority do
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.inventory.expects(:serials).with("host").returns [16]
@ca.crl.expects(:revoke).with { |serial, key| serial == 16 }
@ca.revoke('host')
end
+ it "should revoke all serials matching a name" 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(:serials).with("host").returns [16, 20, 25]
+
+ @ca.crl.expects(:revoke).with { |serial, key| serial == 16 }
+ @ca.crl.expects(:revoke).with { |serial, key| serial == 20 }
+ @ca.crl.expects(:revoke).with { |serial, key| serial == 25 }
+ @ca.revoke('host')
+ end
+
+ it "should raise an error if no certificate match" 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(:serials).with("host").returns []
+
+ @ca.crl.expects(:revoke).never
+ expect { @ca.revoke('host') }.to raise_error
+ end
+
context "revocation by serial number (#16798)" do
it "revokes when given a lower case hexadecimal formatted string" do
@ca.crl.expects(:revoke).with { |serial, key| serial == 15 }
diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb
index 6e4fbd340..879fd90d1 100755
--- a/spec/unit/ssl/inventory_spec.rb
+++ b/spec/unit/ssl/inventory_spec.rb
@@ -133,5 +133,18 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
@inventory.serial("me").should == 15
end
end
+
+ describe "and finding all serial numbers" do
+ it "should return nil if the inventory file is missing" do
+ Puppet::FileSystem.expects(:exist?).with(cert_inventory).returns false
+ @inventory.serials(:whatever).should be_empty
+ end
+
+ it "should return the all the serial numbers from the lines matching the provided name" do
+ File.expects(:readlines).with(cert_inventory).returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n", "0x002 blah blah /CN=me\n"]
+
+ @inventory.serials("me").should == [15, 2]
+ end
+ end
end
end
diff --git a/spec/unit/ssl/validator_spec.rb b/spec/unit/ssl/validator_spec.rb
index 2b8cfb0f9..ade1575dc 100644
--- a/spec/unit/ssl/validator_spec.rb
+++ b/spec/unit/ssl/validator_spec.rb
@@ -1,6 +1,5 @@
require 'spec_helper'
require 'puppet/ssl'
-require 'puppet/ssl/configuration'
describe Puppet::SSL::Validator::DefaultValidator do
let(:ssl_context) do
diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb
index 5eeaf0ba4..7d9c6f439 100755
--- a/spec/unit/transaction/resource_harness_spec.rb
+++ b/spec/unit/transaction/resource_harness_spec.rb
@@ -352,6 +352,70 @@ describe Puppet::Transaction::ResourceHarness do
event.status.should != 'failure'
end
end
+
+ it "should not ignore microseconds when auditing a file's mtime" do
+ test_file = tmpfile('foo')
+ File.open(test_file, 'w').close
+ resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['mtime'], :backup => false
+
+ # construct a property hash with nanosecond resolution as would be
+ # found on an ext4 file system
+ time_with_nsec_resolution = Time.at(1000, 123456.999)
+ current_from_filesystem = {:mtime => time_with_nsec_resolution}
+
+ # construct a property hash with a 1 microsecond difference from above
+ time_with_usec_resolution = Time.at(1000, 123457.000)
+ historical_from_state_yaml = {:mtime => time_with_usec_resolution}
+
+ # set up the sequence of stubs; yeah, this is pretty
+ # brittle, so this might need to be adjusted if the
+ # resource_harness logic changes
+ resource.expects(:retrieve).returns(current_from_filesystem)
+ Puppet::Util::Storage.stubs(:cache).with(resource).
+ returns(historical_from_state_yaml).then.
+ returns(current_from_filesystem).then.
+ returns(current_from_filesystem)
+
+ # there should be an audit change recorded, since the two
+ # timestamps differ by at least 1 microsecond
+ status = @harness.evaluate(resource)
+ status.events.should_not be_empty
+ status.events.each do |event|
+ event.message.should =~ /audit change: previously recorded/
+ end
+ end
+
+ it "should ignore nanoseconds when auditing a file's mtime" do
+ test_file = tmpfile('foo')
+ File.open(test_file, 'w').close
+ resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['mtime'], :backup => false
+
+ # construct a property hash with nanosecond resolution as would be
+ # found on an ext4 file system
+ time_with_nsec_resolution = Time.at(1000, 123456.789)
+ current_from_filesystem = {:mtime => time_with_nsec_resolution}
+
+ # construct a property hash with the same timestamp as above,
+ # truncated to microseconds, as would be read back from state.yaml
+ time_with_usec_resolution = Time.at(1000, 123456.000)
+ historical_from_state_yaml = {:mtime => time_with_usec_resolution}
+
+ # set up the sequence of stubs; yeah, this is pretty
+ # brittle, so this might need to be adjusted if the
+ # resource_harness logic changes
+ resource.expects(:retrieve).returns(current_from_filesystem)
+ Puppet::Util::Storage.stubs(:cache).with(resource).
+ returns(historical_from_state_yaml).then.
+ returns(current_from_filesystem).then.
+ returns(current_from_filesystem)
+
+ # there should be no audit change recorded, despite the
+ # slight difference in the two timestamps
+ status = @harness.evaluate(resource)
+ status.events.each do |event|
+ event.message.should_not =~ /audit change: previously recorded/
+ end
+ end
end
describe "when applying changes" do
diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb
index 04ce08c4f..bf7820227 100755
--- a/spec/unit/transaction_spec.rb
+++ b/spec/unit/transaction_spec.rb
@@ -37,7 +37,8 @@ describe Puppet::Transaction do
# 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)
+ transaction = transaction_with_resource(resource)
+ transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status)
end
it "should add provided resource statuses to its report" do
@@ -72,15 +73,15 @@ describe Puppet::Transaction do
describe "when initializing" do
it "should create an event manager" do
- @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)
- @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager)
- @transaction.event_manager.transaction.should equal(@transaction)
+ transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)
+ 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, nil, nil)
- @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness)
- @transaction.resource_harness.transaction.should equal(@transaction)
+ transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, nil, nil)
+ transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness)
+ transaction.resource_harness.transaction.should equal(transaction)
end
it "should set retrieval time on the report" do
@@ -95,29 +96,25 @@ describe Puppet::Transaction do
end
describe "when evaluating a resource" do
- before do
- @catalog = Puppet::Resource::Catalog.new
- @resource = Puppet::Type.type(:file).new :path => @basepath
- @catalog.add_resource(@resource)
-
- @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::RandomPrioritizer.new)
- @transaction.stubs(:skip?).returns false
- end
+ let(:resource) { Puppet::Type.type(:file).new :path => @basepath }
it "should process events" do
- @transaction.event_manager.expects(:process_events).with(@resource)
+ transaction = transaction_with_resource(resource)
- @transaction.evaluate
+ transaction.expects(:skip?).with(resource).returns false
+ transaction.event_manager.expects(:process_events).with(resource)
+
+ transaction.evaluate
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.evaluate
- @transaction.resource_status(@resource).should be_skipped
+ transaction = transaction_with_resource(resource)
+
+ transaction.expects(:skip?).with(resource).returns true
+
+ transaction.evaluate
+ transaction.resource_status(resource).should be_skipped
end
end
end
@@ -288,6 +285,9 @@ describe Puppet::Transaction do
before :each do
catalog.add_resource generator
generator.stubs(:generate).returns generated
+ # avoid crude failures because of nil resources that result
+ # from implicit containment and lacking containers
+ catalog.stubs(:container_of).returns generator
end
it "should call 'generate' on all created resources" do
@@ -313,6 +313,31 @@ describe Puppet::Transaction do
end
end
+ describe "when performing pre-run checks" do
+ let(:resource) { Puppet::Type.type(:notify).new(:title => "spec") }
+ let(:transaction) { transaction_with_resource(resource) }
+ let(:spec_exception) { 'spec-exception' }
+
+ it "should invoke each resource's hook and apply the catalog after no failures" do
+ resource.expects(:pre_run_check)
+
+ transaction.evaluate
+ end
+
+ it "should abort the transaction on failure" do
+ resource.expects(:pre_run_check).raises(Puppet::Error, spec_exception)
+
+ expect { transaction.evaluate }.to raise_error(Puppet::Error, /Some pre-run checks failed/)
+ end
+
+ it "should log the resource-specific exception" do
+ resource.expects(:pre_run_check).raises(Puppet::Error, spec_exception)
+ resource.expects(:log_exception).with(responds_with(:message, spec_exception))
+
+ expect { transaction.evaluate }.to raise_error(Puppet::Error)
+ end
+ end
+
describe "when skipping a resource" do
before :each do
@resource = Puppet::Type.type(:notify).new :name => "foo"
@@ -478,44 +503,70 @@ describe Puppet::Transaction do
end
describe "during teardown" do
+ let(:catalog) { Puppet::Resource::Catalog.new }
+ let(:transaction) do
+ Puppet::Transaction.new(catalog, nil, Puppet::Graph::RandomPrioritizer.new)
+ end
+
+ let(:teardown_type) do
+ Puppet::Type.newtype(:teardown_test) do
+ newparam(:name) {}
+ end
+ end
+
before :each do
- @catalog = Puppet::Resource::Catalog.new
- @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::RandomPrioritizer.new)
+ teardown_type.provide(:teardown_provider) do
+ class << self
+ attr_reader :result
+
+ def post_resource_eval
+ @result = 'passed'
+ end
+ end
+ end
end
it "should call ::post_resource_eval on provider classes that support it" do
- @resource = Puppet::Type.type(:notify).new :title => "foo"
- @catalog.add_resource @resource
+ resource = teardown_type.new(:title => "foo", :provider => :teardown_provider)
- # 'expects' will cause 'respond_to?(:post_resource_eval)' to return true
- @resource.provider.class.expects(:post_resource_eval)
- @transaction.evaluate
+ transaction = transaction_with_resource(resource)
+ transaction.evaluate
+
+ expect(resource.provider.class.result).to eq('passed')
end
it "should call ::post_resource_eval even if other providers' ::post_resource_eval fails" do
- @resource3 = Puppet::Type.type(:user).new :title => "bloo"
- @resource3.provider.class.stubs(:post_resource_eval).raises
- @resource4 = Puppet::Type.type(:notify).new :title => "blob"
- @resource4.provider.class.stubs(:post_resource_eval).raises
- @catalog.add_resource @resource3
- @catalog.add_resource @resource4
-
- # ruby's Set does not guarantee ordering, so both resource3 and resource4
- # need to expect post_resource_eval, rather than just the 'first' one.
- @resource3.provider.class.expects(:post_resource_eval)
- @resource4.provider.class.expects(:post_resource_eval)
+ teardown_type.provide(:always_fails) do
+ class << self
+ attr_reader :result
+
+ def post_resource_eval
+ @result = 'failed'
+ raise Puppet::Error, "This provider always fails"
+ end
+ end
+ end
- @transaction.evaluate
+ good_resource = teardown_type.new(:title => "bloo", :provider => :teardown_provider)
+ bad_resource = teardown_type.new(:title => "blob", :provider => :always_fails)
+
+ catalog.add_resource(bad_resource)
+ catalog.add_resource(good_resource)
+
+ transaction.evaluate
+
+ expect(good_resource.provider.class.result).to eq('passed')
+ expect(bad_resource.provider.class.result).to eq('failed')
end
it "should call ::post_resource_eval even if one of the resources fails" do
- @resource3 = Puppet::Type.type(:notify).new :title => "bloo"
- @resource3.stubs(:retrieve_resource).raises
- @catalog.add_resource @resource3
+ resource = teardown_type.new(:title => "foo", :provider => :teardown_provider)
+ resource.stubs(:retrieve_resource).raises
+ catalog.add_resource resource
- @resource3.provider.class.expects(:post_resource_eval)
+ resource.provider.class.expects(:post_resource_eval)
- @transaction.evaluate
+ transaction.evaluate
end
end
diff --git a/spec/unit/type/cron_spec.rb b/spec/unit/type/cron_spec.rb
index b4a853173..82f646290 100755
--- a/spec/unit/type/cron_spec.rb
+++ b/spec/unit/type/cron_spec.rb
@@ -452,7 +452,7 @@ describe Puppet::Type.type(:cron), :unless => Puppet.features.microsoft_windows?
describe "special" do
%w(reboot yearly annually monthly weekly daily midnight hourly).each do |value|
it "should support the value '#{value}'" do
- expect { described_class.new(:name => 'foo', :special => value ) }.to_not raise_error(Puppet::Error, /cannot specify both a special schedule and a value/)
+ expect { described_class.new(:name => 'foo', :special => value ) }.to_not raise_error
end
end
@@ -462,7 +462,7 @@ describe Puppet::Type.type(:cron), :unless => Puppet.features.microsoft_windows?
it "should accept the value '#{value}' for special" do
expect {
described_class.new(:name => 'foo', :minute => :absent, :special => value )
- }.to_not raise_error(Puppet::Error, /cannot specify both a special schedule and a value/)
+ }.to_not raise_error
end
}
end
@@ -477,7 +477,7 @@ describe Puppet::Type.type(:cron), :unless => Puppet.features.microsoft_windows?
it "should accept the 'absent' value for special" do
expect {
described_class.new(:name => 'foo', :minute => "1", :special => :absent )
- }.to_not raise_error(Puppet::Error, /cannot specify both a special schedule and a value/)
+ }.to_not raise_error
end
end
end
diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb
index 6d780b3ff..ec9847e91 100755
--- a/spec/unit/type/exec_spec.rb
+++ b/spec/unit/type/exec_spec.rb
@@ -204,6 +204,15 @@ describe Puppet::Type.type(:exec) do
}.to raise_error Puppet::Error, /Parameter user failed/
end
+ it "accepts the current user" do
+ Puppet.features.stubs(:root?).returns(false)
+ Etc.stubs(:getpwuid).returns(Struct::Passwd.new('input'))
+
+ type = Puppet::Type.type(:exec).new(:name => '/bin/true whatever', :user => 'input')
+
+ expect(type[:user]).to eq('input')
+ 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)
diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb
index 33e995416..bf438d680 100755
--- a/spec/unit/type/file/content_spec.rb
+++ b/spec/unit/type/file/content_spec.rb
@@ -332,7 +332,7 @@ describe Puppet::Type.type(:file).attrclass(:content), :uses_checksums => true d
end
describe "from local source" do
- let(:source_content) { "source file content\r\n"*10000 }
+ let(:source_content) { "source file content\r\n"*10 }
before(:each) do
sourcename = tmpfile('source')
resource[:backup] = false
@@ -362,104 +362,87 @@ describe Puppet::Type.type(:file).attrclass(:content), :uses_checksums => true d
end
end
- describe "from an explicit fileserver" do
- let(:source_content) { "source file content\n"*10000 }
- let(:response) { stub_everything 'response' }
+ describe 'from remote source' do
+ let(:source_content) { "source file content\n"*10 }
let(:source) { resource.newattr(:source) }
+ let(:response) { stub_everything('response') }
+ let(:conn) { mock('connection') }
before(:each) do
resource[:backup] = false
- response.stubs(:read_body).multiple_yields(*(["source file content\n"]*10000))
-
- conn = mock('connection')
- conn.stubs(:request_get).yields response
-
- Puppet::Network::HttpPool.expects(:http_instance).with('somehostname',any_parameters).returns(conn).at_least_once
-
# This needs to be invoked to properly initialize the content property,
# or attempting to write a file will fail.
resource.newattr(:content)
- source.stubs(:metadata).returns stub_everything('metadata', :source => "puppet://somehostname/test/foo", :ftype => 'file')
+ response.stubs(:read_body).multiple_yields(*source_content.lines)
+ conn.stubs(:request_get).yields(response)
end
- describe "and the request was successful" do
- before { response.stubs(:code).returns '200' }
-
- it "should write the contents to the file" do
- resource.write(source)
- Puppet::FileSystem.binread(filename).should == source_content
- end
+ it 'should use an explicit fileserver if source starts with puppet://' do
+ response.stubs(:code).returns('200')
+ source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet://somehostname/test/foo', :ftype => 'file')
+ Puppet::Network::HttpPool.expects(:http_instance).with('somehostname', anything).returns(conn)
- with_digest_algorithms do
- it "should return the checksum computed" do
- File.open(filename, 'w') do |file|
- resource[:checksum] = digest_algorithm
- content.write(file).should == "{#{digest_algorithm}}#{digest(source_content)}"
- end
- end
- end
+ resource.write(source)
end
- it "should not write anything if source is not found" do
- response.stubs(:code).returns("404")
- expect { resource.write(source) }.to raise_error(Net::HTTPError, /404/)
- File.read(filename).should == "initial file content"
- end
+ it 'should use the default fileserver if source starts with puppet:///' do
+ response.stubs(:code).returns('200')
+ source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo', :ftype => 'file')
+ Puppet::Network::HttpPool.expects(:http_instance).with(Puppet.settings[:server], anything).returns(conn)
- it "should raise an HTTP error in case of server error" do
- response.stubs(:code).returns("500")
- expect { content.write(fh) }.to raise_error(Net::HTTPError, /500/)
+ resource.write(source)
end
- end
+ it 'should percent encode reserved characters' do
+ response.stubs(:code).returns('200')
+ Puppet::Network::HttpPool.stubs(:http_instance).returns(conn)
+ source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo bar', :ftype => 'file')
- describe "from remote source" do
- let(:source_content) { "source file content\n"*10000 }
- let(:response) { stub_everything 'response' }
- let(:source) { resource.newattr(:source) }
+ conn.unstub(:request_get)
+ conn.expects(:request_get).with('/none/file_content/test/foo%20bar', anything).yields(response)
- before(:each) do
- resource[:backup] = false
- response.stubs(:read_body).multiple_yields(*(["source file content\n"]*10000))
+ resource.write(source)
+ end
- conn = stub_everything 'connection'
- conn.stubs(:request_get).yields response
- Puppet::Network::HttpPool.stubs(:http_instance).returns conn
+ describe 'when handling file_content responses' do
+ before(:each) do
+ Puppet::Network::HttpPool.stubs(:http_instance).returns(conn)
+ source.stubs(:metadata).returns stub_everything('metadata', :source => 'puppet:///test/foo', :ftype => 'file')
+ end
- # This needs to be invoked to properly initialize the content property,
- # or attempting to write a file will fail.
- resource.newattr(:content)
- source.stubs(:metadata).returns stub_everything('metadata', :source => "puppet://somehostname/test/foo", :ftype => 'file')
- end
+ it 'should not write anything if source is not found' do
+ response.stubs(:code).returns('404')
+
+ expect { resource.write(source) }.to raise_error(Net::HTTPError, /404/)
+ expect(File.read(filename)).to eq('initial file content')
+ end
- describe "and the request was successful" do
- before { response.stubs(:code).returns '200' }
+ it 'should raise an HTTP error in case of server error' do
+ response.stubs(:code).returns('500')
- it "should write the contents to the file" do
- resource.write(source)
- Puppet::FileSystem.binread(filename).should == source_content
+ expect { resource.write(source) }.to raise_error(Net::HTTPError, /500/)
end
- with_digest_algorithms do
- it "should return the checksum computed" do
- File.open(filename, 'w') do |file|
- resource[:checksum] = digest_algorithm
- content.write(file).should == "{#{digest_algorithm}}#{digest(source_content)}"
+ context 'and the request was successful' do
+ before(:each) { response.stubs(:code).returns '200' }
+
+ it 'should write the contents to the file' do
+ resource.write(source)
+ expect(Puppet::FileSystem.binread(filename)).to eq(source_content)
+ end
+
+ with_digest_algorithms do
+ it 'should return the checksum computed' do
+ File.open(filename, 'w') do |file|
+ resource[:checksum] = digest_algorithm
+ expect(content.write(file)).to eq("{#{digest_algorithm}}#{digest(source_content)}")
+ end
end
end
- end
- end
- it "should not write anything if source is not found" do
- response.stubs(:code).returns("404")
- expect {resource.write(source)}.to raise_error(Net::HTTPError, /404/)
- File.read(filename).should == "initial file content"
- end
+ end
- it "should raise an HTTP error in case of server error" do
- response.stubs(:code).returns("500")
- expect { content.write(fh) }.to raise_error(Net::HTTPError, /500/)
end
end
diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb
index 9936ebdbc..82bd5a09f 100755
--- a/spec/unit/type/file/mode_spec.rb
+++ b/spec/unit/type/file/mode_spec.rb
@@ -6,7 +6,7 @@ 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(:resource) { Puppet::Type.type(:file).new :path => path, :mode => '0644' }
let(:mode) { resource.property(:mode) }
describe "#validate" do
@@ -192,4 +192,29 @@ describe Puppet::Type.type(:file).attrclass(:mode) do
(stat.mode & 0777).to_s(8).should == "644"
end
end
+
+ describe '#sync with a symbolic mode of +X for a file' do
+ let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'g+wX' }
+ let(:mode_sym) { resource_sym.property(:mode) }
+
+ before { FileUtils.touch(path) }
+
+ it 'does not change executable bit if no executable bit is set' do
+ Puppet::FileSystem.chmod(0644, path)
+
+ mode_sym.sync
+
+ stat = Puppet::FileSystem.stat(path)
+ (stat.mode & 0777).to_s(8).should == '664'
+ end
+
+ it 'does change executable bit if an executable bit is set' do
+ Puppet::FileSystem.chmod(0744, path)
+
+ mode_sym.sync
+
+ stat = Puppet::FileSystem.stat(path)
+ (stat.mode & 0777).to_s(8).should == '774'
+ end
+ end
end
diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb
index b6e97cd7a..ff192a5f4 100755
--- a/spec/unit/type/file/source_spec.rb
+++ b/spec/unit/type/file/source_spec.rb
@@ -176,7 +176,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do
end
describe "when copying the source values" do
- before do
+ before :each do
@resource = Puppet::Type.type(:file).new :path => @foobar
@source = source.new(:resource => @resource)
@@ -186,6 +186,28 @@ describe Puppet::Type.type(:file).attrclass(:source) do
Puppet.features.stubs(:root?).returns true
end
+ it "should not issue a deprecation warning if the source mode value is a Numeric" do
+ @metadata.stubs(:mode).returns 0173
+ if Puppet::Util::Platform.windows?
+ Puppet.expects(:deprecation_warning).with(regexp_matches(/Copying owner\/mode\/group from the source file on Windows is deprecated/)).at_least_once
+ else
+ Puppet.expects(:deprecation_warning).never
+ end
+
+ @source.copy_source_values
+ end
+
+ it "should not issue a deprecation warning if the source mode value is a String" do
+ @metadata.stubs(:mode).returns "173"
+ if Puppet::Util::Platform.windows?
+ Puppet.expects(:deprecation_warning).with(regexp_matches(/Copying owner\/mode\/group from the source file on Windows is deprecated/)).at_least_once
+ else
+ Puppet.expects(:deprecation_warning).never
+ end
+
+ @source.copy_source_values
+ end
+
it "should fail if there is no metadata" do
@source.stubs(:metadata).returns nil
@source.expects(:devfail).raises ArgumentError
@@ -409,7 +431,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do
@source.stubs(:local?).returns false
Puppet.expects(:deprecation_warning).with(deprecation_message).at_least_once
@resource[:group] = 2
- @resource[:mode] = 3
+ @resource[:mode] = "0003"
@source.copy_source_values
end
@@ -418,7 +440,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do
@source.stubs(:local?).returns false
Puppet.expects(:deprecation_warning).with(deprecation_message).at_least_once
@resource[:owner] = 1
- @resource[:mode] = 3
+ @resource[:mode] = "0003"
@source.copy_source_values
end
@@ -437,7 +459,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do
Puppet.expects(:deprecation_warning).with(deprecation_message).never
@resource[:owner] = 1
@resource[:group] = 2
- @resource[:mode] = 3
+ @resource[:mode] = "0003"
@source.copy_source_values
end
diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb
index 00b027ed7..7a4d02553 100755
--- a/spec/unit/type/file_spec.rb
+++ b/spec/unit/type/file_spec.rb
@@ -418,7 +418,7 @@ describe Puppet::Type.type(:file) do
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
+ 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
@@ -1357,13 +1357,13 @@ describe Puppet::Type.type(:file) do
target = described_class.new(
:ensure => :file, :path => @target,
:catalog => catalog, :content => 'yayness',
- :mode => 0644)
+ :mode => '0644')
catalog.add_resource target
@link_resource = described_class.new(
:ensure => :link, :path => @link,
:target => @target, :catalog => catalog,
- :mode => 0755)
+ :mode => '0755')
catalog.add_resource @link_resource
# to prevent the catalog from trying to write state.yaml
diff --git a/spec/unit/type/nagios_spec.rb b/spec/unit/type/nagios_spec.rb
index bc96c26d4..4bd1271c2 100755
--- a/spec/unit/type/nagios_spec.rb
+++ b/spec/unit/type/nagios_spec.rb
@@ -125,7 +125,7 @@ EOL
parser = Nagios::Parser.new
expect {
results = parser.parse(ESCAPED_SEMICOLON)
- }.to_not raise_error Nagios::Parser::SyntaxError
+ }.to_not raise_error
end
it "should ignore it if it is a comment" do
@@ -147,7 +147,7 @@ EOL
parser = Nagios::Parser.new
expect {
results = parser.parse(POUND_SIGN_HASH_SYMBOL_NOT_IN_FIRST_COLUMN)
- }.to_not raise_error Nagios::Parser::SyntaxError
+ }.to_not raise_error
end
@@ -170,7 +170,7 @@ EOL
parser = Nagios::Parser.new
expect {
results = parser.parse(ANOTHER_ESCAPED_SEMICOLON)
- }.to_not raise_error Nagios::Parser::SyntaxError
+ }.to_not raise_error
end
it "should parse correctly" do
@@ -217,6 +217,15 @@ describe "Nagios generator" do
results = parser.parse(nagios_type.to_s)
results[0].command_line.should eql(param)
end
+
+ it "should accept FixNum params and convert to string" do
+ param = 1
+ nagios_type = Nagios::Base.create(:serviceescalation)
+ nagios_type.first_notification = param
+ parser = Nagios::Parser.new
+ results = parser.parse(nagios_type.to_s)
+ results[0].first_notification.should eql(param.to_s)
+ end
end
describe "Nagios resource types" do
diff --git a/spec/unit/type/resources_spec.rb b/spec/unit/type/resources_spec.rb
index f08afd7ae..e985b9752 100755
--- a/spec/unit/type/resources_spec.rb
+++ b/spec/unit/type/resources_spec.rb
@@ -5,6 +5,11 @@ resources = Puppet::Type.type(:resources)
# There are still plenty of tests to port over from test/.
describe resources do
+
+ before :each do
+ described_class.reset_system_users_max_uid!
+ end
+
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
@@ -47,7 +52,7 @@ describe resources do
it "can be set to true for a resource type that has instances and can accept ensure" do
instance.resource_type.stubs(:respond_to?).returns true
instance.resource_type.stubs(:validproperty?).returns true
- expect { instance[:purge] = 'yes' }.not_to raise_error Puppet::Error
+ expect { instance[:purge] = 'yes' }.to_not raise_error
end
end
@@ -56,6 +61,7 @@ describe resources do
before do
@res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true
@res.catalog = Puppet::Resource::Catalog.new
+ Puppet::FileSystem.stubs(:exist?).with('/etc/login.defs').returns false
end
it "should never purge hardcoded system users" do
@@ -71,60 +77,89 @@ describe resources do
@res.user_check(user).should be_false
end
- it "should purge manual users if unless_system_user => true" do
- user_hash = {:name => 'system_user', :uid => 525, :system => true}
+ it "should purge non-system users if unless_system_user => true" do
+ user_hash = {:name => 'system_user', :uid => described_class.system_users_max_uid + 1, :system => true}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
@res.user_check(user).should be_true
end
- it "should purge system users over 500 if unless_system_user => 600" do
+ it "should not purge system users under 600 if unless_system_user => 600" do
res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => 600
res.catalog = Puppet::Resource::Catalog.new
- user_hash = {:name => 'system_user', :uid => 525, :system => true}
+ user_hash = {:name => 'system_user', :uid => 500, :system => true}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
res.user_check(user).should be_false
end
end
- describe "with unless_uid" do
- describe "with a uid range" do
- before do
- @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => 10_000..20_000
+ %w(FreeBSD OpenBSD).each do |os|
+ describe "on #{os}" do
+ before :each do
+ Facter.stubs(:value).with(:kernel).returns(os)
+ Facter.stubs(:value).with(:operatingsystem).returns(os)
+ Facter.stubs(:value).with(:osfamily).returns(os)
+ Puppet::FileSystem.stubs(:exist?).with('/etc/login.defs').returns false
+ @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true
@res.catalog = Puppet::Resource::Catalog.new
end
- it "should purge uids that are not in a specified range" do
- user_hash = {:name => 'special_user', :uid => 25_000}
+ it "should not purge system users under 1000" do
+ user_hash = {:name => 'system_user', :uid => 999}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
- @res.user_check(user).should be_true
+ @res.user_check(user).should be_false
end
- it "should not purge uids that are in a specified range" do
- user_hash = {:name => 'special_user', :uid => 15_000}
+ it "should purge users over 999" do
+ user_hash = {:name => 'system_user', :uid => 1000}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
- @res.user_check(user).should be_false
+ @res.user_check(user).should be_true
end
end
+ end
+
+ describe 'with login.defs present' do
+ before :each do
+ Puppet::FileSystem.expects(:exist?).with('/etc/login.defs').returns true
+ Puppet::FileSystem.expects(:each_line).with('/etc/login.defs').yields(' UID_MIN 1234 # UID_MIN comment ')
+ @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_system_user => true
+ @res.catalog = Puppet::Resource::Catalog.new
+ end
+
+ it 'should not purge a system user' do
+ user_hash = {:name => 'system_user', :uid => 1233}
+ user = Puppet::Type.type(:user).new(user_hash)
+ user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
+ @res.user_check(user).should be_false
+ end
+
+ it 'should purge a non-system user' do
+ user_hash = {:name => 'system_user', :uid => 1234}
+ user = Puppet::Type.type(:user).new(user_hash)
+ user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
+ @res.user_check(user).should be_true
+ end
+ end
- describe "with a uid range array" do
+ describe "with unless_uid" do
+ describe "with a uid array" do
before do
- @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [10_000..15_000, 15_000..20_000]
+ @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [15_000, 15_001, 15_002]
@res.catalog = Puppet::Resource::Catalog.new
end
- it "should purge uids that are not in a specified range array" do
+ it "should purge uids that are not in a specified array" do
user_hash = {:name => 'special_user', :uid => 25_000}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
@res.user_check(user).should be_true
end
- it "should not purge uids that are in a specified range array" do
- user_hash = {:name => 'special_user', :uid => 15_000}
+ it "should not purge uids that are in a specified array" do
+ user_hash = {:name => 'special_user', :uid => 15000}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
@res.user_check(user).should be_false
@@ -132,31 +167,30 @@ describe resources do
end
- describe "with a uid array" do
+ describe "with a single integer uid" do
before do
- @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [15_000, 15_001, 15_002]
+ @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => 15_000
@res.catalog = Puppet::Resource::Catalog.new
end
- it "should purge uids that are not in a specified array" do
+ it "should purge uids that are not specified" do
user_hash = {:name => 'special_user', :uid => 25_000}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
@res.user_check(user).should be_true
end
- it "should not purge uids that are in a specified array" do
- user_hash = {:name => 'special_user', :uid => 15000}
+ it "should not purge uids that are specified" do
+ user_hash = {:name => 'special_user', :uid => 15_000}
user = Puppet::Type.type(:user).new(user_hash)
user.stubs(:retrieve_resource).returns Puppet::Resource.new("user", user_hash[:name], :parameters => user_hash)
@res.user_check(user).should be_false
end
-
end
- describe "with a single uid" do
+ describe "with a single string uid" do
before do
- @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => 15_000
+ @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => '15000'
@res.catalog = Puppet::Resource::Catalog.new
end
@@ -177,7 +211,7 @@ describe resources do
describe "with a mixed uid array" do
before do
- @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => [10_000..15_000, 16_666]
+ @res = Puppet::Type.type(:resources).new :name => :user, :purge => true, :unless_uid => ['15000', 16_666]
@res.catalog = Puppet::Resource::Catalog.new
end
@@ -202,7 +236,7 @@ describe resources do
@res.user_check(user).should be_true
end
end
-
+
end
end
diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb
index d9468357b..f5a351752 100755
--- a/spec/unit/type/user_spec.rb
+++ b/spec/unit/type/user_spec.rb
@@ -467,40 +467,47 @@ describe Puppet::Type.type(:user) do
res.catalog = Puppet::Resource::Catalog.new
res
end
- it "should not just return from eval_generate" do
+ it "should not just return from generate" do
subject.expects :find_unmanaged_keys
- subject.eval_generate
+ subject.generate
end
it "should check each keyfile for readability" do
paths.each do |path|
File.expects(:readable?).with(path)
end
- subject.eval_generate
+ subject.generate
end
end
describe "generated keys" do
subject do
- res = described_class.new(:name => "test", :purge_ssh_keys => purge_param)
+ res = described_class.new(:name => "test_user_name", :purge_ssh_keys => purge_param)
res.catalog = Puppet::Resource::Catalog.new
res
end
context "when purging is disabled" do
let(:purge_param) { false }
- its(:eval_generate) { should be_empty }
+ its(:generate) { should be_empty }
end
context "when purging is enabled" do
let(:purge_param) { my_fixture('authorized_keys') }
- let(:resources) { subject.eval_generate }
+ let(:resources) { subject.generate }
it "should contain a resource for each key" do
names = resources.collect { |res| res.name }
- names.should include("keyname1")
+ names.should include("key1 name")
names.should include("keyname2")
end
it "should not include keys in comment lines" do
names = resources.collect { |res| res.name }
names.should_not include("keyname3")
end
+ it "should each have a value for the user property" do
+ resources.map { |res|
+ res[:user]
+ }.reject { |user_name|
+ user_name == "test_user_name"
+ }.should be_empty
+ end
end
end
end
diff --git a/spec/unit/type/yumrepo_spec.rb b/spec/unit/type/yumrepo_spec.rb
index b97c60666..2246b7274 100644..100755
--- a/spec/unit/type/yumrepo_spec.rb
+++ b/spec/unit/type/yumrepo_spec.rb
@@ -10,6 +10,37 @@ shared_examples_for "a yumrepo parameter that can be absent" do |param|
end
end
+shared_examples_for "a yumrepo parameter that expects a natural value" do |param|
+ it "accepts a valid positive integer" do
+ instance = described_class.new(:name => 'puppetlabs', param => '12')
+ expect(instance[param]).to eq '12'
+ end
+ it "rejects invalid negative integer" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ param => '-12'
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)
+ end
+ it "rejects invalid non-integer" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ param => 'I\'m a six'
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)
+ end
+ it "rejects invalid string with integers inside" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ param => 'I\'m a 6'
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)
+ end
+end
+
shared_examples_for "a yumrepo parameter that expects a boolean parameter" do |param|
valid_values = %w[True False 0 1 No Yes]
@@ -22,6 +53,14 @@ shared_examples_for "a yumrepo parameter that expects a boolean parameter" do |p
instance = described_class.new(:name => 'puppetlabs', param => value.downcase)
expect(instance[param]).to eq value.downcase
end
+ it "fails on valid value #{value} contained in another value" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ param => "bla#{value}bla"
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)
+ end
end
it "rejects invalid boolean values" do
@@ -76,6 +115,26 @@ shared_examples_for "a yumrepo parameter that accepts multiple URLs" do |param|
end
end
+shared_examples_for "a yumrepo parameter that accepts kMG units" do |param|
+ %w[k M G].each do |unit|
+ it "can accept an integer with #{unit} units" do
+ described_class.new(
+ :name => 'puppetlabs',
+ param => "123#{unit}"
+ )
+ end
+ end
+
+ it "fails if wrong unit passed" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ param => '123J'
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter #{param} failed/)
+ end
+end
+
describe Puppet::Type.type(:yumrepo) do
it "has :name as its namevar" do
expect(described_class.key_attributes).to eq [:name]
@@ -154,6 +213,14 @@ describe Puppet::Type.type(:yumrepo) do
it "accepts a value of #{value}" do
described_class.new(:name => "puppetlabs", :failovermethod => value)
end
+ it "fails on valid value #{value} contained in another value" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ :failovermethod => "bla#{value}bla"
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter failovermethod failed/)
+ end
end
it "raises an error if an invalid value is given" do
@@ -175,6 +242,14 @@ describe Puppet::Type.type(:yumrepo) do
it "accepts a valid value of #{value}" do
described_class.new(:name => 'puppetlabs', :http_caching => value)
end
+ it "fails on valid value #{value} contained in another value" do
+ expect {
+ described_class.new(
+ :name => 'puppetlabs',
+ :http_caching => "bla#{value}bla"
+ )
+ }.to raise_error(Puppet::ResourceError, /Parameter http_caching failed/)
+ end
end
it "rejects invalid values" do
@@ -188,10 +263,25 @@ describe Puppet::Type.type(:yumrepo) do
describe "timeout" do
it_behaves_like "a yumrepo parameter that can be absent", :timeout
+ it_behaves_like "a yumrepo parameter that expects a natural value", :timeout
end
describe "metadata_expire" do
it_behaves_like "a yumrepo parameter that can be absent", :metadata_expire
+ it_behaves_like "a yumrepo parameter that expects a natural value", :metadata_expire
+
+ it "accepts dhm units" do
+ %W[d h m].each do |unit|
+ described_class.new(
+ :name => 'puppetlabs',
+ :metadata_expire => "123#{unit}"
+ )
+ end
+ end
+
+ it "accepts never as value" do
+ described_class.new(:name => 'puppetlabs', :metadata_expire => 'never')
+ end
end
describe "protect" do
@@ -205,6 +295,12 @@ describe Puppet::Type.type(:yumrepo) do
describe "proxy" do
it_behaves_like "a yumrepo parameter that can be absent", :proxy
+ it "accepts _none_" do
+ described_class.new(
+ :name => 'puppetlabs',
+ :proxy => "_none_"
+ )
+ end
it_behaves_like "a yumrepo parameter that accepts a single URL", :proxy
end
@@ -247,5 +343,45 @@ describe Puppet::Type.type(:yumrepo) do
it_behaves_like "a yumrepo parameter that can be absent", :metalink
it_behaves_like "a yumrepo parameter that accepts a single URL", :metalink
end
+
+
+ describe "cost" do
+ it_behaves_like "a yumrepo parameter that can be absent", :cost
+ it_behaves_like "a yumrepo parameter that expects a natural value", :cost
+ end
+
+ describe "throttle" do
+ it_behaves_like "a yumrepo parameter that can be absent", :throttle
+ it_behaves_like "a yumrepo parameter that expects a natural value", :throttle
+ it_behaves_like "a yumrepo parameter that accepts kMG units", :throttle
+
+ it "accepts percentage as unit" do
+ described_class.new(
+ :name => 'puppetlabs',
+ :throttle => '123%'
+ )
+ end
+ end
+
+ describe "bandwidth" do
+ it_behaves_like "a yumrepo parameter that can be absent", :bandwidth
+ it_behaves_like "a yumrepo parameter that expects a natural value", :bandwidth
+ it_behaves_like "a yumrepo parameter that accepts kMG units", :bandwidth
+ end
+
+ describe "gpgcakey" do
+ it_behaves_like "a yumrepo parameter that can be absent", :gpgcakey
+ it_behaves_like "a yumrepo parameter that accepts a single URL", :gpgcakey
+ end
+
+ describe "retries" do
+ it_behaves_like "a yumrepo parameter that can be absent", :retries
+ it_behaves_like "a yumrepo parameter that expects a natural value", :retries
+ end
+
+ describe "mirrorlist_expire" do
+ it_behaves_like "a yumrepo parameter that can be absent", :mirrorlist_expire
+ it_behaves_like "a yumrepo parameter that expects a natural value", :mirrorlist_expire
+ end
end
end
diff --git a/spec/unit/type/zone_spec.rb b/spec/unit/type/zone_spec.rb
index e54902627..3497ae3a7 100755
--- a/spec/unit/type/zone_spec.rb
+++ b/spec/unit/type/zone_spec.rb
@@ -2,8 +2,11 @@
require 'spec_helper'
describe Puppet::Type.type(:zone) do
- let(:zone) { described_class.new(:name => 'dummy', :path => '/dummy', :provider => :solaris) }
+ let(:zone) { described_class.new(:name => 'dummy', :path => '/dummy', :provider => :solaris, :ip=>'if:1.2.3.4:2.3.4.5', :inherit=>'/', :dataset=>'tank') }
let(:provider) { zone.provider }
+ let(:ip) { zone.property(:ip) }
+ let(:inherit) { zone.property(:inherit) }
+ let(:dataset) { zone.property(:dataset) }
parameters = [:create_args, :install_args, :sysidcfg, :realhostname]
@@ -21,6 +24,46 @@ describe Puppet::Type.type(:zone) do
end
end
+ describe "when trying to set a property that is empty" do
+ it "should verify that property.insync? of nil or :absent is true" do
+ [inherit, ip, dataset].each do |prop|
+ prop.stubs(:should).returns []
+ end
+ [inherit, ip, dataset].each do |prop|
+ prop.insync?(nil).should be_true
+ end
+ [inherit, ip, dataset].each do |prop|
+ prop.insync?(:absent).should be_true
+ end
+ end
+ end
+ describe "when trying to set a property that is non empty" do
+ it "should verify that property.insync? of nil or :absent is false" do
+ [inherit, ip, dataset].each do |prop|
+ prop.stubs(:should).returns ['a','b']
+ end
+ [inherit, ip, dataset].each do |prop|
+ prop.insync?(nil).should be_false
+ end
+ [inherit, ip, dataset].each do |prop|
+ prop.insync?(:absent).should be_false
+ end
+ end
+ end
+ describe "when trying to set a property that is non empty" do
+ it "insync? should return true or false depending on the current value, and new value" do
+ [inherit, ip, dataset].each do |prop|
+ prop.stubs(:should).returns ['a','b']
+ end
+ [inherit, ip, dataset].each do |prop|
+ prop.insync?(['b', 'a']).should be_true
+ end
+ [inherit, ip, dataset].each do |prop|
+ prop.insync?(['a']).should be_false
+ end
+ end
+ end
+
it "should be valid when only :path is given" do
described_class.new(:name => "dummy", :path => '/dummy', :provider => :solaris)
end
diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb
index 75a5a0768..8c250ee34 100755
--- a/spec/unit/type_spec.rb
+++ b/spec/unit/type_spec.rb
@@ -328,6 +328,26 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
provider.ancestors.should include(Puppet::Provider)
provider.should == @type.provider(:test_provider)
end
+
+ describe "with a parent class from another type" do
+ before :each do
+ @parent_type = Puppet::Type.newtype(:provider_parent_type) do
+ newparam(:name) { isnamevar }
+ end
+ @parent_provider = @parent_type.provide(:parent_provider)
+ end
+
+ it "should be created successfully" do
+ child_provider = @type.provide(:child_provider, :parent => @parent_provider)
+ child_provider.ancestors.should include(@parent_provider)
+ end
+
+ it "should be registered as a provider of the child type" do
+ child_provider = @type.provide(:child_provider, :parent => @parent_provider)
+ @type.providers.should include(:child_provider)
+ @parent_type.providers.should_not include(:child_provider)
+ end
+ end
end
describe "when choosing a default provider" do
diff --git a/spec/unit/util/colors_spec.rb b/spec/unit/util/colors_spec.rb
index 7407b628b..b0f791f82 100755
--- a/spec/unit/util/colors_spec.rb
+++ b/spec/unit/util/colors_spec.rb
@@ -67,17 +67,23 @@ describe Puppet::Util::Colors do
end
end
- describe "on Windows", :if => Puppet.features.microsoft_windows? do
- it "expects a trailing embedded NULL character in the wide string" do
- message = "hello"
+ context "on Windows in Ruby 1.x", :if => Puppet.features.microsoft_windows? && RUBY_VERSION =~ /^1./ do
+ it "should define WideConsole" do
+ expect(defined?(Puppet::Util::Colors::WideConsole)).to be_true
+ end
- console = Puppet::Util::Colors::WideConsole.new
- wstr, nchars = console.string_encode(message)
+ it "should define WideIO" do
+ expect(defined?(Puppet::Util::Colors::WideIO)).to be_true
+ end
+ end
- expect(nchars).to eq(message.length)
+ context "on Windows in Ruby 2.x", :if => Puppet.features.microsoft_windows? && RUBY_VERSION =~ /^2./ do
+ it "should not define WideConsole" do
+ expect(defined?(Puppet::Util::Colors::WideConsole)).to be_false
+ end
- expect(wstr.length).to eq(nchars + 1)
- expect(wstr[-1].ord).to be_zero
+ it "should not define WideIO" do
+ expect(defined?(Puppet::Util::Colors::WideIO)).to be_false
end
end
end
diff --git a/spec/unit/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb
index 6ba8077c2..9eb61b077 100755
--- a/spec/unit/util/command_line_spec.rb
+++ b/spec/unit/util/command_line_spec.rb
@@ -70,7 +70,7 @@ describe Puppet::Util::CommandLine do
it "should print the version and exit if #{arg} is given" do
expect do
described_class.new("puppet", [arg]).execute
- end.to have_printed(/^#{Puppet.version}$/)
+ end.to have_printed(/^#{Regexp.escape(Puppet.version)}$/)
end
end
end
@@ -93,35 +93,39 @@ describe Puppet::Util::CommandLine do
end
describe "and an external implementation cannot be found" do
+ before :each do
+ Puppet::Util::CommandLine::UnknownSubcommand.any_instance.stubs(:console_has_color?).returns false
+ end
+
it "should abort and show the usage message" do
- commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'])
Puppet::Util.expects(:which).with('puppet-whatever').returns(nil)
+ commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'])
commandline.expects(:exec).never
expect {
commandline.execute
- }.to have_printed(/Unknown Puppet subcommand 'whatever'/)
+ }.to have_printed(/Unknown Puppet subcommand 'whatever'/).and_exit_with(1)
end
it "should abort and show the help message" do
- commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'])
Puppet::Util.expects(:which).with('puppet-whatever').returns(nil)
+ commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', 'argument'])
commandline.expects(:exec).never
expect {
commandline.execute
- }.to have_printed(/See 'puppet help' for help on available puppet subcommands/)
+ }.to have_printed(/See 'puppet help' for help on available puppet subcommands/).and_exit_with(1)
end
%w{--version -V}.each do |arg|
it "should abort and display #{arg} information" do
- commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', arg])
Puppet::Util.expects(:which).with('puppet-whatever').returns(nil)
+ commandline = Puppet::Util::CommandLine.new("puppet", ['whatever', arg])
commandline.expects(:exec).never
expect {
commandline.execute
- }.to have_printed(/^#{Puppet.version}$/)
+ }.to have_printed(%r[^#{Regexp.escape(Puppet.version)}$]).and_exit_with(1)
end
end
end
diff --git a/spec/unit/util/execution_spec.rb b/spec/unit/util/execution_spec.rb
index 7c6238f9f..6a4bee490 100755
--- a/spec/unit/util/execution_spec.rb
+++ b/spec/unit/util/execution_spec.rb
@@ -1,15 +1,9 @@
#! /usr/bin/env ruby
require 'spec_helper'
+require 'puppet/file_system/uniquefile'
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)
@@ -28,8 +22,8 @@ describe Puppet::Util::Execution do
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)
+ FFI::WIN32.stubs(:CloseHandle).with(process_handle)
+ FFI::WIN32.stubs(:CloseHandle).with(thread_handle)
else
Process.stubs(:waitpid2).with(pid).returns([pid, stub('child_status', :exitstatus => exitstatus)])
end
@@ -52,7 +46,7 @@ describe Puppet::Util::Execution do
$stderr.stubs(:reopen)
@stdin = File.open(null_file, 'r')
- @stdout = Tempfile.new('stdout')
+ @stdout = Puppet::FileSystem::Uniquefile.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
@@ -132,7 +126,7 @@ describe Puppet::Util::Execution do
stub_process_wait(0)
@stdin = File.open(null_file, 'r')
- @stdout = Tempfile.new('stdout')
+ @stdout = Puppet::FileSystem::Uniquefile.new('stdout')
@stderr = File.open(null_file, 'w')
end
@@ -223,8 +217,8 @@ describe Puppet::Util::Execution do
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)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,_|
stdout.path == outfile.path
@@ -234,8 +228,8 @@ describe Puppet::Util::Execution do
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)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr|
stdout.path == outfile.path and stderr.path == outfile.path
@@ -245,8 +239,8 @@ describe Puppet::Util::Execution do
end
it "should set stderr to the null device if combine is false" do
- outfile = Tempfile.new('stdout')
- Tempfile.stubs(:new).returns(outfile)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr|
stdout.path == outfile.path and stderr.path == null_file
@@ -256,8 +250,8 @@ describe Puppet::Util::Execution do
end
it "should combine stdout and stderr if combine is true" do
- outfile = Tempfile.new('stdout')
- Tempfile.stubs(:new).returns(outfile)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr|
stdout.path == outfile.path and stderr.path == outfile.path
@@ -267,8 +261,8 @@ describe Puppet::Util::Execution do
end
it "should default combine to true when no options are specified" do
- outfile = Tempfile.new('stdout')
- Tempfile.stubs(:new).returns(outfile)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr|
stdout.path == outfile.path and stderr.path == outfile.path
@@ -278,8 +272,8 @@ describe Puppet::Util::Execution do
end
it "should default combine to false when options are specified, but combine is not" do
- outfile = Tempfile.new('stdout')
- Tempfile.stubs(:new).returns(outfile)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr|
stdout.path == outfile.path and stderr.path == null_file
@@ -289,8 +283,8 @@ describe Puppet::Util::Execution do
end
it "should default combine to false when an empty hash of options is specified" do
- outfile = Tempfile.new('stdout')
- Tempfile.stubs(:new).returns(outfile)
+ outfile = Puppet::FileSystem::Uniquefile.new('stdout')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(outfile)
Puppet::Util::Execution.expects(executor).with do |_,_,_,stdout,stderr|
stdout.path == outfile.path and stderr.path == null_file
@@ -306,8 +300,8 @@ describe Puppet::Util::Execution do
Puppet::Util::Execution.stubs(:execute_windows).returns(proc_info_stub)
Puppet::Util::Windows::Process.expects(:wait_process).with(process_handle).raises('whatever')
- Puppet::Util::Windows::Process.expects(:CloseHandle).with(thread_handle)
- Puppet::Util::Windows::Process.expects(:CloseHandle).with(process_handle)
+ FFI::WIN32.expects(:CloseHandle).with(thread_handle)
+ FFI::WIN32.expects(:CloseHandle).with(process_handle)
expect { Puppet::Util::Execution.execute('test command') }.to raise_error(RuntimeError)
end
@@ -507,25 +501,25 @@ describe Puppet::Util::Execution do
end
it "should read and return the output if squelch is false" do
- stdout = Tempfile.new('test')
- Tempfile.stubs(:new).returns(stdout)
+ stdout = Puppet::FileSystem::Uniquefile.new('test')
+ Puppet::FileSystem::Uniquefile.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 = Puppet::FileSystem::Uniquefile.new('test')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(stdout)
stdout.write("My expected command output")
Puppet::Util::Execution.execute('test command', :squelch => true).should == ''
end
it "should delete the file used for output if squelch is false" do
- stdout = Tempfile.new('test')
+ stdout = Puppet::FileSystem::Uniquefile.new('test')
path = stdout.path
- Tempfile.stubs(:new).returns(stdout)
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(stdout)
Puppet::Util::Execution.execute('test command')
@@ -533,8 +527,8 @@ describe Puppet::Util::Execution do
end
it "should not raise an error if the file is open" do
- stdout = Tempfile.new('test')
- Tempfile.stubs(:new).returns(stdout)
+ stdout = Puppet::FileSystem::Uniquefile.new('test')
+ Puppet::FileSystem::Uniquefile.stubs(:new).returns(stdout)
file = File.new(stdout.path, 'r')
Puppet::Util.execute('test command')
@@ -597,40 +591,39 @@ describe Puppet::Util::Execution do
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.expects(:exitstatus).returns(0)
Puppet::Util::Execution.execpipe('echo hello').should == 'hello'
end
it "should print meaningful debug message for string argument" do
Puppet::Util::Execution.expects(:debug).with("Executing 'echo hello'")
Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello')
- $CHILD_STATUS.expects(:==).with(0).returns(true)
+ Puppet::Util::Execution.expects(:exitstatus).returns(0)
Puppet::Util::Execution.execpipe('echo hello')
end
it "should print meaningful debug message for array argument" do
Puppet::Util::Execution.expects(:debug).with("Executing 'echo hello'")
Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello')
- $CHILD_STATUS.expects(:==).with(0).returns(true)
+ Puppet::Util::Execution.expects(:exitstatus).returns(0)
Puppet::Util::Execution.execpipe(['echo','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.expects(:exitstatus).returns(0)
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)
+ Puppet::Util::Execution.stubs(:open).with('| echo hello 2>&1').returns('error message')
+ Puppet::Util::Execution.expects(:exitstatus).returns(1)
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)
Puppet::Util::Execution.execpipe('echo hello', false).should == 'error message'
end
end
diff --git a/spec/unit/util/feature_spec.rb b/spec/unit/util/feature_spec.rb
index aa8afbba6..e6d844533 100755
--- a/spec/unit/util/feature_spec.rb
+++ b/spec/unit/util/feature_spec.rb
@@ -91,4 +91,16 @@ describe Puppet::Util::Feature do
@features.should_not be_myfeature
@features.should be_myfeature
end
+
+ it "should cache load failures when configured to do so" do
+ Puppet[:always_cache_features] = true
+
+ @features.add(:myfeature, :libs => %w{foo bar})
+ @features.expects(:require).with("foo").raises(LoadError)
+
+ @features.should_not be_myfeature
+ # second call would cause an expectation exception if 'require' was
+ # called a second time
+ @features.should_not be_myfeature
+ end
end
diff --git a/spec/unit/util/http_proxy_spec.rb b/spec/unit/util/http_proxy_spec.rb
index bc6b4d2b7..59f39c511 100644
--- a/spec/unit/util/http_proxy_spec.rb
+++ b/spec/unit/util/http_proxy_spec.rb
@@ -4,7 +4,7 @@ require 'puppet/util/http_proxy'
describe Puppet::Util::HttpProxy do
- host, port = 'some.host', 1234
+ host, port, user, password = 'some.host', 1234, 'user1', 'pAssw0rd'
describe ".http_proxy_env" do
it "should return nil if no environment variables" do
@@ -80,4 +80,46 @@ describe Puppet::Util::HttpProxy do
end
+ describe ".http_proxy_user" do
+ it "should return a proxy user if set in environment" do
+ Puppet::Util.withenv('HTTP_PROXY' => "http://#{user}:#{password}@#{host}:#{port}") do
+ subject.http_proxy_user.should == user
+ end
+ end
+
+ it "should return a proxy user if set in config" do
+ Puppet.settings[:http_proxy_user] = user
+ subject.http_proxy_user.should == user
+ end
+
+ it "should use environment variable before puppet settings" do
+ Puppet::Util.withenv('HTTP_PROXY' => "http://#{user}:#{password}@#{host}:#{port}") do
+ Puppet.settings[:http_proxy_user] = 'clownpants'
+ subject.http_proxy_user.should == user
+ end
+ end
+
+ end
+
+ describe ".http_proxy_password" do
+ it "should return a proxy password if set in environment" do
+ Puppet::Util.withenv('HTTP_PROXY' => "http://#{user}:#{password}@#{host}:#{port}") do
+ subject.http_proxy_password.should == password
+ end
+ end
+
+ it "should return a proxy password if set in config" do
+ Puppet.settings[:http_proxy_user] = user
+ Puppet.settings[:http_proxy_password] = password
+ subject.http_proxy_password.should == password
+ end
+
+ it "should use environment variable before puppet settings" do
+ Puppet::Util.withenv('HTTP_PROXY' => "http://#{user}:#{password}@#{host}:#{port}") do
+ Puppet.settings[:http_proxy_password] = 'clownpants'
+ subject.http_proxy_password.should == password
+ end
+ end
+
+ end
end
diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb
index a91236dba..a81eac631 100755
--- a/spec/unit/util/log/destinations_spec.rb
+++ b/spec/unit/util/log/destinations_spec.rb
@@ -29,7 +29,7 @@ describe Puppet::Util::Log.desttypes[:file] do
before do
File.stubs(:open) # prevent actually creating the file
- File.stubs(:chown) # prevent chown on non existing file from failing
+ File.stubs(:chown) # prevent chown on non existing file from failing
@class = Puppet::Util::Log.desttypes[:file]
end
@@ -181,3 +181,47 @@ describe Puppet::Util::Log.desttypes[:console] do
end
end
end
+
+
+describe ":eventlog", :if => Puppet::Util::Platform.windows? do
+ before do
+ if Facter.value(:kernelmajversion).to_f < 6.0
+ pending("requires win32-eventlog gem upgrade to 0.6.2 on Windows 2003")
+ end
+ end
+
+ let(:klass) { Puppet::Util::Log.desttypes[:eventlog] }
+
+ def expects_message_with_type(klass, level, eventlog_type, eventlog_id)
+ eventlog = stub('eventlog')
+ eventlog.expects(:report_event).with(has_entries(:source => "Puppet", :event_type => eventlog_type, :event_id => eventlog_id, :data => "a hitchhiker: don't panic"))
+ Win32::EventLog.stubs(:open).returns(eventlog)
+
+ msg = Puppet::Util::Log.new(:level => level, :message => "don't panic", :source => "a hitchhiker")
+ dest = klass.new
+ dest.handle(msg)
+ end
+
+ it "supports the eventlog feature" do
+ expect(Puppet.features.eventlog?).to be_true
+ end
+
+ it "logs to the Application event log" do
+ eventlog = stub('eventlog')
+ Win32::EventLog.expects(:open).with('Application').returns(stub('eventlog'))
+
+ klass.new
+ end
+
+ it "logs :debug level as an information type event" do
+ expects_message_with_type(klass, :debug, klass::EVENTLOG_INFORMATION_TYPE, 0x1)
+ end
+
+ it "logs :warning level as an warning type event" do
+ expects_message_with_type(klass, :warning, klass::EVENTLOG_WARNING_TYPE, 0x2)
+ end
+
+ it "logs :err level as an error type event" do
+ expects_message_with_type(klass, :err, klass::EVENTLOG_ERROR_TYPE, 0x3)
+ end
+end
diff --git a/spec/unit/util/logging_spec.rb b/spec/unit/util/logging_spec.rb
index 0858f7857..abdae9189 100755
--- a/spec/unit/util/logging_spec.rb
+++ b/spec/unit/util/logging_spec.rb
@@ -93,6 +93,12 @@ describe Puppet::Util::Logging do
end
describe "when sending a deprecation warning" do
+ it "does not log a message when deprecation warnings are disabled" do
+ Puppet.expects(:[]).with(:disable_warnings).returns %w[deprecations]
+ @logger.expects(:warning).never
+ @logger.deprecation_warning 'foo'
+ end
+
it "logs the message with warn" do
@logger.expects(:warning).with do |msg|
msg =~ /^foo\n/
@@ -133,6 +139,44 @@ describe Puppet::Util::Logging do
end
end
+ describe "when sending a puppet_deprecation_warning" do
+ it "requires file and line or key options" do
+ expect do
+ @logger.puppet_deprecation_warning("foo")
+ end.to raise_error(Puppet::DevError, /Need either :file and :line, or :key/)
+ expect do
+ @logger.puppet_deprecation_warning("foo", :file => 'bar')
+ end.to raise_error(Puppet::DevError, /Need either :file and :line, or :key/)
+ expect do
+ @logger.puppet_deprecation_warning("foo", :key => 'akey')
+ @logger.puppet_deprecation_warning("foo", :file => 'afile', :line => 1)
+ end.to_not raise_error
+ end
+
+ it "warns with file and line" do
+ @logger.expects(:warning).with(regexp_matches(/deprecated foo.*afile:5/m))
+ @logger.puppet_deprecation_warning("deprecated foo", :file => 'afile', :line => 5)
+ end
+
+ it "warns keyed from file and line" do
+ @logger.expects(:warning).with(regexp_matches(/deprecated foo.*afile:5/m)).once
+ 5.times do
+ @logger.puppet_deprecation_warning("deprecated foo", :file => 'afile', :line => 5)
+ end
+ end
+
+ it "warns with separate key only once regardless of file and line" do
+ @logger.expects(:warning).with(regexp_matches(/deprecated foo.*afile:5/m)).once
+ @logger.puppet_deprecation_warning("deprecated foo", :key => 'some_key', :file => 'afile', :line => 5)
+ @logger.puppet_deprecation_warning("deprecated foo", :key => 'some_key', :file => 'bfile', :line => 3)
+ end
+
+ it "warns with key but no file and line" do
+ @logger.expects(:warning).with(regexp_matches(/deprecated foo.*unknown:unknown/m))
+ @logger.puppet_deprecation_warning("deprecated foo", :key => 'some_key')
+ end
+ end
+
describe "when formatting exceptions" do
it "should be able to format a chain of exceptions" do
exc3 = Puppet::Error.new("original")
diff --git a/spec/unit/util/pidlock_spec.rb b/spec/unit/util/pidlock_spec.rb
index 2ebe7dec8..fcef7aa31 100644
--- a/spec/unit/util/pidlock_spec.rb
+++ b/spec/unit/util/pidlock_spec.rb
@@ -50,6 +50,26 @@ describe Puppet::Util::Pidlock do
Puppet::FileSystem.exist?(@lockfile).should be_true
end
+ it 'should create an empty lock file even when pid is missing' do
+ Process.stubs(:pid).returns('')
+ @lock.lock
+ Puppet::FileSystem.exist?(@lock.file_path).should be_true
+ Puppet::FileSystem.read(@lock.file_path).should be_empty
+ end
+
+ it 'should replace an existing empty lockfile with a pid, given a subsequent lock call made against a valid pid' do
+ # empty pid results in empty lockfile
+ Process.stubs(:pid).returns('')
+ @lock.lock
+ Puppet::FileSystem.exist?(@lock.file_path).should be_true
+
+ # next lock call with valid pid kills existing empty lockfile
+ Process.stubs(:pid).returns(1234)
+ @lock.lock
+ Puppet::FileSystem.exist?(@lock.file_path).should be_true
+ Puppet::FileSystem.read(@lock.file_path).should == '1234'
+ end
+
it "should expose the lock file_path" do
@lock.file_path.should == @lockfile
end
@@ -83,6 +103,22 @@ describe Puppet::Util::Pidlock do
@lock.lock
@lock.should be_locked
end
+
+ it "should remove the lockfile when pid is missing" do
+ Process.stubs(:pid).returns('')
+ @lock.lock
+ @lock.locked?.should be_false
+ Puppet::FileSystem.exist?(@lock.file_path).should be_false
+ end
+ end
+
+ describe '#lock_pid' do
+ it 'should return nil if the pid is empty' do
+ # fake pid to get empty lockfile
+ Process.stubs(:pid).returns('')
+ @lock.lock
+ @lock.lock_pid.should == nil
+ end
end
describe "with a stale lock" do
@@ -105,7 +141,7 @@ describe Puppet::Util::Pidlock do
describe "#lock" do
it "should clear stale locks" do
- @lock.locked?
+ @lock.locked?.should be_false
Puppet::FileSystem.exist?(@lockfile).should be_false
end
diff --git a/spec/unit/util/profiler/aggregate_spec.rb b/spec/unit/util/profiler/aggregate_spec.rb
new file mode 100644
index 000000000..8d38d4673
--- /dev/null
+++ b/spec/unit/util/profiler/aggregate_spec.rb
@@ -0,0 +1,59 @@
+require 'spec_helper'
+require 'puppet/util/profiler'
+require 'puppet/util/profiler/around_profiler'
+require 'puppet/util/profiler/aggregate'
+
+describe Puppet::Util::Profiler::Aggregate do
+ let(:logger) { AggregateSimpleLog.new }
+ let(:profiler) { Puppet::Util::Profiler::Aggregate.new(logger, nil) }
+ let(:profiler_mgr) do
+ p = Puppet::Util::Profiler::AroundProfiler.new
+ p.add_profiler(profiler)
+ p
+ end
+
+ it "tracks the aggregate counts and time for the hierarchy of metrics" do
+ profiler_mgr.profile("Looking up hiera data in production environment", ["function", "hiera_lookup", "production"]) { sleep 0.01 }
+ profiler_mgr.profile("Looking up hiera data in test environment", ["function", "hiera_lookup", "test"]) {}
+ profiler_mgr.profile("looking up stuff for compilation", ["compiler", "lookup"]) { sleep 0.01 }
+ profiler_mgr.profile("COMPILING ALL OF THE THINGS!", ["compiler", "compiling"]) {}
+
+ profiler.values["function"].count.should == 2
+ profiler.values["function"].time.should be > 0
+ profiler.values["function"]["hiera_lookup"].count.should == 2
+ profiler.values["function"]["hiera_lookup"]["production"].count.should == 1
+ profiler.values["function"]["hiera_lookup"]["test"].count.should == 1
+ profiler.values["function"].time.should be >= profiler.values["function"]["hiera_lookup"]["test"].time
+
+ profiler.values["compiler"].count.should == 2
+ profiler.values["compiler"].time.should be > 0
+ profiler.values["compiler"]["lookup"].count.should == 1
+ profiler.values["compiler"]["compiling"].count.should == 1
+ profiler.values["compiler"].time.should be >= profiler.values["compiler"]["lookup"].time
+
+ profiler.shutdown
+
+ logger.output.should =~ /function -> hiera_lookup: .*\(2 calls\)\nfunction -> hiera_lookup ->.*\(1 calls\)/
+ logger.output.should =~ /compiler: .*\(2 calls\)\ncompiler ->.*\(1 calls\)/
+ end
+
+ it "tolerates calls to `profile` that don't include a metric id" do
+ profiler_mgr.profile("yo") {}
+ end
+
+ it "supports both symbols and strings as components of a metric id" do
+ profiler_mgr.profile("yo", [:foo, "bar"]) {}
+ end
+
+ class AggregateSimpleLog
+ attr_reader :output
+
+ def initialize
+ @output = ""
+ end
+
+ def call(msg)
+ @output << msg << "\n"
+ end
+ end
+end
diff --git a/spec/unit/util/profiler/around_profiler_spec.rb b/spec/unit/util/profiler/around_profiler_spec.rb
new file mode 100644
index 000000000..0837395b5
--- /dev/null
+++ b/spec/unit/util/profiler/around_profiler_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+require 'puppet/util/profiler'
+
+describe Puppet::Util::Profiler::AroundProfiler do
+ let(:child) { TestAroundProfiler.new() }
+ let(:profiler) { Puppet::Util::Profiler::AroundProfiler.new }
+
+ before :each do
+ profiler.add_profiler(child)
+ end
+
+ it "returns the value of the profiled segment" do
+ retval = profiler.profile("Testing", ["testing"]) { "the return value" }
+
+ retval.should == "the return value"
+ end
+
+ it "propagates any errors raised in the profiled segment" do
+ expect do
+ profiler.profile("Testing", ["testing"]) { raise "a problem" }
+ end.to raise_error("a problem")
+ end
+
+ it "makes the description and the context available to the `start` and `finish` methods" do
+ profiler.profile("Testing", ["testing"]) { }
+
+ child.context.should == "Testing"
+ child.description.should == "Testing"
+ end
+
+ it "calls finish even when an error is raised" do
+ begin
+ profiler.profile("Testing", ["testing"]) { raise "a problem" }
+ rescue
+ child.context.should == "Testing"
+ end
+ end
+
+ it "supports multiple profilers" do
+ profiler2 = TestAroundProfiler.new
+ profiler.add_profiler(profiler2)
+ profiler.profile("Testing", ["testing"]) {}
+
+ child.context.should == "Testing"
+ profiler2.context.should == "Testing"
+ end
+
+ class TestAroundProfiler
+ attr_accessor :context, :description
+
+ def start(description, metric_id)
+ description
+ end
+
+ def finish(context, description, metric_id)
+ @context = context
+ @description = description
+ end
+ end
+end
+
diff --git a/spec/unit/util/profiler/logging_spec.rb b/spec/unit/util/profiler/logging_spec.rb
index 5316e5ae9..3f6a728dd 100644
--- a/spec/unit/util/profiler/logging_spec.rb
+++ b/spec/unit/util/profiler/logging_spec.rb
@@ -4,51 +4,40 @@ require 'puppet/util/profiler'
describe Puppet::Util::Profiler::Logging do
let(:logger) { SimpleLog.new }
let(:identifier) { "Profiling ID" }
- let(:profiler) { TestLoggingProfiler.new(logger, identifier) }
-
- it "returns the value of the profiled segment" do
- retval = profiler.profile("Testing") { "the return value" }
-
- retval.should == "the return value"
- end
-
- it "propogates any errors raised in the profiled segment" do
- expect do
- profiler.profile("Testing") { raise "a problem" }
- end.to raise_error("a problem")
+ let(:logging_profiler) { TestLoggingProfiler.new(logger, identifier) }
+ let(:profiler) do
+ p = Puppet::Util::Profiler::AroundProfiler.new
+ p.add_profiler(logging_profiler)
+ p
end
it "logs the explanation of the profile results" do
- profiler.profile("Testing") { }
+ profiler.profile("Testing", ["test"]) { }
logger.messages.first.should =~ /the explanation/
end
- it "logs results even when an error is raised" do
- begin
- profiler.profile("Testing") { raise "a problem" }
- rescue
- logger.messages.first.should =~ /the explanation/
- end
- end
-
it "describes the profiled segment" do
- profiler.profile("Tested measurement") { }
+ profiler.profile("Tested measurement", ["test"]) { }
logger.messages.first.should =~ /PROFILE \[#{identifier}\] \d Tested measurement/
end
it "indicates the order in which segments are profiled" do
- profiler.profile("Measurement") { }
- profiler.profile("Another measurement") { }
+ profiler.profile("Measurement", ["measurement"]) { }
+ profiler.profile("Another measurement", ["measurement"]) { }
logger.messages[0].should =~ /1 Measurement/
logger.messages[1].should =~ /2 Another measurement/
end
it "indicates the nesting of profiled segments" do
- profiler.profile("Measurement") { profiler.profile("Nested measurement") { } }
- profiler.profile("Another measurement") { profiler.profile("Another nested measurement") { } }
+ profiler.profile("Measurement", ["measurement1"]) do
+ profiler.profile("Nested measurement", ["measurement2"]) { }
+ end
+ profiler.profile("Another measurement", ["measurement1"]) do
+ profiler.profile("Another nested measurement", ["measurement2"]) { }
+ end
logger.messages[0].should =~ /1.1 Nested measurement/
logger.messages[1].should =~ /1 Measurement/
@@ -57,12 +46,12 @@ describe Puppet::Util::Profiler::Logging do
end
class TestLoggingProfiler < Puppet::Util::Profiler::Logging
- def start
+ def do_start(metric, description)
"the start"
end
- def finish(context)
- "the explanation of #{context}"
+ def do_finish(context, metric, description)
+ {:msg => "the explanation of #{context}"}
end
end
diff --git a/spec/unit/util/profiler/none_spec.rb b/spec/unit/util/profiler/none_spec.rb
deleted file mode 100644
index 0cabfef6f..000000000
--- a/spec/unit/util/profiler/none_spec.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-require 'spec_helper'
-require 'puppet/util/profiler'
-
-describe Puppet::Util::Profiler::None do
- let(:profiler) { Puppet::Util::Profiler::None.new }
-
- it "returns the value of the profiled block" do
- retval = profiler.profile("Testing") { "the return value" }
-
- retval.should == "the return value"
- end
-end
diff --git a/spec/unit/util/profiler/wall_clock_spec.rb b/spec/unit/util/profiler/wall_clock_spec.rb
index 668f63221..1adcf0d61 100644
--- a/spec/unit/util/profiler/wall_clock_spec.rb
+++ b/spec/unit/util/profiler/wall_clock_spec.rb
@@ -6,7 +6,7 @@ describe Puppet::Util::Profiler::WallClock do
it "logs the number of seconds it took to execute the segment" do
profiler = Puppet::Util::Profiler::WallClock.new(nil, nil)
- message = profiler.finish(profiler.start)
+ message = profiler.do_finish(profiler.start(["foo", "bar"], "Testing"), ["foo", "bar"], "Testing")[:msg]
message.should =~ /took \d\.\d{4} seconds/
end
diff --git a/spec/unit/util/profiler_spec.rb b/spec/unit/util/profiler_spec.rb
new file mode 100644
index 000000000..c7fc48cb9
--- /dev/null
+++ b/spec/unit/util/profiler_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+require 'puppet/util/profiler'
+
+describe Puppet::Util::Profiler do
+ let(:profiler) { TestProfiler.new() }
+
+ it "supports adding profilers" do
+ subject.add_profiler(profiler)
+ subject.current[0].should == profiler
+ end
+
+ it "supports removing profilers" do
+ subject.add_profiler(profiler)
+ subject.remove_profiler(profiler)
+ subject.current.length.should == 0
+ end
+
+ it "supports clearing profiler list" do
+ subject.add_profiler(profiler)
+ subject.clear
+ subject.current.length.should == 0
+ end
+
+ it "supports profiling" do
+ subject.add_profiler(profiler)
+ subject.profile("hi", ["mymetric"]) {}
+ profiler.context[:metric_id].should == ["mymetric"]
+ profiler.context[:description].should == "hi"
+ profiler.description.should == "hi"
+ end
+
+ it "supports profiling without a metric id" do
+ subject.add_profiler(profiler)
+ subject.profile("hi") {}
+ profiler.context[:metric_id].should == nil
+ profiler.context[:description].should == "hi"
+ profiler.description.should == "hi"
+ end
+
+ class TestProfiler
+ attr_accessor :context, :metric, :description
+
+ def start(description, metric_id)
+ {:metric_id => metric_id,
+ :description => description}
+ end
+
+ def finish(context, description, metric_id)
+ @context = context
+ @metric_id = metric_id
+ @description = description
+ end
+ end
+end
+
diff --git a/spec/unit/util/queue_spec.rb b/spec/unit/util/queue_spec.rb
index d7ba57f85..48b98e8e3 100755
--- a/spec/unit/util/queue_spec.rb
+++ b/spec/unit/util/queue_spec.rb
@@ -1,7 +1,6 @@
#! /usr/bin/env ruby
require 'spec_helper'
require 'puppet/util/queue'
-require 'spec/mocks'
def make_test_client_class(n)
c = Class.new do
diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb
index acc606b76..7ed2cffcc 100755
--- a/spec/unit/util/rdoc/parser_spec.rb
+++ b/spec/unit/util/rdoc/parser_spec.rb
@@ -21,6 +21,17 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
end
describe "when scanning files" do
+ around(:each) do |example|
+ Puppet.override({
+ :current_environment => Puppet::Node::Environment.create(:doc, [], '/somewhere/etc/manifests/site.pp')
+ },
+ "A fake current environment that the application would have established by now"
+ ) do
+
+ example.run
+ end
+ end
+
it "should parse puppet files with the puppet parser" do
@parser.stubs(:scan_top_level)
parser = stub 'parser'
@@ -56,15 +67,12 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
it "should scan the top level even if the file has already parsed" do
known_type = stub 'known_types'
- env = Puppet::Node::Environment.create(Puppet[:environment].to_sym, [])
+ env = Puppet.lookup(:current_environment)
env.stubs(:known_resource_types).returns(known_type)
known_type.expects(:watching_file?).with("module/manifests/init.pp").returns(true)
- Puppet.override(:environments => Puppet::Environments::Static.new(env)) do
-
- @parser.expects(:scan_top_level)
+ @parser.expects(:scan_top_level)
- @parser.scan
- end
+ @parser.scan
end
end
diff --git a/spec/unit/util/tagging_spec.rb b/spec/unit/util/tagging_spec.rb
index 248e915e9..53bb39d7a 100755
--- a/spec/unit/util/tagging_spec.rb
+++ b/spec/unit/util/tagging_spec.rb
@@ -41,7 +41,7 @@ describe Puppet::Util::Tagging do
end
it "should allow tags containing '.' characters" do
- expect { tagger.tag("good.tag") }.to_not raise_error(Puppet::ParseError)
+ expect { tagger.tag("good.tag") }.to_not raise_error
end
it "should add qualified classes as tags" do
@@ -127,5 +127,36 @@ describe Puppet::Util::Tagging do
expect(tagger).to be_tagged("two")
expect(tagger).to be_tagged("three")
end
+
+ it "protects against empty tags" do
+ expect { tagger.tags = "one,,two"}.to raise_error(/Invalid tag ''/)
+ end
+
+ it "takes an array of tags" do
+ tagger.tags = ["one", "two"]
+
+ expect(tagger).to be_tagged("one")
+ expect(tagger).to be_tagged("two")
+ end
+
+ it "removes any existing tags when reassigning" do
+ tagger.tags = "one, two"
+
+ tagger.tags = "three, four"
+
+ expect(tagger).to_not be_tagged("one")
+ expect(tagger).to_not be_tagged("two")
+ expect(tagger).to be_tagged("three")
+ expect(tagger).to be_tagged("four")
+ end
+
+ it "allows empty tags that are generated from :: separated tags" do
+ tagger.tags = "one::::two::three"
+
+ expect(tagger).to be_tagged("one")
+ expect(tagger).to be_tagged("")
+ expect(tagger).to be_tagged("two")
+ expect(tagger).to be_tagged("three")
+ end
end
end
diff --git a/spec/unit/util/windows/access_control_entry_spec.rb b/spec/unit/util/windows/access_control_entry_spec.rb
index b139b0d42..8d3f51c8a 100644
--- a/spec/unit/util/windows/access_control_entry_spec.rb
+++ b/spec/unit/util/windows/access_control_entry_spec.rb
@@ -5,7 +5,7 @@ require 'puppet/util/windows'
describe "Puppet::Util::Windows::AccessControlEntry", :if => Puppet.features.microsoft_windows? do
let(:klass) { Puppet::Util::Windows::AccessControlEntry }
let(:sid) { 'S-1-5-18' }
- let(:mask) { Windows::File::FILE_ALL_ACCESS }
+ let(:mask) { Puppet::Util::Windows::File::FILE_ALL_ACCESS }
it "creates an access allowed ace" do
ace = klass.new(sid, mask)
diff --git a/spec/unit/util/adsi_spec.rb b/spec/unit/util/windows/adsi_spec.rb
index 491c4374b..f569d91e9 100755
--- a/spec/unit/util/adsi_spec.rb
+++ b/spec/unit/util/windows/adsi_spec.rb
@@ -2,97 +2,104 @@
require 'spec_helper'
-require 'puppet/util/adsi'
+require 'puppet/util/windows'
-describe Puppet::Util::ADSI do
+describe Puppet::Util::Windows::ADSI, :if => Puppet.features.microsoft_windows? do
let(:connection) { stub 'connection' }
before(:each) do
- Puppet::Util::ADSI.instance_variable_set(:@computer_name, 'testcomputername')
- Puppet::Util::ADSI.stubs(:connect).returns connection
+ Puppet::Util::Windows::ADSI.instance_variable_set(:@computer_name, 'testcomputername')
+ Puppet::Util::Windows::ADSI.stubs(:connect).returns connection
end
after(:each) do
- Puppet::Util::ADSI.instance_variable_set(:@computer_name, nil)
+ Puppet::Util::Windows::ADSI.instance_variable_set(:@computer_name, nil)
end
it "should generate the correct URI for a resource" do
- Puppet::Util::ADSI.uri('test', 'user').should == "WinNT://./test,user"
+ Puppet::Util::Windows::ADSI.uri('test', 'user').should == "WinNT://./test,user"
end
it "should be able to get the name of the computer" do
- Puppet::Util::ADSI.computer_name.should == 'testcomputername'
+ Puppet::Util::Windows::ADSI.computer_name.should == 'testcomputername'
end
it "should be able to provide the correct WinNT base URI for the computer" do
- Puppet::Util::ADSI.computer_uri.should == "WinNT://."
+ Puppet::Util::Windows::ADSI.computer_uri.should == "WinNT://."
end
it "should generate a fully qualified WinNT URI" do
- Puppet::Util::ADSI.computer_uri('testcomputername').should == "WinNT://testcomputername"
+ Puppet::Util::Windows::ADSI.computer_uri('testcomputername').should == "WinNT://testcomputername"
end
- describe ".sid_for_account", :if => Puppet.features.microsoft_windows? do
+ describe ".sid_for_account" do
it "should return nil if the account does not exist" do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('foobar').returns nil
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('foobar').returns nil
- Puppet::Util::ADSI.sid_for_account('foobar').should be_nil
+ Puppet::Util::Windows::ADSI.sid_for_account('foobar').should be_nil
end
it "should return a SID for a passed user or group name" do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('testers').returns 'S-1-5-32-547'
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('testers').returns 'S-1-5-32-547'
- Puppet::Util::ADSI.sid_for_account('testers').should == 'S-1-5-32-547'
+ Puppet::Util::Windows::ADSI.sid_for_account('testers').should == 'S-1-5-32-547'
end
it "should return a SID for a passed fully-qualified user or group name" do
- Puppet::Util::Windows::Security.expects(:name_to_sid).with('MACHINE\testers').returns 'S-1-5-32-547'
+ Puppet::Util::Windows::SID.expects(:name_to_sid).with('MACHINE\testers').returns 'S-1-5-32-547'
- Puppet::Util::ADSI.sid_for_account('MACHINE\testers').should == 'S-1-5-32-547'
+ Puppet::Util::Windows::ADSI.sid_for_account('MACHINE\testers').should == 'S-1-5-32-547'
end
end
- describe ".sid_uri", :if => Puppet.features.microsoft_windows? do
+ describe ".computer_name" do
+ it "should return a non-empty ComputerName string" do
+ Puppet::Util::Windows::ADSI.instance_variable_set(:@computer_name, nil)
+ Puppet::Util::Windows::ADSI.computer_name.should_not be_empty
+ end
+ end
+
+ describe ".sid_uri" do
it "should raise an error when the input is not a SID object" do
[Object.new, {}, 1, :symbol, '', nil].each do |input|
expect {
- Puppet::Util::ADSI.sid_uri(input)
+ Puppet::Util::Windows::ADSI.sid_uri(input)
}.to raise_error(Puppet::Error, /Must use a valid SID object/)
end
end
it "should return a SID uri for a well-known SID (SYSTEM)" do
sid = Win32::Security::SID.new('SYSTEM')
- Puppet::Util::ADSI.sid_uri(sid).should == 'WinNT://S-1-5-18'
+ Puppet::Util::Windows::ADSI.sid_uri(sid).should == 'WinNT://S-1-5-18'
end
end
- describe Puppet::Util::ADSI::User do
+ describe Puppet::Util::Windows::ADSI::User do
let(:username) { 'testuser' }
let(:domain) { 'DOMAIN' }
let(:domain_username) { "#{domain}\\#{username}"}
it "should generate the correct URI" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI::User.uri(username).should == "WinNT://./#{username},user"
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI::User.uri(username).should == "WinNT://./#{username},user"
end
it "should generate the correct URI for a user with a domain" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI::User.uri(username, domain).should == "WinNT://#{domain}/#{username},user"
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI::User.uri(username, domain).should == "WinNT://#{domain}/#{username},user"
end
it "should be able to parse a username without a domain" do
- Puppet::Util::ADSI::User.parse_name(username).should == [username, '.']
+ Puppet::Util::Windows::ADSI::User.parse_name(username).should == [username, '.']
end
it "should be able to parse a username with a domain" do
- Puppet::Util::ADSI::User.parse_name(domain_username).should == [username, domain]
+ Puppet::Util::Windows::ADSI::User.parse_name(domain_username).should == [username, domain]
end
it "should raise an error with a username that contains a /" do
expect {
- Puppet::Util::ADSI::User.parse_name("#{domain}/#{username}")
+ Puppet::Util::Windows::ADSI::User.parse_name("#{domain}/#{username}")
}.to raise_error(Puppet::Error, /Value must be in DOMAIN\\user style syntax/)
end
@@ -100,63 +107,61 @@ describe Puppet::Util::ADSI do
adsi_user = stub('adsi')
connection.expects(:Create).with('user', username).returns(adsi_user)
- Puppet::Util::ADSI::Group.expects(:exists?).with(username).returns(false)
+ Puppet::Util::Windows::ADSI::Group.expects(:exists?).with(username).returns(false)
- user = Puppet::Util::ADSI::User.create(username)
+ user = Puppet::Util::Windows::ADSI::User.create(username)
- user.should be_a(Puppet::Util::ADSI::User)
+ user.should be_a(Puppet::Util::Windows::ADSI::User)
user.native_user.should == adsi_user
end
it "should be able to check the existence of a user" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{username},user").returns connection
- Puppet::Util::ADSI::User.exists?(username).should be_true
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.expects(:connect).with("WinNT://./#{username},user").returns connection
+ Puppet::Util::Windows::ADSI::User.exists?(username).should be_true
end
it "should be able to check the existence of a domain user" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI.expects(:connect).with("WinNT://#{domain}/#{username},user").returns connection
- Puppet::Util::ADSI::User.exists?(domain_username).should be_true
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.expects(:connect).with("WinNT://#{domain}/#{username},user").returns connection
+ Puppet::Util::Windows::ADSI::User.exists?(domain_username).should be_true
end
- it "should be able to confirm the existence of a user with a well-known SID",
- :if => Puppet.features.microsoft_windows? do
+ it "should be able to confirm the existence of a user with a well-known SID" do
system_user = Win32::Security::SID::LocalSystem
# ensure that the underlying OS is queried here
- Puppet::Util::ADSI.unstub(:connect)
- Puppet::Util::ADSI::User.exists?(system_user).should be_true
+ Puppet::Util::Windows::ADSI.unstub(:connect)
+ Puppet::Util::Windows::ADSI::User.exists?(system_user).should be_true
end
- it "should return nil with an unknown SID",
- :if => Puppet.features.microsoft_windows? do
+ it "should return nil with an unknown SID" do
bogus_sid = 'S-1-2-3-4'
# ensure that the underlying OS is queried here
- Puppet::Util::ADSI.unstub(:connect)
- Puppet::Util::ADSI::User.exists?(bogus_sid).should be_false
+ Puppet::Util::Windows::ADSI.unstub(:connect)
+ Puppet::Util::Windows::ADSI::User.exists?(bogus_sid).should be_false
end
it "should be able to delete a user" do
connection.expects(:Delete).with('user', username)
- Puppet::Util::ADSI::User.delete(username)
+ Puppet::Util::Windows::ADSI::User.delete(username)
end
it "should return an enumeration of IADsUser wrapped objects" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
name = 'Administrator'
wmi_users = [stub('WMI', :name => name)]
- Puppet::Util::ADSI.expects(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(wmi_users)
+ Puppet::Util::Windows::ADSI.expects(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(wmi_users)
native_user = stub('IADsUser')
homedir = "C:\\Users\\#{name}"
native_user.expects(:Get).with('HomeDirectory').returns(homedir)
- Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{name},user").returns(native_user)
+ Puppet::Util::Windows::ADSI.expects(:connect).with("WinNT://./#{name},user").returns(native_user)
- users = Puppet::Util::ADSI::User.to_a
+ users = Puppet::Util::Windows::ADSI::User.to_a
users.length.should == 1
users[0].name.should == name
users[0]['HomeDirectory'].should == homedir
@@ -165,7 +170,7 @@ describe Puppet::Util::ADSI do
describe "an instance" do
let(:adsi_user) { stub('user', :objectSID => []) }
let(:sid) { stub(:account => username, :domain => 'testcomputername') }
- let(:user) { Puppet::Util::ADSI::User.new(username, adsi_user) }
+ let(:user) { Puppet::Util::Windows::ADSI::User.new(username, adsi_user) }
it "should provide its groups as a list of names" do
names = ["group1", "group2"]
@@ -178,8 +183,8 @@ describe Puppet::Util::ADSI do
end
it "should be able to test whether a given password is correct" do
- Puppet::Util::ADSI::User.expects(:logon).with(username, 'pwdwrong').returns(false)
- Puppet::Util::ADSI::User.expects(:logon).with(username, 'pwdright').returns(true)
+ Puppet::Util::Windows::ADSI::User.expects(:logon).with(username, 'pwdwrong').returns(false)
+ Puppet::Util::Windows::ADSI::User.expects(:logon).with(username, 'pwdright').returns(true)
user.password_is?('pwdwrong').should be_false
user.password_is?('pwdright').should be_true
@@ -198,16 +203,16 @@ describe Puppet::Util::ADSI do
user.password = 'pwd'
end
- it "should generate the correct URI", :if => Puppet.features.microsoft_windows? do
- Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid)
+ it "should generate the correct URI" do
+ Puppet::Util::Windows::SID.stubs(:octet_string_to_sid_object).returns(sid)
user.uri.should == "WinNT://testcomputername/#{username},user"
end
- describe "when given a set of groups to which to add the user", :if => Puppet.features.microsoft_windows? do
+ describe "when given a set of groups to which to add the user" do
let(:groups_to_set) { 'group1,group2' }
before(:each) do
- Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid)
+ Puppet::Util::Windows::SID.stubs(:octet_string_to_sid_object).returns(sid)
user.expects(:groups).returns ['group2', 'group3']
end
@@ -219,9 +224,9 @@ describe Puppet::Util::ADSI do
group3 = stub 'group1'
group3.expects(:Remove).with("WinNT://testcomputername/#{username},user")
- Puppet::Util::ADSI.expects(:sid_uri).with(sid).returns("WinNT://testcomputername/#{username},user").twice
- Puppet::Util::ADSI.expects(:connect).with('WinNT://./group1,group').returns group1
- Puppet::Util::ADSI.expects(:connect).with('WinNT://./group3,group').returns group3
+ Puppet::Util::Windows::ADSI.expects(:sid_uri).with(sid).returns("WinNT://testcomputername/#{username},user").twice
+ Puppet::Util::Windows::ADSI.expects(:connect).with('WinNT://./group1,group').returns group1
+ Puppet::Util::Windows::ADSI.expects(:connect).with('WinNT://./group3,group').returns group3
user.set_groups(groups_to_set, false)
end
@@ -232,8 +237,8 @@ describe Puppet::Util::ADSI do
group1 = stub 'group1'
group1.expects(:Add).with("WinNT://testcomputername/#{username},user")
- Puppet::Util::ADSI.expects(:sid_uri).with(sid).returns("WinNT://testcomputername/#{username},user")
- Puppet::Util::ADSI.expects(:connect).with('WinNT://./group1,group').returns group1
+ Puppet::Util::Windows::ADSI.expects(:sid_uri).with(sid).returns("WinNT://testcomputername/#{username},user")
+ Puppet::Util::Windows::ADSI.expects(:connect).with('WinNT://./group1,group').returns group1
user.set_groups(groups_to_set, true)
end
@@ -242,50 +247,50 @@ describe Puppet::Util::ADSI do
end
end
- describe Puppet::Util::ADSI::Group do
+ describe Puppet::Util::Windows::ADSI::Group do
let(:groupname) { 'testgroup' }
describe "an instance" do
let(:adsi_group) { stub 'group' }
- let(:group) { Puppet::Util::ADSI::Group.new(groupname, adsi_group) }
+ let(:group) { Puppet::Util::Windows::ADSI::Group.new(groupname, adsi_group) }
let(:someone_sid){ stub(:account => 'someone', :domain => 'testcomputername')}
- it "should be able to add a member (deprecated)", :if => Puppet.features.microsoft_windows? do
- Puppet.expects(:deprecation_warning).with('Puppet::Util::ADSI::Group#add_members is deprecated; please use Puppet::Util::ADSI::Group#add_member_sids')
+ it "should be able to add a member (deprecated)" do
+ Puppet.expects(:deprecation_warning).with('Puppet::Util::Windows::ADSI::Group#add_members is deprecated; please use Puppet::Util::Windows::ADSI::Group#add_member_sids')
- Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('someone').returns(someone_sid)
- Puppet::Util::ADSI.expects(:sid_uri).with(someone_sid).returns("WinNT://testcomputername/someone,user")
+ Puppet::Util::Windows::SID.expects(:name_to_sid_object).with('someone').returns(someone_sid)
+ Puppet::Util::Windows::ADSI.expects(:sid_uri).with(someone_sid).returns("WinNT://testcomputername/someone,user")
adsi_group.expects(:Add).with("WinNT://testcomputername/someone,user")
group.add_member('someone')
end
- it "should raise when adding a member that can't resolve to a SID (deprecated)", :if => Puppet.features.microsoft_windows? do
+ it "should raise when adding a member that can't resolve to a SID (deprecated)" do
expect {
group.add_member('foobar')
}.to raise_error(Puppet::Error, /Could not resolve username: foobar/)
end
- it "should be able to remove a member (deprecated)", :if => Puppet.features.microsoft_windows? do
- Puppet.expects(:deprecation_warning).with('Puppet::Util::ADSI::Group#remove_members is deprecated; please use Puppet::Util::ADSI::Group#remove_member_sids')
+ it "should be able to remove a member (deprecated)" do
+ Puppet.expects(:deprecation_warning).with('Puppet::Util::Windows::ADSI::Group#remove_members is deprecated; please use Puppet::Util::Windows::ADSI::Group#remove_member_sids')
- Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('someone').returns(someone_sid)
- Puppet::Util::ADSI.expects(:sid_uri).with(someone_sid).returns("WinNT://testcomputername/someone,user")
+ Puppet::Util::Windows::SID.expects(:name_to_sid_object).with('someone').returns(someone_sid)
+ Puppet::Util::Windows::ADSI.expects(:sid_uri).with(someone_sid).returns("WinNT://testcomputername/someone,user")
adsi_group.expects(:Remove).with("WinNT://testcomputername/someone,user")
group.remove_member('someone')
end
- it "should raise when removing a member that can't resolve to a SID (deprecated)", :if => Puppet.features.microsoft_windows? do
+ it "should raise when removing a member that can't resolve to a SID (deprecated)" do
expect {
group.remove_member('foobar')
}.to raise_error(Puppet::Error, /Could not resolve username: foobar/)
end
- describe "should be able to use SID objects", :if => Puppet.features.microsoft_windows? do
- let(:system) { Puppet::Util::Windows::Security.name_to_sid_object('SYSTEM') }
+ describe "should be able to use SID objects" do
+ let(:system) { Puppet::Util::Windows::SID.name_to_sid_object('SYSTEM') }
it "to add a member" do
adsi_group.expects(:Add).with("WinNT://S-1-5-18")
@@ -310,7 +315,7 @@ describe Puppet::Util::ADSI do
group.members.should =~ names
end
- it "should be able to add a list of users to a group", :if => Puppet.features.microsoft_windows? do
+ it "should be able to add a list of users to a group" do
names = ['DOMAIN\user1', 'user2']
sids = [
stub(:account => 'user1', :domain => 'DOMAIN'),
@@ -319,14 +324,14 @@ describe Puppet::Util::ADSI do
]
# use stubbed objectSid on member to return stubbed SID
- Puppet::Util::Windows::Security.expects(:octet_string_to_sid_object).with([0]).returns(sids[0])
- Puppet::Util::Windows::Security.expects(:octet_string_to_sid_object).with([1]).returns(sids[1])
+ Puppet::Util::Windows::SID.expects(:octet_string_to_sid_object).with([0]).returns(sids[0])
+ Puppet::Util::Windows::SID.expects(:octet_string_to_sid_object).with([1]).returns(sids[1])
- Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('user2').returns(sids[1])
- Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('DOMAIN2\user3').returns(sids[2])
+ Puppet::Util::Windows::SID.expects(:name_to_sid_object).with('user2').returns(sids[1])
+ Puppet::Util::Windows::SID.expects(:name_to_sid_object).with('DOMAIN2\user3').returns(sids[2])
- Puppet::Util::ADSI.expects(:sid_uri).with(sids[0]).returns("WinNT://DOMAIN/user1,user")
- Puppet::Util::ADSI.expects(:sid_uri).with(sids[2]).returns("WinNT://DOMAIN2/user3,user")
+ Puppet::Util::Windows::ADSI.expects(:sid_uri).with(sids[0]).returns("WinNT://DOMAIN/user1,user")
+ Puppet::Util::Windows::ADSI.expects(:sid_uri).with(sids[2]).returns("WinNT://DOMAIN2/user3,user")
members = names.each_with_index.map{|n,i| stub(:Name => n, :objectSID => [i])}
adsi_group.expects(:Members).returns members
@@ -337,7 +342,7 @@ describe Puppet::Util::ADSI do
group.set_members(['user2', 'DOMAIN2\user3'])
end
- it "should raise an error when a username does not resolve to a SID", :if => Puppet.features.microsoft_windows? do
+ it "should raise an error when a username does not resolve to a SID" do
expect {
adsi_group.expects(:Members).returns []
group.set_members(['foobar'])
@@ -345,81 +350,79 @@ describe Puppet::Util::ADSI do
end
it "should generate the correct URI" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
group.uri.should == "WinNT://./#{groupname},group"
end
end
it "should generate the correct URI" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI::Group.uri("people").should == "WinNT://./people,group"
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI::Group.uri("people").should == "WinNT://./people,group"
end
it "should be able to create a group" do
adsi_group = stub("adsi")
connection.expects(:Create).with('group', groupname).returns(adsi_group)
- Puppet::Util::ADSI::User.expects(:exists?).with(groupname).returns(false)
+ Puppet::Util::Windows::ADSI::User.expects(:exists?).with(groupname).returns(false)
- group = Puppet::Util::ADSI::Group.create(groupname)
+ group = Puppet::Util::Windows::ADSI::Group.create(groupname)
- group.should be_a(Puppet::Util::ADSI::Group)
+ group.should be_a(Puppet::Util::Windows::ADSI::Group)
group.native_group.should == adsi_group
end
it "should be able to confirm the existence of a group" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
- Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{groupname},group").returns connection
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.expects(:connect).with("WinNT://./#{groupname},group").returns connection
- Puppet::Util::ADSI::Group.exists?(groupname).should be_true
+ Puppet::Util::Windows::ADSI::Group.exists?(groupname).should be_true
end
- it "should be able to confirm the existence of a group with a well-known SID",
- :if => Puppet.features.microsoft_windows? do
+ it "should be able to confirm the existence of a group with a well-known SID" do
service_group = Win32::Security::SID::Service
# ensure that the underlying OS is queried here
- Puppet::Util::ADSI.unstub(:connect)
- Puppet::Util::ADSI::Group.exists?(service_group).should be_true
+ Puppet::Util::Windows::ADSI.unstub(:connect)
+ Puppet::Util::Windows::ADSI::Group.exists?(service_group).should be_true
end
- it "should return nil with an unknown SID",
- :if => Puppet.features.microsoft_windows? do
+ it "should return nil with an unknown SID" do
bogus_sid = 'S-1-2-3-4'
# ensure that the underlying OS is queried here
- Puppet::Util::ADSI.unstub(:connect)
- Puppet::Util::ADSI::Group.exists?(bogus_sid).should be_false
+ Puppet::Util::Windows::ADSI.unstub(:connect)
+ Puppet::Util::Windows::ADSI::Group.exists?(bogus_sid).should be_false
end
it "should be able to delete a group" do
connection.expects(:Delete).with('group', groupname)
- Puppet::Util::ADSI::Group.delete(groupname)
+ Puppet::Util::Windows::ADSI::Group.delete(groupname)
end
it "should return an enumeration of IADsGroup wrapped objects" do
- Puppet::Util::ADSI.stubs(:sid_uri_safe).returns(nil)
+ Puppet::Util::Windows::ADSI.stubs(:sid_uri_safe).returns(nil)
name = 'Administrators'
wmi_groups = [stub('WMI', :name => name)]
- Puppet::Util::ADSI.expects(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns(wmi_groups)
+ Puppet::Util::Windows::ADSI.expects(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns(wmi_groups)
native_group = stub('IADsGroup')
native_group.expects(:Members).returns([stub(:Name => 'Administrator')])
- Puppet::Util::ADSI.expects(:connect).with("WinNT://./#{name},group").returns(native_group)
+ Puppet::Util::Windows::ADSI.expects(:connect).with("WinNT://./#{name},group").returns(native_group)
- groups = Puppet::Util::ADSI::Group.to_a
+ groups = Puppet::Util::Windows::ADSI::Group.to_a
groups.length.should == 1
groups[0].name.should == name
groups[0].members.should == ['Administrator']
end
end
- describe Puppet::Util::ADSI::UserProfile do
+ describe Puppet::Util::Windows::ADSI::UserProfile do
it "should be able to delete a user profile" do
connection.expects(:Delete).with("Win32_UserProfile.SID='S-A-B-C'")
- Puppet::Util::ADSI::UserProfile.delete('S-A-B-C')
+ Puppet::Util::Windows::ADSI::UserProfile.delete('S-A-B-C')
end
it "should warn on 2003" do
@@ -431,7 +434,7 @@ describe Puppet::Util::ADSI do
Exception occurred.")
Puppet.expects(:warning).with("Cannot delete user profile for 'S-A-B-C' prior to Vista SP1")
- Puppet::Util::ADSI::UserProfile.delete('S-A-B-C')
+ Puppet::Util::Windows::ADSI::UserProfile.delete('S-A-B-C')
end
end
end
diff --git a/spec/unit/util/windows/api_types_spec.rb b/spec/unit/util/windows/api_types_spec.rb
new file mode 100644
index 000000000..a1e1c76c9
--- /dev/null
+++ b/spec/unit/util/windows/api_types_spec.rb
@@ -0,0 +1,28 @@
+# encoding: UTF-8
+#!/usr/bin/env ruby
+
+require 'spec_helper'
+
+describe "FFI::MemoryPointer", :if => Puppet.features.microsoft_windows? do
+ context "read_wide_string" do
+ let (:string) { "foo_bar" }
+
+ it "should properly roundtrip a given string" do
+ read_string = nil
+ FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr|
+ read_string = ptr.read_wide_string(string.length)
+ end
+
+ read_string.should == string
+ end
+
+ it "should return a given string in the default encoding" do
+ read_string = nil
+ FFI::MemoryPointer.from_string_to_wide_string(string) do |ptr|
+ read_string = ptr.read_wide_string(string.length)
+ end
+
+ read_string.encoding.should == Encoding.default_external
+ end
+ end
+end
diff --git a/spec/unit/util/windows/registry_spec.rb b/spec/unit/util/windows/registry_spec.rb
index 636ba0c93..ed52539a5 100755
--- a/spec/unit/util/windows/registry_spec.rb
+++ b/spec/unit/util/windows/registry_spec.rb
@@ -1,7 +1,6 @@
#! /usr/bin/env ruby
require 'spec_helper'
require 'puppet/util/windows'
-require 'puppet/util/windows/registry'
describe Puppet::Util::Windows::Registry, :if => Puppet::Util::Platform.windows? do
subject do
@@ -43,12 +42,14 @@ describe Puppet::Util::Windows::Registry, :if => Puppet::Util::Platform.windows?
yielded.should == subkey
end
- [described_class::KEY64, described_class::KEY32].each do |access|
- it "should open the key for read access 0x#{access.to_s(16)}" do
- mode = described_class::KEY_READ | access
- hkey.expects(:open).with(path, mode)
+ if Puppet::Util::Platform.windows?
+ [described_class::KEY64, described_class::KEY32].each do |access|
+ it "should open the key for read access 0x#{access.to_s(16)}" do
+ mode = described_class::KEY_READ | access
+ hkey.expects(:open).with(path, mode)
- subject.open(name, path, mode) {|reg| }
+ subject.open(name, path, mode) {|reg| }
+ end
end
end
diff --git a/spec/unit/util/windows/sid_spec.rb b/spec/unit/util/windows/sid_spec.rb
index 770512188..2748f13c6 100755
--- a/spec/unit/util/windows/sid_spec.rb
+++ b/spec/unit/util/windows/sid_spec.rb
@@ -4,12 +4,9 @@ require 'spec_helper'
describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows? do
if Puppet.features.microsoft_windows?
require 'puppet/util/windows'
- class SIDTester
- include Puppet::Util::Windows::SID
- end
end
- let(:subject) { SIDTester.new }
+ let(:subject) { Puppet::Util::Windows::SID }
let(:sid) { Win32::Security::SID::LocalSystem }
let(:invalid_sid) { 'bogus' }
let(:unknown_sid) { 'S-0-0-0' }
@@ -50,7 +47,7 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows?
expect {
invalid_octet = [1]
subject.octet_string_to_sid_object(invalid_octet)
- }.to raise_error(Win32::Security::SID::Error, /No mapping between account names and security IDs was done./)
+ }.to raise_error(SystemCallError, /No mapping between account names and security IDs was done./)
end
end
@@ -159,7 +156,7 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows?
it "should raise if the conversion fails" do
subject.expects(:string_to_sid_ptr).with(sid).
- raises(Puppet::Util::Windows::Error.new("Failed to convert string SID: #{sid}", Windows::Error::ERROR_ACCESS_DENIED))
+ raises(Puppet::Util::Windows::Error.new("Failed to convert string SID: #{sid}", Puppet::Util::Windows::Error::ERROR_ACCESS_DENIED))
expect {
subject.string_to_sid_ptr(sid) {|ptr| }
diff --git a/spec/unit/util/windows/string_spec.rb b/spec/unit/util/windows/string_spec.rb
index 60f7e6449..5c6473e70 100644
--- a/spec/unit/util/windows/string_spec.rb
+++ b/spec/unit/util/windows/string_spec.rb
@@ -50,5 +50,9 @@ describe "Puppet::Util::Windows::String", :if => Puppet.features.microsoft_windo
it "should convert an UTF-32BE string" do
converts_to_wide_string("bob\u00E8".encode(Encoding::UTF_32BE))
end
+
+ it "should return a nil when given a nil" do
+ wide_string(nil).should == nil
+ end
end
end
diff --git a/spec/unit/util/zaml_spec.rb b/spec/unit/util/zaml_spec.rb
index 56c5cb719..b239b4a84 100755
--- a/spec/unit/util/zaml_spec.rb
+++ b/spec/unit/util/zaml_spec.rb
@@ -69,7 +69,11 @@ describe "Pure ruby yaml implementation" do
end
it "serializes a time in UTC" do
- pending("not supported on Windows", :if => Puppet.features.microsoft_windows? && RUBY_VERSION[0,3] == '1.8') do
+ bad_rubies =
+ RUBY_VERSION[0,3] == '1.8' ||
+ RUBY_VERSION[0,3] == '2.0' && RUBY_PLATFORM == 'i386-mingw32'
+
+ pending("not supported on Windows", :if => Puppet.features.microsoft_windows? && bad_rubies) do
the_time_in("Europe/London").should be_equivalent_to(the_time_in_yaml_offset_by("+00:00"))
end
end